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