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