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