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