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