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