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