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