• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.launcher2;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.AnimatorSet;
22 import android.animation.ObjectAnimator;
23 import android.content.Context;
24 import android.content.res.Resources;
25 import android.graphics.Bitmap;
26 import android.graphics.Canvas;
27 import android.util.AttributeSet;
28 import android.view.LayoutInflater;
29 import android.view.MotionEvent;
30 import android.view.View;
31 import android.view.ViewGroup;
32 import android.view.animation.DecelerateInterpolator;
33 import android.widget.ImageView;
34 import android.widget.TabHost;
35 import android.widget.TabWidget;
36 import android.widget.TextView;
37 
38 import com.android.launcher.R;
39 
40 public class AppsCustomizeTabHost extends TabHost implements LauncherTransitionable,
41         TabHost.OnTabChangeListener  {
42     static final String LOG_TAG = "AppsCustomizeTabHost";
43 
44     private static final String APPS_TAB_TAG = "APPS";
45     private static final String WIDGETS_TAB_TAG = "WIDGETS";
46 
47     private final LayoutInflater mLayoutInflater;
48     private ViewGroup mTabs;
49     private ViewGroup mTabsContainer;
50     private AppsCustomizePagedView mAppsCustomizePane;
51     private boolean mSuppressContentCallback = false;
52     private ImageView mAnimationBuffer;
53 
54     private boolean mInTransition;
55     private boolean mResetAfterTransition;
56 
AppsCustomizeTabHost(Context context, AttributeSet attrs)57     public AppsCustomizeTabHost(Context context, AttributeSet attrs) {
58         super(context, attrs);
59         mLayoutInflater = LayoutInflater.from(context);
60     }
61 
62     /**
63      * Convenience methods to select specific tabs.  We want to set the content type immediately
64      * in these cases, but we note that we still call setCurrentTabByTag() so that the tab view
65      * reflects the new content (but doesn't do the animation and logic associated with changing
66      * tabs manually).
67      */
setContentTypeImmediate(AppsCustomizePagedView.ContentType type)68     private void setContentTypeImmediate(AppsCustomizePagedView.ContentType type) {
69         onTabChangedStart();
70         onTabChangedEnd(type);
71     }
selectAppsTab()72     void selectAppsTab() {
73         setContentTypeImmediate(AppsCustomizePagedView.ContentType.Applications);
74         setCurrentTabByTag(APPS_TAB_TAG);
75     }
selectWidgetsTab()76     void selectWidgetsTab() {
77         setContentTypeImmediate(AppsCustomizePagedView.ContentType.Widgets);
78         setCurrentTabByTag(WIDGETS_TAB_TAG);
79     }
80 
81     /**
82      * Setup the tab host and create all necessary tabs.
83      */
84     @Override
onFinishInflate()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         mAnimationBuffer = (ImageView) findViewById(R.id.animation_buffer);
97         if (tabs == null || mAppsCustomizePane == null) throw new Resources.NotFoundException();
98 
99         // Configure the tabs content factory to return the same paged view (that we change the
100         // content filter on)
101         TabContentFactory contentFactory = new TabContentFactory() {
102             public View createTabContent(String tag) {
103                 return appsCustomizePane;
104             }
105         };
106 
107         // Create the tabs
108         TextView tabView;
109         String label;
110         label = mContext.getString(R.string.all_apps_button_label);
111         tabView = (TextView) mLayoutInflater.inflate(R.layout.tab_widget_indicator, tabs, false);
112         tabView.setText(label);
113         tabView.setContentDescription(label);
114         addTab(newTabSpec(APPS_TAB_TAG).setIndicator(tabView).setContent(contentFactory));
115         label = mContext.getString(R.string.widgets_tab_label);
116         tabView = (TextView) mLayoutInflater.inflate(R.layout.tab_widget_indicator, tabs, false);
117         tabView.setText(label);
118         tabView.setContentDescription(label);
119         addTab(newTabSpec(WIDGETS_TAB_TAG).setIndicator(tabView).setContent(contentFactory));
120         setOnTabChangedListener(this);
121 
122         // Setup the key listener to jump between the last tab view and the market icon
123         AppsCustomizeTabKeyEventListener keyListener = new AppsCustomizeTabKeyEventListener();
124         View lastTab = tabs.getChildTabViewAt(tabs.getTabCount() - 1);
125         lastTab.setOnKeyListener(keyListener);
126         View shopButton = findViewById(R.id.market_button);
127         shopButton.setOnKeyListener(keyListener);
128 
129         // Hide the tab bar until we measure
130         mTabsContainer.setAlpha(0f);
131     }
132 
133     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)134     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
135         boolean remeasureTabWidth = (mTabs.getLayoutParams().width <= 0);
136         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
137 
138         // Set the width of the tab list to the content width
139         if (remeasureTabWidth) {
140             int contentWidth = mAppsCustomizePane.getPageContentWidth();
141             if (contentWidth > 0) {
142                 // Set the width and show the tab bar (if we have a loading graphic, we can switch
143                 // it off here)
144                 mTabs.getLayoutParams().width = contentWidth;
145                 post(new Runnable() {
146                     public void run() {
147                         mTabs.requestLayout();
148                         mTabsContainer.setAlpha(1f);
149                     }
150                 });
151             }
152         }
153         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
154     }
155 
156     @Override
onTouchEvent(MotionEvent event)157     public boolean onTouchEvent(MotionEvent event) {
158         // Intercept all touch events up to the bottom of the AppsCustomizePane so they do not fall
159         // through to the workspace and trigger showWorkspace()
160         if (event.getY() < mAppsCustomizePane.getBottom()) {
161             return true;
162         }
163         return super.onTouchEvent(event);
164     }
165 
onTabChangedStart()166     private void onTabChangedStart() {
167         mAppsCustomizePane.hideScrollingIndicator(false);
168     }
169 
reloadCurrentPage()170     private void reloadCurrentPage() {
171         if (!LauncherApplication.isScreenLarge()) {
172             mAppsCustomizePane.flashScrollingIndicator();
173         }
174         mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage());
175         mAppsCustomizePane.requestFocus();
176     }
177 
onTabChangedEnd(AppsCustomizePagedView.ContentType type)178     private void onTabChangedEnd(AppsCustomizePagedView.ContentType type) {
179         mAppsCustomizePane.setContentType(type);
180     }
181 
182     @Override
onTabChanged(String tabId)183     public void onTabChanged(String tabId) {
184         final AppsCustomizePagedView.ContentType type = getContentTypeForTabTag(tabId);
185         if (mSuppressContentCallback) {
186             mSuppressContentCallback = false;
187             return;
188         }
189 
190         // Animate the changing of the tab content by fading pages in and out
191         final Resources res = getResources();
192         final int duration = res.getInteger(R.integer.config_tabTransitionDuration);
193 
194         // We post a runnable here because there is a delay while the first page is loading and
195         // the feedback from having changed the tab almost feels better than having it stick
196         post(new Runnable() {
197             @Override
198             public void run() {
199                 if (mAppsCustomizePane.getMeasuredWidth() <= 0 ||
200                         mAppsCustomizePane.getMeasuredHeight() <= 0) {
201                     reloadCurrentPage();
202                     return;
203                 }
204 
205                 // Setup the animation buffer
206                 Bitmap b = Bitmap.createBitmap(mAppsCustomizePane.getMeasuredWidth(),
207                         mAppsCustomizePane.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
208                 Canvas c = new Canvas(b);
209                 mAppsCustomizePane.draw(c);
210                 mAppsCustomizePane.setAlpha(0f);
211                 mAnimationBuffer.setImageBitmap(b);
212                 mAnimationBuffer.setAlpha(1f);
213                 mAnimationBuffer.setVisibility(View.VISIBLE);
214                 c.setBitmap(null);
215                 b = null;
216 
217                 // Toggle the new content
218                 onTabChangedStart();
219                 onTabChangedEnd(type);
220 
221                 // Animate the transition
222                 ObjectAnimator outAnim = ObjectAnimator.ofFloat(mAnimationBuffer, "alpha", 0f);
223                 outAnim.addListener(new AnimatorListenerAdapter() {
224                     @Override
225                     public void onAnimationEnd(Animator animation) {
226                         mAnimationBuffer.setVisibility(View.GONE);
227                         mAnimationBuffer.setImageBitmap(null);
228                     }
229                 });
230                 ObjectAnimator inAnim = ObjectAnimator.ofFloat(mAppsCustomizePane, "alpha", 1f);
231                 inAnim.addListener(new AnimatorListenerAdapter() {
232                     @Override
233                     public void onAnimationEnd(Animator animation) {
234                         reloadCurrentPage();
235                     }
236                 });
237                 AnimatorSet animSet = new AnimatorSet();
238                 animSet.playTogether(outAnim, inAnim);
239                 animSet.setDuration(duration);
240                 animSet.start();
241             }
242         });
243     }
244 
setCurrentTabFromContent(AppsCustomizePagedView.ContentType type)245     public void setCurrentTabFromContent(AppsCustomizePagedView.ContentType type) {
246         mSuppressContentCallback = true;
247         setCurrentTabByTag(getTabTagForContentType(type));
248     }
249 
250     /**
251      * Returns the content type for the specified tab tag.
252      */
getContentTypeForTabTag(String tag)253     public AppsCustomizePagedView.ContentType getContentTypeForTabTag(String tag) {
254         if (tag.equals(APPS_TAB_TAG)) {
255             return AppsCustomizePagedView.ContentType.Applications;
256         } else if (tag.equals(WIDGETS_TAB_TAG)) {
257             return AppsCustomizePagedView.ContentType.Widgets;
258         }
259         return AppsCustomizePagedView.ContentType.Applications;
260     }
261 
262     /**
263      * Returns the tab tag for a given content type.
264      */
getTabTagForContentType(AppsCustomizePagedView.ContentType type)265     public String getTabTagForContentType(AppsCustomizePagedView.ContentType type) {
266         if (type == AppsCustomizePagedView.ContentType.Applications) {
267             return APPS_TAB_TAG;
268         } else if (type == AppsCustomizePagedView.ContentType.Widgets) {
269             return WIDGETS_TAB_TAG;
270         }
271         return APPS_TAB_TAG;
272     }
273 
274     /**
275      * Disable focus on anything under this view in the hierarchy if we are not visible.
276      */
277     @Override
getDescendantFocusability()278     public int getDescendantFocusability() {
279         if (getVisibility() != View.VISIBLE) {
280             return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
281         }
282         return super.getDescendantFocusability();
283     }
284 
reset()285     void reset() {
286         if (mInTransition) {
287             // Defer to after the transition to reset
288             mResetAfterTransition = true;
289         } else {
290             // Reset immediately
291             mAppsCustomizePane.reset();
292         }
293     }
294 
295     /* LauncherTransitionable overrides */
296     @Override
onLauncherTransitionStart(Launcher l, Animator animation, boolean toWorkspace)297     public void onLauncherTransitionStart(Launcher l, Animator animation, boolean toWorkspace) {
298         mInTransition = true;
299         // isHardwareAccelerated() checks if we're attached to a window and if that
300         // window is HW accelerated-- we were sometimes not attached to a window
301         // and buildLayer was throwing an IllegalStateException
302         if (animation != null && isHardwareAccelerated()) {
303             // Turn on hardware layers for performance
304             setLayerType(LAYER_TYPE_HARDWARE, null);
305 
306             // force building the layer at the beginning of the animation, so you don't get a
307             // blip early in the animation
308             buildLayer();
309         }
310         if (!toWorkspace && !LauncherApplication.isScreenLarge()) {
311             mAppsCustomizePane.showScrollingIndicator(false);
312         }
313         if (mResetAfterTransition) {
314             mAppsCustomizePane.reset();
315             mResetAfterTransition = false;
316         }
317     }
318 
319     @Override
onLauncherTransitionEnd(Launcher l, Animator animation, boolean toWorkspace)320     public void onLauncherTransitionEnd(Launcher l, Animator animation, boolean toWorkspace) {
321         mInTransition = false;
322         if (animation != null) {
323             setLayerType(LAYER_TYPE_NONE, null);
324         }
325 
326         if (!toWorkspace) {
327             // Dismiss the cling if necessary
328             l.dismissWorkspaceCling(null);
329 
330             if (!LauncherApplication.isScreenLarge()) {
331                 mAppsCustomizePane.hideScrollingIndicator(false);
332             }
333         }
334     }
335 
isTransitioning()336     boolean isTransitioning() {
337         return mInTransition;
338     }
339 }
340