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