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