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