AppsCustomizeTabHost.java revision f30ad5f1bf33baceeca6b770464fb543b58af985
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.launcher2; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.ObjectAnimator; 22import android.content.Context; 23import android.content.res.Resources; 24import android.util.AttributeSet; 25import android.view.LayoutInflater; 26import android.view.MotionEvent; 27import android.view.View; 28import android.view.ViewGroup; 29import android.view.ViewPropertyAnimator; 30import android.widget.TabHost; 31import android.widget.TabWidget; 32import android.widget.TextView; 33 34import com.android.launcher.R; 35 36public class AppsCustomizeTabHost extends TabHost implements LauncherTransitionable, 37 TabHost.OnTabChangeListener { 38 static final String LOG_TAG = "AppsCustomizeTabHost"; 39 40 private static final String APPS_TAB_TAG = "APPS"; 41 private static final String WIDGETS_TAB_TAG = "WIDGETS"; 42 43 private final LayoutInflater mLayoutInflater; 44 private ViewGroup mTabs; 45 private ViewGroup mTabsContainer; 46 private AppsCustomizePagedView mAppsCustomizePane; 47 48 public AppsCustomizeTabHost(Context context, AttributeSet attrs) { 49 super(context, attrs); 50 mLayoutInflater = LayoutInflater.from(context); 51 } 52 53 /** 54 * Convenience methods to select specific tabs. We want to set the content type immediately 55 * in these cases, but we note that we still call setCurrentTabByTag() so that the tab view 56 * reflects the new content (but doesn't do the animation and logic associated with changing 57 * tabs manually). 58 */ 59 private void setContentTypeImmediate(AppsCustomizePagedView.ContentType type) { 60 onTabChangedStart(); 61 onTabChangedEnd(type); 62 } 63 void selectAppsTab() { 64 setContentTypeImmediate(AppsCustomizePagedView.ContentType.Applications); 65 setCurrentTabByTag(APPS_TAB_TAG); 66 } 67 void selectWidgetsTab() { 68 setContentTypeImmediate(AppsCustomizePagedView.ContentType.Widgets); 69 mAppsCustomizePane.setCurrentPageToWidgets(); 70 71 setCurrentTabByTag(WIDGETS_TAB_TAG); 72 } 73 74 /** 75 * Setup the tab host and create all necessary tabs. 76 */ 77 @Override 78 protected void onFinishInflate() { 79 // Setup the tab host 80 setup(); 81 82 final ViewGroup tabsContainer = (ViewGroup) findViewById(R.id.tabs_container); 83 final TabWidget tabs = (TabWidget) findViewById(com.android.internal.R.id.tabs); 84 final AppsCustomizePagedView appsCustomizePane = (AppsCustomizePagedView) 85 findViewById(R.id.apps_customize_pane_content); 86 mTabs = tabs; 87 mTabsContainer = tabsContainer; 88 mAppsCustomizePane = appsCustomizePane; 89 if (tabs == null || mAppsCustomizePane == null) throw new Resources.NotFoundException(); 90 91 // Configure the tabs content factory to return the same paged view (that we change the 92 // content filter on) 93 TabContentFactory contentFactory = new TabContentFactory() { 94 public View createTabContent(String tag) { 95 return appsCustomizePane; 96 } 97 }; 98 99 // Create the tabs 100 TextView tabView; 101 tabView = (TextView) mLayoutInflater.inflate(R.layout.tab_widget_indicator, tabs, false); 102 tabView.setText(mContext.getString(R.string.all_apps_button_label)); 103 addTab(newTabSpec(APPS_TAB_TAG).setIndicator(tabView).setContent(contentFactory)); 104 tabView = (TextView) mLayoutInflater.inflate(R.layout.tab_widget_indicator, tabs, false); 105 tabView.setText(mContext.getString(R.string.widgets_tab_label)); 106 addTab(newTabSpec(WIDGETS_TAB_TAG).setIndicator(tabView).setContent(contentFactory)); 107 setOnTabChangedListener(this); 108 109 // Setup the key listener to jump between the last tab view and the market icon 110 AppsCustomizeTabKeyEventListener keyListener = new AppsCustomizeTabKeyEventListener(); 111 View lastTab = tabs.getChildTabViewAt(tabs.getTabCount() - 1); 112 lastTab.setOnKeyListener(keyListener); 113 View shopButton = findViewById(R.id.market_button); 114 shopButton.setOnKeyListener(keyListener); 115 116 // Hide the tab bar until we measure 117 mTabsContainer.setAlpha(0f); 118 } 119 120 @Override 121 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 122 boolean remeasureTabWidth = (mTabs.getLayoutParams().width <= 0); 123 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 124 125 // Set the width of the tab list to the content width 126 if (remeasureTabWidth) { 127 int contentWidth = mAppsCustomizePane.getPageContentWidth(); 128 if (contentWidth > 0) { 129 // Set the width and show the tab bar (if we have a loading graphic, we can switch 130 // it off here) 131 mTabs.getLayoutParams().width = contentWidth; 132 mTabsContainer.setAlpha(1f); 133 } 134 } 135 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 136 } 137 138 @Override 139 public boolean onTouchEvent(MotionEvent event) { 140 // Intercept all touch events up to the bottom of the AppsCustomizePane so they do not fall 141 // through to the workspace and trigger showWorkspace() 142 if (event.getY() < mAppsCustomizePane.getBottom()) { 143 return true; 144 } 145 return super.onTouchEvent(event); 146 } 147 148 private void onTabChangedStart() { 149 mAppsCustomizePane.hideScrollingIndicator(false); 150 } 151 private void onTabChangedEnd(AppsCustomizePagedView.ContentType type) { 152 mAppsCustomizePane.setContentType(type); 153 } 154 155 @Override 156 public void onTabChanged(String tabId) { 157 final AppsCustomizePagedView.ContentType type = getContentTypeForTabTag(tabId); 158 if (!mAppsCustomizePane.isContentType(type)) { 159 // Animate the changing of the tab content by fading pages in and out 160 final Resources res = getResources(); 161 final int duration = res.getInteger(R.integer.config_tabTransitionDuration); 162 163 ObjectAnimator anim = ObjectAnimator.ofFloat(mAppsCustomizePane, "alpha", 0f); 164 anim.setDuration(duration); 165 anim.addListener(new AnimatorListenerAdapter() { 166 @Override 167 public void onAnimationStart(android.animation.Animator animation) { 168 onTabChangedStart(); 169 } 170 @Override 171 public void onAnimationEnd(android.animation.Animator animation) { 172 onTabChangedEnd(type); 173 174 ObjectAnimator anim = ObjectAnimator.ofFloat(mAppsCustomizePane, "alpha", 1f); 175 anim.setDuration(duration); 176 anim.addListener(new AnimatorListenerAdapter() { 177 @Override 178 public void onAnimationEnd(android.animation.Animator animation) { 179 if (!LauncherApplication.isScreenLarge()) { 180 mAppsCustomizePane.flashScrollingIndicator(); 181 } 182 } 183 }); 184 anim.start(); 185 } 186 }); 187 anim.start(); 188 } 189 } 190 191 /** 192 * Returns the content type for the specified tab tag. 193 */ 194 public AppsCustomizePagedView.ContentType getContentTypeForTabTag(String tag) { 195 if (tag.equals(APPS_TAB_TAG)) { 196 return AppsCustomizePagedView.ContentType.Applications; 197 } else if (tag.equals(WIDGETS_TAB_TAG)) { 198 return AppsCustomizePagedView.ContentType.Widgets; 199 } 200 return AppsCustomizePagedView.ContentType.Applications; 201 } 202 203 /** 204 * Returns the tab tag for a given content type. 205 */ 206 public String getTabTagForContentType(AppsCustomizePagedView.ContentType type) { 207 if (type == AppsCustomizePagedView.ContentType.Applications) { 208 return APPS_TAB_TAG; 209 } else if (type == AppsCustomizePagedView.ContentType.Widgets) { 210 return WIDGETS_TAB_TAG; 211 } 212 return APPS_TAB_TAG; 213 } 214 215 /** 216 * Disable focus on anything under this view in the hierarchy if we are not visible. 217 */ 218 @Override 219 public int getDescendantFocusability() { 220 if (getVisibility() != View.VISIBLE) { 221 return ViewGroup.FOCUS_BLOCK_DESCENDANTS; 222 } 223 return super.getDescendantFocusability(); 224 } 225 226 /* LauncherTransitionable overrides */ 227 @Override 228 public void onLauncherTransitionStart(Animator animation, boolean toWorkspace) { 229 // isHardwareAccelerated() checks if we're attached to a window and if that 230 // window is HW accelerated-- we were sometimes not attached to a window 231 // and buildLayer was throwing an IllegalStateException 232 if (animation != null && isHardwareAccelerated()) { 233 // Turn on hardware layers for performance 234 setLayerType(LAYER_TYPE_HARDWARE, null); 235 236 // force building the layer at the beginning of the animation, so you don't get a 237 // blip early in the animation 238 buildLayer(); 239 } 240 } 241 242 @Override 243 public void onLauncherTransitionEnd(Animator animation, boolean toWorkspace) { 244 if (animation != null) { 245 setLayerType(LAYER_TYPE_NONE, null); 246 } 247 248 if (!toWorkspace && !LauncherApplication.isScreenLarge()) { 249 mAppsCustomizePane.flashScrollingIndicator(); 250 } 251 } 252} 253