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