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