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