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