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