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