WindowDecorActionBar.java revision e43340c80dc66c45edc793ecd0343774aa34d108
1/*
2 * Copyright (C) 2010 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.internal.app;
18
19import android.animation.ValueAnimator;
20import android.view.ViewParent;
21import com.android.internal.view.ActionBarPolicy;
22import com.android.internal.view.menu.MenuBuilder;
23import com.android.internal.view.menu.MenuPopupHelper;
24import com.android.internal.view.menu.SubMenuBuilder;
25import com.android.internal.widget.ActionBarContainer;
26import com.android.internal.widget.ActionBarContextView;
27import com.android.internal.widget.ActionBarOverlayLayout;
28import com.android.internal.widget.ActionBarView;
29import com.android.internal.widget.ScrollingTabContainerView;
30
31import android.animation.Animator;
32import android.animation.Animator.AnimatorListener;
33import android.animation.AnimatorListenerAdapter;
34import android.animation.AnimatorSet;
35import android.animation.ObjectAnimator;
36import android.app.ActionBar;
37import android.app.Activity;
38import android.app.Dialog;
39import android.app.FragmentTransaction;
40import android.content.Context;
41import android.content.res.Configuration;
42import android.content.res.Resources;
43import android.graphics.drawable.Drawable;
44import android.os.Handler;
45import android.util.TypedValue;
46import android.view.ActionMode;
47import android.view.ContextThemeWrapper;
48import android.view.LayoutInflater;
49import android.view.Menu;
50import android.view.MenuInflater;
51import android.view.MenuItem;
52import android.view.View;
53import android.view.Window;
54import android.view.accessibility.AccessibilityEvent;
55import android.view.animation.AnimationUtils;
56import android.widget.SpinnerAdapter;
57
58import java.lang.ref.WeakReference;
59import java.util.ArrayList;
60import java.util.Map;
61
62/**
63 * WindowDecorActionBar is the ActionBar implementation used
64 * by devices of all screen sizes as part of the window decor layout.
65 * If it detects a compatible decor, it will split contextual modes
66 * across both the ActionBarView at the top of the screen and
67 * a horizontal LinearLayout at the bottom which is normally hidden.
68 */
69public class WindowDecorActionBar extends ActionBar {
70    private static final String TAG = "WindowDecorActionBar";
71
72    private Context mContext;
73    private Context mThemedContext;
74    private Activity mActivity;
75    private Dialog mDialog;
76
77    private ActionBarOverlayLayout mOverlayLayout;
78    private ActionBarContainer mContainerView;
79    private ActionBarView mActionView;
80    private ActionBarContextView mContextView;
81    private ActionBarContainer mSplitView;
82    private View mContentView;
83    private ScrollingTabContainerView mTabScrollView;
84
85    private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
86
87    private TabImpl mSelectedTab;
88    private int mSavedTabPosition = INVALID_POSITION;
89
90    private boolean mDisplayHomeAsUpSet;
91
92    ActionModeImpl mActionMode;
93    ActionMode mDeferredDestroyActionMode;
94    ActionMode.Callback mDeferredModeDestroyCallback;
95
96    private boolean mLastMenuVisibility;
97    private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners =
98            new ArrayList<OnMenuVisibilityListener>();
99
100    private static final int CONTEXT_DISPLAY_NORMAL = 0;
101    private static final int CONTEXT_DISPLAY_SPLIT = 1;
102
103    private static final int INVALID_POSITION = -1;
104
105    private int mContextDisplayMode;
106    private boolean mHasEmbeddedTabs;
107
108    private int mCurWindowVisibility = View.VISIBLE;
109
110    private boolean mContentAnimations = true;
111    private boolean mHiddenByApp;
112    private boolean mHiddenBySystem;
113    private boolean mShowingForMode;
114
115    private boolean mNowShowing = true;
116
117    private Animator mCurrentShowAnim;
118    private boolean mShowHideAnimationEnabled;
119
120    final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
121        @Override
122        public void onAnimationEnd(Animator animation) {
123            if (mContentAnimations && mContentView != null) {
124                mContentView.setTranslationY(0);
125                mContainerView.setTranslationY(0);
126            }
127            if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
128                mSplitView.setVisibility(View.GONE);
129            }
130            mContainerView.setVisibility(View.GONE);
131            mContainerView.setTransitioning(false);
132            mCurrentShowAnim = null;
133            completeDeferredDestroyActionMode();
134            if (mOverlayLayout != null) {
135                mOverlayLayout.requestFitSystemWindows();
136            }
137        }
138    };
139
140    final AnimatorListener mShowListener = new AnimatorListenerAdapter() {
141        @Override
142        public void onAnimationEnd(Animator animation) {
143            mCurrentShowAnim = null;
144            mContainerView.requestLayout();
145        }
146    };
147
148    final ValueAnimator.AnimatorUpdateListener mUpdateListener =
149            new ValueAnimator.AnimatorUpdateListener() {
150        @Override
151        public void onAnimationUpdate(ValueAnimator animation) {
152            final ViewParent parent = mContainerView.getParent();
153            ((View) parent).invalidate();
154        }
155    };
156
157    public WindowDecorActionBar(Activity activity) {
158        mActivity = activity;
159        Window window = activity.getWindow();
160        View decor = window.getDecorView();
161        boolean overlayMode = mActivity.getWindow().hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
162        init(decor);
163        if (!overlayMode) {
164            mContentView = decor.findViewById(android.R.id.content);
165        }
166    }
167
168    public WindowDecorActionBar(Dialog dialog) {
169        mDialog = dialog;
170        init(dialog.getWindow().getDecorView());
171    }
172
173    private void init(View decor) {
174        mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById(
175                com.android.internal.R.id.action_bar_overlay_layout);
176        if (mOverlayLayout != null) {
177            mOverlayLayout.setActionBar(this);
178        }
179        mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
180        mContextView = (ActionBarContextView) decor.findViewById(
181                com.android.internal.R.id.action_context_bar);
182        mContainerView = (ActionBarContainer) decor.findViewById(
183                com.android.internal.R.id.action_bar_container);
184        mSplitView = (ActionBarContainer) decor.findViewById(
185                com.android.internal.R.id.split_action_bar);
186
187        if (mActionView == null || mContextView == null || mContainerView == null) {
188            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
189                    "with a compatible window decor layout");
190        }
191
192        mContext = mActionView.getContext();
193        mActionView.setContextView(mContextView);
194        mContextDisplayMode = mActionView.isSplitActionBar() ?
195                CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;
196
197        // This was initially read from the action bar style
198        final int current = mActionView.getDisplayOptions();
199        final boolean homeAsUp = (current & DISPLAY_HOME_AS_UP) != 0;
200        if (homeAsUp) {
201            mDisplayHomeAsUpSet = true;
202        }
203
204        ActionBarPolicy abp = ActionBarPolicy.get(mContext);
205        setHomeButtonEnabled(abp.enableHomeButtonByDefault() || homeAsUp);
206        setHasEmbeddedTabs(abp.hasEmbeddedTabs());
207    }
208
209    public void onConfigurationChanged(Configuration newConfig) {
210        setHasEmbeddedTabs(ActionBarPolicy.get(mContext).hasEmbeddedTabs());
211    }
212
213    private void setHasEmbeddedTabs(boolean hasEmbeddedTabs) {
214        mHasEmbeddedTabs = hasEmbeddedTabs;
215        // Switch tab layout configuration if needed
216        if (!mHasEmbeddedTabs) {
217            mActionView.setEmbeddedTabView(null);
218            mContainerView.setTabContainer(mTabScrollView);
219        } else {
220            mContainerView.setTabContainer(null);
221            mActionView.setEmbeddedTabView(mTabScrollView);
222        }
223        final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS;
224        if (mTabScrollView != null) {
225            if (isInTabMode) {
226                mTabScrollView.setVisibility(View.VISIBLE);
227                if (mOverlayLayout != null) {
228                    mOverlayLayout.requestFitSystemWindows();
229                }
230            } else {
231                mTabScrollView.setVisibility(View.GONE);
232            }
233        }
234        mActionView.setCollapsable(!mHasEmbeddedTabs && isInTabMode);
235    }
236
237    public boolean hasNonEmbeddedTabs() {
238        return !mHasEmbeddedTabs && getNavigationMode() == NAVIGATION_MODE_TABS;
239    }
240
241    private void ensureTabsExist() {
242        if (mTabScrollView != null) {
243            return;
244        }
245
246        ScrollingTabContainerView tabScroller = new ScrollingTabContainerView(mContext);
247
248        if (mHasEmbeddedTabs) {
249            tabScroller.setVisibility(View.VISIBLE);
250            mActionView.setEmbeddedTabView(tabScroller);
251        } else {
252            if (getNavigationMode() == NAVIGATION_MODE_TABS) {
253                tabScroller.setVisibility(View.VISIBLE);
254                if (mOverlayLayout != null) {
255                    mOverlayLayout.requestFitSystemWindows();
256                }
257            } else {
258                tabScroller.setVisibility(View.GONE);
259            }
260            mContainerView.setTabContainer(tabScroller);
261        }
262        mTabScrollView = tabScroller;
263    }
264
265    void completeDeferredDestroyActionMode() {
266        if (mDeferredModeDestroyCallback != null) {
267            mDeferredModeDestroyCallback.onDestroyActionMode(mDeferredDestroyActionMode);
268            mDeferredDestroyActionMode = null;
269            mDeferredModeDestroyCallback = null;
270        }
271    }
272
273    public void setWindowVisibility(int visibility) {
274        mCurWindowVisibility = visibility;
275    }
276
277    /**
278     * Enables or disables animation between show/hide states.
279     * If animation is disabled using this method, animations in progress
280     * will be finished.
281     *
282     * @param enabled true to animate, false to not animate.
283     */
284    public void setShowHideAnimationEnabled(boolean enabled) {
285        mShowHideAnimationEnabled = enabled;
286        if (!enabled && mCurrentShowAnim != null) {
287            mCurrentShowAnim.end();
288        }
289    }
290
291    public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
292        mMenuVisibilityListeners.add(listener);
293    }
294
295    public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
296        mMenuVisibilityListeners.remove(listener);
297    }
298
299    public void dispatchMenuVisibilityChanged(boolean isVisible) {
300        if (isVisible == mLastMenuVisibility) {
301            return;
302        }
303        mLastMenuVisibility = isVisible;
304
305        final int count = mMenuVisibilityListeners.size();
306        for (int i = 0; i < count; i++) {
307            mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible);
308        }
309    }
310
311    @Override
312    public void setCustomView(int resId) {
313        setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId, mActionView, false));
314    }
315
316    @Override
317    public void setDisplayUseLogoEnabled(boolean useLogo) {
318        setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO);
319    }
320
321    @Override
322    public void setDisplayShowHomeEnabled(boolean showHome) {
323        setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME);
324    }
325
326    @Override
327    public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) {
328        setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP);
329    }
330
331    @Override
332    public void setDisplayShowTitleEnabled(boolean showTitle) {
333        setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE);
334    }
335
336    @Override
337    public void setDisplayShowCustomEnabled(boolean showCustom) {
338        setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM);
339    }
340
341    @Override
342    public void setHomeButtonEnabled(boolean enable) {
343        mActionView.setHomeButtonEnabled(enable);
344    }
345
346    @Override
347    public void setTitle(int resId) {
348        setTitle(mContext.getString(resId));
349    }
350
351    @Override
352    public void setSubtitle(int resId) {
353        setSubtitle(mContext.getString(resId));
354    }
355
356    public void captureSharedElements(Map<String, View> sharedElements) {
357        mContainerView.findSharedElements(sharedElements);
358    }
359
360    public void setSelectedNavigationItem(int position) {
361        switch (mActionView.getNavigationMode()) {
362        case NAVIGATION_MODE_TABS:
363            selectTab(mTabs.get(position));
364            break;
365        case NAVIGATION_MODE_LIST:
366            mActionView.setDropdownSelectedPosition(position);
367            break;
368        default:
369            throw new IllegalStateException(
370                    "setSelectedNavigationIndex not valid for current navigation mode");
371        }
372    }
373
374    public void removeAllTabs() {
375        cleanupTabs();
376    }
377
378    private void cleanupTabs() {
379        if (mSelectedTab != null) {
380            selectTab(null);
381        }
382        mTabs.clear();
383        if (mTabScrollView != null) {
384            mTabScrollView.removeAllTabs();
385        }
386        mSavedTabPosition = INVALID_POSITION;
387    }
388
389    public void setTitle(CharSequence title) {
390        mActionView.setTitle(title);
391    }
392
393    public void setSubtitle(CharSequence subtitle) {
394        mActionView.setSubtitle(subtitle);
395    }
396
397    public void setDisplayOptions(int options) {
398        if ((options & DISPLAY_HOME_AS_UP) != 0) {
399            mDisplayHomeAsUpSet = true;
400        }
401        mActionView.setDisplayOptions(options);
402    }
403
404    public void setDisplayOptions(int options, int mask) {
405        final int current = mActionView.getDisplayOptions();
406        if ((mask & DISPLAY_HOME_AS_UP) != 0) {
407            mDisplayHomeAsUpSet = true;
408        }
409        mActionView.setDisplayOptions((options & mask) | (current & ~mask));
410    }
411
412    public void setBackgroundDrawable(Drawable d) {
413        mContainerView.setPrimaryBackground(d);
414    }
415
416    public void setStackedBackgroundDrawable(Drawable d) {
417        mContainerView.setStackedBackground(d);
418    }
419
420    public void setSplitBackgroundDrawable(Drawable d) {
421        if (mSplitView != null) {
422            mSplitView.setSplitBackground(d);
423        }
424    }
425
426    public View getCustomView() {
427        return mActionView.getCustomNavigationView();
428    }
429
430    public CharSequence getTitle() {
431        return mActionView.getTitle();
432    }
433
434    public CharSequence getSubtitle() {
435        return mActionView.getSubtitle();
436    }
437
438    public int getNavigationMode() {
439        return mActionView.getNavigationMode();
440    }
441
442    public int getDisplayOptions() {
443        return mActionView.getDisplayOptions();
444    }
445
446    public ActionMode startActionMode(ActionMode.Callback callback) {
447        if (mActionMode != null) {
448            mActionMode.finish();
449        }
450
451        mContextView.killMode();
452        ActionModeImpl mode = new ActionModeImpl(callback);
453        if (mode.dispatchOnCreate()) {
454            mode.invalidate();
455            mContextView.initForMode(mode);
456            animateToMode(true);
457            if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
458                // TODO animate this
459                if (mSplitView.getVisibility() != View.VISIBLE) {
460                    mSplitView.setVisibility(View.VISIBLE);
461                    if (mOverlayLayout != null) {
462                        mOverlayLayout.requestFitSystemWindows();
463                    }
464                }
465            }
466            mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
467            mActionMode = mode;
468            return mode;
469        }
470        return null;
471    }
472
473    private void configureTab(Tab tab, int position) {
474        final TabImpl tabi = (TabImpl) tab;
475        final ActionBar.TabListener callback = tabi.getCallback();
476
477        if (callback == null) {
478            throw new IllegalStateException("Action Bar Tab must have a Callback");
479        }
480
481        tabi.setPosition(position);
482        mTabs.add(position, tabi);
483
484        final int count = mTabs.size();
485        for (int i = position + 1; i < count; i++) {
486            mTabs.get(i).setPosition(i);
487        }
488    }
489
490    @Override
491    public void addTab(Tab tab) {
492        addTab(tab, mTabs.isEmpty());
493    }
494
495    @Override
496    public void addTab(Tab tab, int position) {
497        addTab(tab, position, mTabs.isEmpty());
498    }
499
500    @Override
501    public void addTab(Tab tab, boolean setSelected) {
502        ensureTabsExist();
503        mTabScrollView.addTab(tab, setSelected);
504        configureTab(tab, mTabs.size());
505        if (setSelected) {
506            selectTab(tab);
507        }
508    }
509
510    @Override
511    public void addTab(Tab tab, int position, boolean setSelected) {
512        ensureTabsExist();
513        mTabScrollView.addTab(tab, position, setSelected);
514        configureTab(tab, position);
515        if (setSelected) {
516            selectTab(tab);
517        }
518    }
519
520    @Override
521    public Tab newTab() {
522        return new TabImpl();
523    }
524
525    @Override
526    public void removeTab(Tab tab) {
527        removeTabAt(tab.getPosition());
528    }
529
530    @Override
531    public void removeTabAt(int position) {
532        if (mTabScrollView == null) {
533            // No tabs around to remove
534            return;
535        }
536
537        int selectedTabPosition = mSelectedTab != null
538                ? mSelectedTab.getPosition() : mSavedTabPosition;
539        mTabScrollView.removeTabAt(position);
540        TabImpl removedTab = mTabs.remove(position);
541        if (removedTab != null) {
542            removedTab.setPosition(-1);
543        }
544
545        final int newTabCount = mTabs.size();
546        for (int i = position; i < newTabCount; i++) {
547            mTabs.get(i).setPosition(i);
548        }
549
550        if (selectedTabPosition == position) {
551            selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
552        }
553    }
554
555    @Override
556    public void selectTab(Tab tab) {
557        if (getNavigationMode() != NAVIGATION_MODE_TABS) {
558            mSavedTabPosition = tab != null ? tab.getPosition() : INVALID_POSITION;
559            return;
560        }
561
562        final FragmentTransaction trans = mActivity.getFragmentManager().beginTransaction()
563                .disallowAddToBackStack();
564
565        if (mSelectedTab == tab) {
566            if (mSelectedTab != null) {
567                mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans);
568                mTabScrollView.animateToTab(tab.getPosition());
569            }
570        } else {
571            mTabScrollView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
572            if (mSelectedTab != null) {
573                mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans);
574            }
575            mSelectedTab = (TabImpl) tab;
576            if (mSelectedTab != null) {
577                mSelectedTab.getCallback().onTabSelected(mSelectedTab, trans);
578            }
579        }
580
581        if (!trans.isEmpty()) {
582            trans.commit();
583        }
584    }
585
586    @Override
587    public Tab getSelectedTab() {
588        return mSelectedTab;
589    }
590
591    @Override
592    public int getHeight() {
593        return mContainerView.getHeight();
594    }
595
596    public void enableContentAnimations(boolean enabled) {
597        mContentAnimations = enabled;
598    }
599
600    @Override
601    public void show() {
602        if (mHiddenByApp) {
603            mHiddenByApp = false;
604            updateVisibility(false);
605        }
606    }
607
608    private void showForActionMode() {
609        if (!mShowingForMode) {
610            mShowingForMode = true;
611            if (mOverlayLayout != null) {
612                mOverlayLayout.setShowingForActionMode(true);
613            }
614            updateVisibility(false);
615        }
616    }
617
618    public void showForSystem() {
619        if (mHiddenBySystem) {
620            mHiddenBySystem = false;
621            updateVisibility(true);
622        }
623    }
624
625    @Override
626    public void hide() {
627        if (!mHiddenByApp) {
628            mHiddenByApp = true;
629            updateVisibility(false);
630        }
631    }
632
633    private void hideForActionMode() {
634        if (mShowingForMode) {
635            mShowingForMode = false;
636            if (mOverlayLayout != null) {
637                mOverlayLayout.setShowingForActionMode(false);
638            }
639            updateVisibility(false);
640        }
641    }
642
643    public void hideForSystem() {
644        if (!mHiddenBySystem) {
645            mHiddenBySystem = true;
646            updateVisibility(true);
647        }
648    }
649
650    private static boolean checkShowingFlags(boolean hiddenByApp, boolean hiddenBySystem,
651            boolean showingForMode) {
652        if (showingForMode) {
653            return true;
654        } else if (hiddenByApp || hiddenBySystem) {
655            return false;
656        } else {
657            return true;
658        }
659    }
660
661    private void updateVisibility(boolean fromSystem) {
662        // Based on the current state, should we be hidden or shown?
663        final boolean shown = checkShowingFlags(mHiddenByApp, mHiddenBySystem,
664                mShowingForMode);
665
666        if (shown) {
667            if (!mNowShowing) {
668                mNowShowing = true;
669                doShow(fromSystem);
670            }
671        } else {
672            if (mNowShowing) {
673                mNowShowing = false;
674                doHide(fromSystem);
675            }
676        }
677    }
678
679    public void doShow(boolean fromSystem) {
680        if (mCurrentShowAnim != null) {
681            mCurrentShowAnim.end();
682        }
683        mContainerView.setVisibility(View.VISIBLE);
684
685        if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled
686                || fromSystem)) {
687            mContainerView.setTranslationY(0); // because we're about to ask its window loc
688            float startingY = -mContainerView.getHeight();
689            if (fromSystem) {
690                int topLeft[] = {0, 0};
691                mContainerView.getLocationInWindow(topLeft);
692                startingY -= topLeft[1];
693            }
694            mContainerView.setTranslationY(startingY);
695            AnimatorSet anim = new AnimatorSet();
696            ObjectAnimator a = ObjectAnimator.ofFloat(mContainerView, View.TRANSLATION_Y, 0);
697            a.addUpdateListener(mUpdateListener);
698            AnimatorSet.Builder b = anim.play(a);
699            if (mContentAnimations && mContentView != null) {
700                b.with(ObjectAnimator.ofFloat(mContentView, View.TRANSLATION_Y,
701                        startingY, 0));
702            }
703            if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
704                mSplitView.setTranslationY(mSplitView.getHeight());
705                mSplitView.setVisibility(View.VISIBLE);
706                b.with(ObjectAnimator.ofFloat(mSplitView, View.TRANSLATION_Y, 0));
707            }
708            anim.setInterpolator(AnimationUtils.loadInterpolator(mContext,
709                    com.android.internal.R.interpolator.decelerate_cubic));
710            anim.setDuration(250);
711            // If this is being shown from the system, add a small delay.
712            // This is because we will also be animating in the status bar,
713            // and these two elements can't be done in lock-step.  So we give
714            // a little time for the status bar to start its animation before
715            // the action bar animates.  (This corresponds to the corresponding
716            // case when hiding, where the status bar has a small delay before
717            // starting.)
718            anim.addListener(mShowListener);
719            mCurrentShowAnim = anim;
720            anim.start();
721        } else {
722            mContainerView.setAlpha(1);
723            mContainerView.setTranslationY(0);
724            if (mContentAnimations && mContentView != null) {
725                mContentView.setTranslationY(0);
726            }
727            if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
728                mSplitView.setAlpha(1);
729                mSplitView.setTranslationY(0);
730                mSplitView.setVisibility(View.VISIBLE);
731            }
732            mShowListener.onAnimationEnd(null);
733        }
734        if (mOverlayLayout != null) {
735            mOverlayLayout.requestFitSystemWindows();
736        }
737    }
738
739    public void doHide(boolean fromSystem) {
740        if (mCurrentShowAnim != null) {
741            mCurrentShowAnim.end();
742        }
743
744        if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled
745                || fromSystem)) {
746            mContainerView.setAlpha(1);
747            mContainerView.setTransitioning(true);
748            AnimatorSet anim = new AnimatorSet();
749            float endingY = -mContainerView.getHeight();
750            if (fromSystem) {
751                int topLeft[] = {0, 0};
752                mContainerView.getLocationInWindow(topLeft);
753                endingY -= topLeft[1];
754            }
755            ObjectAnimator a = ObjectAnimator.ofFloat(mContainerView, View.TRANSLATION_Y, endingY);
756            a.addUpdateListener(mUpdateListener);
757            AnimatorSet.Builder b = anim.play(a);
758            if (mContentAnimations && mContentView != null) {
759                b.with(ObjectAnimator.ofFloat(mContentView, View.TRANSLATION_Y,
760                        0, endingY));
761            }
762            if (mSplitView != null && mSplitView.getVisibility() == View.VISIBLE) {
763                mSplitView.setAlpha(1);
764                b.with(ObjectAnimator.ofFloat(mSplitView, View.TRANSLATION_Y,
765                        mSplitView.getHeight()));
766            }
767            anim.setInterpolator(AnimationUtils.loadInterpolator(mContext,
768                    com.android.internal.R.interpolator.accelerate_cubic));
769            anim.setDuration(250);
770            anim.addListener(mHideListener);
771            mCurrentShowAnim = anim;
772            anim.start();
773        } else {
774            mHideListener.onAnimationEnd(null);
775        }
776    }
777
778    public boolean isShowing() {
779        return mNowShowing;
780    }
781
782    public boolean isSystemShowing() {
783        return !mHiddenBySystem;
784    }
785
786    void animateToMode(boolean toActionMode) {
787        if (toActionMode) {
788            showForActionMode();
789        } else {
790            hideForActionMode();
791        }
792
793        mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
794        mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE);
795        if (mTabScrollView != null && !mActionView.hasEmbeddedTabs() && mActionView.isCollapsed()) {
796            mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
797        }
798    }
799
800    public Context getThemedContext() {
801        if (mThemedContext == null) {
802            TypedValue outValue = new TypedValue();
803            Resources.Theme currentTheme = mContext.getTheme();
804            currentTheme.resolveAttribute(com.android.internal.R.attr.actionBarWidgetTheme,
805                    outValue, true);
806            final int targetThemeRes = outValue.resourceId;
807
808            if (targetThemeRes != 0 && mContext.getThemeResId() != targetThemeRes) {
809                mThemedContext = new ContextThemeWrapper(mContext, targetThemeRes);
810            } else {
811                mThemedContext = mContext;
812            }
813        }
814        return mThemedContext;
815    }
816
817    @Override
818    public boolean isTitleTruncated() {
819        return mActionView != null && mActionView.isTitleTruncated();
820    }
821
822    @Override
823    public void setHomeAsUpIndicator(Drawable indicator) {
824        mActionView.setHomeAsUpIndicator(indicator);
825    }
826
827    @Override
828    public void setHomeAsUpIndicator(int resId) {
829        mActionView.setHomeAsUpIndicator(resId);
830    }
831
832    @Override
833    public void setHomeActionContentDescription(CharSequence description) {
834        mActionView.setHomeActionContentDescription(description);
835    }
836
837    @Override
838    public void setHomeActionContentDescription(int resId) {
839        mActionView.setHomeActionContentDescription(resId);
840    }
841
842    /**
843     * @hide
844     */
845    public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback {
846        private ActionMode.Callback mCallback;
847        private MenuBuilder mMenu;
848        private WeakReference<View> mCustomView;
849
850        public ActionModeImpl(ActionMode.Callback callback) {
851            mCallback = callback;
852            mMenu = new MenuBuilder(getThemedContext())
853                    .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
854            mMenu.setCallback(this);
855        }
856
857        @Override
858        public MenuInflater getMenuInflater() {
859            return new MenuInflater(getThemedContext());
860        }
861
862        @Override
863        public Menu getMenu() {
864            return mMenu;
865        }
866
867        @Override
868        public void finish() {
869            if (mActionMode != this) {
870                // Not the active action mode - no-op
871                return;
872            }
873
874            // If this change in state is going to cause the action bar
875            // to be hidden, defer the onDestroy callback until the animation
876            // is finished and associated relayout is about to happen. This lets
877            // apps better anticipate visibility and layout behavior.
878            if (!checkShowingFlags(mHiddenByApp, mHiddenBySystem, false)) {
879                // With the current state but the action bar hidden, our
880                // overall showing state is going to be false.
881                mDeferredDestroyActionMode = this;
882                mDeferredModeDestroyCallback = mCallback;
883            } else {
884                mCallback.onDestroyActionMode(this);
885            }
886            mCallback = null;
887            animateToMode(false);
888
889            // Clear out the context mode views after the animation finishes
890            mContextView.closeMode();
891            mActionView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
892
893            mActionMode = null;
894        }
895
896        @Override
897        public void invalidate() {
898            mMenu.stopDispatchingItemsChanged();
899            try {
900                mCallback.onPrepareActionMode(this, mMenu);
901            } finally {
902                mMenu.startDispatchingItemsChanged();
903            }
904        }
905
906        public boolean dispatchOnCreate() {
907            mMenu.stopDispatchingItemsChanged();
908            try {
909                return mCallback.onCreateActionMode(this, mMenu);
910            } finally {
911                mMenu.startDispatchingItemsChanged();
912            }
913        }
914
915        @Override
916        public void setCustomView(View view) {
917            mContextView.setCustomView(view);
918            mCustomView = new WeakReference<View>(view);
919        }
920
921        @Override
922        public void setSubtitle(CharSequence subtitle) {
923            mContextView.setSubtitle(subtitle);
924        }
925
926        @Override
927        public void setTitle(CharSequence title) {
928            mContextView.setTitle(title);
929        }
930
931        @Override
932        public void setTitle(int resId) {
933            setTitle(mContext.getResources().getString(resId));
934        }
935
936        @Override
937        public void setSubtitle(int resId) {
938            setSubtitle(mContext.getResources().getString(resId));
939        }
940
941        @Override
942        public CharSequence getTitle() {
943            return mContextView.getTitle();
944        }
945
946        @Override
947        public CharSequence getSubtitle() {
948            return mContextView.getSubtitle();
949        }
950
951        @Override
952        public void setTitleOptionalHint(boolean titleOptional) {
953            super.setTitleOptionalHint(titleOptional);
954            mContextView.setTitleOptional(titleOptional);
955        }
956
957        @Override
958        public boolean isTitleOptional() {
959            return mContextView.isTitleOptional();
960        }
961
962        @Override
963        public View getCustomView() {
964            return mCustomView != null ? mCustomView.get() : null;
965        }
966
967        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
968            if (mCallback != null) {
969                return mCallback.onActionItemClicked(this, item);
970            } else {
971                return false;
972            }
973        }
974
975        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
976        }
977
978        public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
979            if (mCallback == null) {
980                return false;
981            }
982
983            if (!subMenu.hasVisibleItems()) {
984                return true;
985            }
986
987            new MenuPopupHelper(getThemedContext(), subMenu).show();
988            return true;
989        }
990
991        public void onCloseSubMenu(SubMenuBuilder menu) {
992        }
993
994        public void onMenuModeChange(MenuBuilder menu) {
995            if (mCallback == null) {
996                return;
997            }
998            invalidate();
999            mContextView.showOverflowMenu();
1000        }
1001    }
1002
1003    /**
1004     * @hide
1005     */
1006    public class TabImpl extends ActionBar.Tab {
1007        private ActionBar.TabListener mCallback;
1008        private Object mTag;
1009        private Drawable mIcon;
1010        private CharSequence mText;
1011        private CharSequence mContentDesc;
1012        private int mPosition = -1;
1013        private View mCustomView;
1014
1015        @Override
1016        public Object getTag() {
1017            return mTag;
1018        }
1019
1020        @Override
1021        public Tab setTag(Object tag) {
1022            mTag = tag;
1023            return this;
1024        }
1025
1026        public ActionBar.TabListener getCallback() {
1027            return mCallback;
1028        }
1029
1030        @Override
1031        public Tab setTabListener(ActionBar.TabListener callback) {
1032            mCallback = callback;
1033            return this;
1034        }
1035
1036        @Override
1037        public View getCustomView() {
1038            return mCustomView;
1039        }
1040
1041        @Override
1042        public Tab setCustomView(View view) {
1043            mCustomView = view;
1044            if (mPosition >= 0) {
1045                mTabScrollView.updateTab(mPosition);
1046            }
1047            return this;
1048        }
1049
1050        @Override
1051        public Tab setCustomView(int layoutResId) {
1052            return setCustomView(LayoutInflater.from(getThemedContext())
1053                    .inflate(layoutResId, null));
1054        }
1055
1056        @Override
1057        public Drawable getIcon() {
1058            return mIcon;
1059        }
1060
1061        @Override
1062        public int getPosition() {
1063            return mPosition;
1064        }
1065
1066        public void setPosition(int position) {
1067            mPosition = position;
1068        }
1069
1070        @Override
1071        public CharSequence getText() {
1072            return mText;
1073        }
1074
1075        @Override
1076        public Tab setIcon(Drawable icon) {
1077            mIcon = icon;
1078            if (mPosition >= 0) {
1079                mTabScrollView.updateTab(mPosition);
1080            }
1081            return this;
1082        }
1083
1084        @Override
1085        public Tab setIcon(int resId) {
1086            return setIcon(mContext.getDrawable(resId));
1087        }
1088
1089        @Override
1090        public Tab setText(CharSequence text) {
1091            mText = text;
1092            if (mPosition >= 0) {
1093                mTabScrollView.updateTab(mPosition);
1094            }
1095            return this;
1096        }
1097
1098        @Override
1099        public Tab setText(int resId) {
1100            return setText(mContext.getResources().getText(resId));
1101        }
1102
1103        @Override
1104        public void select() {
1105            selectTab(this);
1106        }
1107
1108        @Override
1109        public Tab setContentDescription(int resId) {
1110            return setContentDescription(mContext.getResources().getText(resId));
1111        }
1112
1113        @Override
1114        public Tab setContentDescription(CharSequence contentDesc) {
1115            mContentDesc = contentDesc;
1116            if (mPosition >= 0) {
1117                mTabScrollView.updateTab(mPosition);
1118            }
1119            return this;
1120        }
1121
1122        @Override
1123        public CharSequence getContentDescription() {
1124            return mContentDesc;
1125        }
1126    }
1127
1128    @Override
1129    public void setCustomView(View view) {
1130        mActionView.setCustomNavigationView(view);
1131    }
1132
1133    @Override
1134    public void setCustomView(View view, LayoutParams layoutParams) {
1135        view.setLayoutParams(layoutParams);
1136        mActionView.setCustomNavigationView(view);
1137    }
1138
1139    @Override
1140    public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
1141        mActionView.setDropdownAdapter(adapter);
1142        mActionView.setCallback(callback);
1143    }
1144
1145    @Override
1146    public int getSelectedNavigationIndex() {
1147        switch (mActionView.getNavigationMode()) {
1148            case NAVIGATION_MODE_TABS:
1149                return mSelectedTab != null ? mSelectedTab.getPosition() : -1;
1150            case NAVIGATION_MODE_LIST:
1151                return mActionView.getDropdownSelectedPosition();
1152            default:
1153                return -1;
1154        }
1155    }
1156
1157    @Override
1158    public int getNavigationItemCount() {
1159        switch (mActionView.getNavigationMode()) {
1160            case NAVIGATION_MODE_TABS:
1161                return mTabs.size();
1162            case NAVIGATION_MODE_LIST:
1163                SpinnerAdapter adapter = mActionView.getDropdownAdapter();
1164                return adapter != null ? adapter.getCount() : 0;
1165            default:
1166                return 0;
1167        }
1168    }
1169
1170    @Override
1171    public int getTabCount() {
1172        return mTabs.size();
1173    }
1174
1175    @Override
1176    public void setNavigationMode(int mode) {
1177        final int oldMode = mActionView.getNavigationMode();
1178        switch (oldMode) {
1179            case NAVIGATION_MODE_TABS:
1180                mSavedTabPosition = getSelectedNavigationIndex();
1181                selectTab(null);
1182                mTabScrollView.setVisibility(View.GONE);
1183                break;
1184        }
1185        if (oldMode != mode && !mHasEmbeddedTabs) {
1186            if (mOverlayLayout != null) {
1187                mOverlayLayout.requestFitSystemWindows();
1188            }
1189        }
1190        mActionView.setNavigationMode(mode);
1191        switch (mode) {
1192            case NAVIGATION_MODE_TABS:
1193                ensureTabsExist();
1194                mTabScrollView.setVisibility(View.VISIBLE);
1195                if (mSavedTabPosition != INVALID_POSITION) {
1196                    setSelectedNavigationItem(mSavedTabPosition);
1197                    mSavedTabPosition = INVALID_POSITION;
1198                }
1199                break;
1200        }
1201        mActionView.setCollapsable(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
1202    }
1203
1204    @Override
1205    public Tab getTabAt(int index) {
1206        return mTabs.get(index);
1207    }
1208
1209
1210    @Override
1211    public void setIcon(int resId) {
1212        mActionView.setIcon(resId);
1213    }
1214
1215    @Override
1216    public void setIcon(Drawable icon) {
1217        mActionView.setIcon(icon);
1218    }
1219
1220    public boolean hasIcon() {
1221        return mActionView.hasIcon();
1222    }
1223
1224    @Override
1225    public void setLogo(int resId) {
1226        mActionView.setLogo(resId);
1227    }
1228
1229    @Override
1230    public void setLogo(Drawable logo) {
1231        mActionView.setLogo(logo);
1232    }
1233
1234    public boolean hasLogo() {
1235        return mActionView.hasLogo();
1236    }
1237
1238    public void setDefaultDisplayHomeAsUpEnabled(boolean enable) {
1239        if (!mDisplayHomeAsUpSet) {
1240            setDisplayHomeAsUpEnabled(enable);
1241        }
1242    }
1243}
1244