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