Home | History | Annotate | Download | only in widget
      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.widget;
     18 
     19 import android.animation.LayoutTransition;
     20 import android.app.ActionBar;
     21 import android.app.ActionBar.OnNavigationListener;
     22 import android.content.Context;
     23 import android.content.pm.ApplicationInfo;
     24 import android.content.pm.PackageManager;
     25 import android.content.res.Configuration;
     26 import android.content.res.TypedArray;
     27 import android.graphics.drawable.Drawable;
     28 import android.os.Parcel;
     29 import android.os.Parcelable;
     30 import android.text.Layout;
     31 import android.text.TextUtils;
     32 import android.util.AttributeSet;
     33 import android.view.CollapsibleActionView;
     34 import android.view.Gravity;
     35 import android.view.LayoutInflater;
     36 import android.view.Menu;
     37 import android.view.MenuItem;
     38 import android.view.MotionEvent;
     39 import android.view.View;
     40 import android.view.ViewGroup;
     41 import android.view.ViewParent;
     42 import android.view.Window;
     43 import android.view.accessibility.AccessibilityEvent;
     44 import android.widget.AdapterView;
     45 import android.widget.FrameLayout;
     46 import android.widget.ImageView;
     47 import android.widget.LinearLayout;
     48 import android.widget.ProgressBar;
     49 import android.widget.Spinner;
     50 import android.widget.SpinnerAdapter;
     51 import android.widget.TextView;
     52 import com.android.internal.R;
     53 import com.android.internal.transition.ActionBarTransition;
     54 import com.android.internal.view.menu.ActionMenuItem;
     55 import com.android.internal.view.menu.ActionMenuPresenter;
     56 import com.android.internal.view.menu.ActionMenuView;
     57 import com.android.internal.view.menu.MenuBuilder;
     58 import com.android.internal.view.menu.MenuItemImpl;
     59 import com.android.internal.view.menu.MenuPresenter;
     60 import com.android.internal.view.menu.MenuView;
     61 import com.android.internal.view.menu.SubMenuBuilder;
     62 
     63 /**
     64  * @hide
     65  */
     66 public class ActionBarView extends AbsActionBarView {
     67     private static final String TAG = "ActionBarView";
     68 
     69     /**
     70      * Display options applied by default
     71      */
     72     public static final int DISPLAY_DEFAULT = 0;
     73 
     74     /**
     75      * Display options that require re-layout as opposed to a simple invalidate
     76      */
     77     private static final int DISPLAY_RELAYOUT_MASK =
     78             ActionBar.DISPLAY_SHOW_HOME |
     79             ActionBar.DISPLAY_USE_LOGO |
     80             ActionBar.DISPLAY_HOME_AS_UP |
     81             ActionBar.DISPLAY_SHOW_CUSTOM |
     82             ActionBar.DISPLAY_SHOW_TITLE |
     83             ActionBar.DISPLAY_TITLE_MULTIPLE_LINES;
     84 
     85     private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.START | Gravity.CENTER_VERTICAL;
     86 
     87     private int mNavigationMode;
     88     private int mDisplayOptions = -1;
     89     private CharSequence mTitle;
     90     private CharSequence mSubtitle;
     91     private Drawable mIcon;
     92     private Drawable mLogo;
     93     private CharSequence mHomeDescription;
     94     private int mHomeDescriptionRes;
     95 
     96     private HomeView mHomeLayout;
     97     private HomeView mExpandedHomeLayout;
     98     private LinearLayout mTitleLayout;
     99     private TextView mTitleView;
    100     private TextView mSubtitleView;
    101     private ViewGroup mUpGoerFive;
    102 
    103     private Spinner mSpinner;
    104     private LinearLayout mListNavLayout;
    105     private ScrollingTabContainerView mTabScrollView;
    106     private View mCustomNavView;
    107     private ProgressBar mProgressView;
    108     private ProgressBar mIndeterminateProgressView;
    109 
    110     private int mProgressBarPadding;
    111     private int mItemPadding;
    112 
    113     private int mTitleStyleRes;
    114     private int mSubtitleStyleRes;
    115     private int mProgressStyle;
    116     private int mIndeterminateProgressStyle;
    117 
    118     private boolean mUserTitle;
    119     private boolean mIncludeTabs;
    120     private boolean mIsCollapsable;
    121     private boolean mIsCollapsed;
    122     private boolean mWasHomeEnabled; // Was it enabled before action view expansion?
    123 
    124     private MenuBuilder mOptionsMenu;
    125     private boolean mMenuPrepared;
    126 
    127     private ActionBarContextView mContextView;
    128 
    129     private ActionMenuItem mLogoNavItem;
    130 
    131     private SpinnerAdapter mSpinnerAdapter;
    132     private OnNavigationListener mCallback;
    133 
    134     private Runnable mTabSelector;
    135 
    136     private ExpandedActionViewMenuPresenter mExpandedMenuPresenter;
    137     View mExpandedActionView;
    138 
    139     Window.Callback mWindowCallback;
    140 
    141     private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
    142             new AdapterView.OnItemSelectedListener() {
    143         public void onItemSelected(AdapterView parent, View view, int position, long id) {
    144             if (mCallback != null) {
    145                 mCallback.onNavigationItemSelected(position, id);
    146             }
    147         }
    148         public void onNothingSelected(AdapterView parent) {
    149             // Do nothing
    150         }
    151     };
    152 
    153     private final OnClickListener mExpandedActionViewUpListener = new OnClickListener() {
    154         @Override
    155         public void onClick(View v) {
    156             final MenuItemImpl item = mExpandedMenuPresenter.mCurrentExpandedItem;
    157             if (item != null) {
    158                 item.collapseActionView();
    159             }
    160         }
    161     };
    162 
    163     private final OnClickListener mUpClickListener = new OnClickListener() {
    164         public void onClick(View v) {
    165             if (mMenuPrepared) {
    166                 // Only invoke the window callback if the options menu has been initialized.
    167                 mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem);
    168             }
    169         }
    170     };
    171 
    172     public ActionBarView(Context context, AttributeSet attrs) {
    173         super(context, attrs);
    174 
    175         // Background is always provided by the container.
    176         setBackgroundResource(0);
    177 
    178         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar,
    179                 com.android.internal.R.attr.actionBarStyle, 0);
    180 
    181         ApplicationInfo appInfo = context.getApplicationInfo();
    182         PackageManager pm = context.getPackageManager();
    183         mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode,
    184                 ActionBar.NAVIGATION_MODE_STANDARD);
    185         mTitle = a.getText(R.styleable.ActionBar_title);
    186         mSubtitle = a.getText(R.styleable.ActionBar_subtitle);
    187         mLogo = a.getDrawable(R.styleable.ActionBar_logo);
    188         mIcon = a.getDrawable(R.styleable.ActionBar_icon);
    189 
    190         final LayoutInflater inflater = LayoutInflater.from(context);
    191 
    192         final int homeResId = a.getResourceId(
    193                 com.android.internal.R.styleable.ActionBar_homeLayout,
    194                 com.android.internal.R.layout.action_bar_home);
    195 
    196         mUpGoerFive = (ViewGroup) inflater.inflate(
    197                 com.android.internal.R.layout.action_bar_up_container, this, false);
    198         mHomeLayout = (HomeView) inflater.inflate(homeResId, mUpGoerFive, false);
    199 
    200         mExpandedHomeLayout = (HomeView) inflater.inflate(homeResId, mUpGoerFive, false);
    201         mExpandedHomeLayout.setShowUp(true);
    202         mExpandedHomeLayout.setOnClickListener(mExpandedActionViewUpListener);
    203         mExpandedHomeLayout.setContentDescription(getResources().getText(
    204                 R.string.action_bar_up_description));
    205 
    206         // This needs to highlight/be focusable on its own.
    207         // TODO: Clean up the handoff between expanded/normal.
    208         final Drawable upBackground = mUpGoerFive.getBackground();
    209         if (upBackground != null) {
    210             mExpandedHomeLayout.setBackground(upBackground.getConstantState().newDrawable());
    211         }
    212         mExpandedHomeLayout.setEnabled(true);
    213         mExpandedHomeLayout.setFocusable(true);
    214 
    215         mTitleStyleRes = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
    216         mSubtitleStyleRes = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0);
    217         mProgressStyle = a.getResourceId(R.styleable.ActionBar_progressBarStyle, 0);
    218         mIndeterminateProgressStyle = a.getResourceId(
    219                 R.styleable.ActionBar_indeterminateProgressStyle, 0);
    220 
    221         mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0);
    222         mItemPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_itemPadding, 0);
    223 
    224         setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT));
    225 
    226         final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
    227         if (customNavId != 0) {
    228             mCustomNavView = (View) inflater.inflate(customNavId, this, false);
    229             mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD;
    230             setDisplayOptions(mDisplayOptions | ActionBar.DISPLAY_SHOW_CUSTOM);
    231         }
    232 
    233         mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
    234 
    235         a.recycle();
    236 
    237         mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle);
    238 
    239         mUpGoerFive.setOnClickListener(mUpClickListener);
    240         mUpGoerFive.setClickable(true);
    241         mUpGoerFive.setFocusable(true);
    242 
    243         if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
    244             setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
    245         }
    246     }
    247 
    248     @Override
    249     protected void onConfigurationChanged(Configuration newConfig) {
    250         super.onConfigurationChanged(newConfig);
    251 
    252         mTitleView = null;
    253         mSubtitleView = null;
    254         if (mTitleLayout != null && mTitleLayout.getParent() == mUpGoerFive) {
    255             mUpGoerFive.removeView(mTitleLayout);
    256         }
    257         mTitleLayout = null;
    258         if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
    259             initTitle();
    260         }
    261 
    262         if (mHomeDescriptionRes != 0) {
    263             setHomeActionContentDescription(mHomeDescriptionRes);
    264         }
    265 
    266         if (mTabScrollView != null && mIncludeTabs) {
    267             ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams();
    268             if (lp != null) {
    269                 lp.width = LayoutParams.WRAP_CONTENT;
    270                 lp.height = LayoutParams.MATCH_PARENT;
    271             }
    272             mTabScrollView.setAllowCollapse(true);
    273         }
    274     }
    275 
    276     /**
    277      * Set the window callback used to invoke menu items; used for dispatching home button presses.
    278      * @param cb Window callback to dispatch to
    279      */
    280     public void setWindowCallback(Window.Callback cb) {
    281         mWindowCallback = cb;
    282     }
    283 
    284     @Override
    285     public void onDetachedFromWindow() {
    286         super.onDetachedFromWindow();
    287         removeCallbacks(mTabSelector);
    288         if (mActionMenuPresenter != null) {
    289             mActionMenuPresenter.hideOverflowMenu();
    290             mActionMenuPresenter.hideSubMenus();
    291         }
    292     }
    293 
    294     @Override
    295     public boolean shouldDelayChildPressedState() {
    296         return false;
    297     }
    298 
    299     public void initProgress() {
    300         mProgressView = new ProgressBar(mContext, null, 0, mProgressStyle);
    301         mProgressView.setId(R.id.progress_horizontal);
    302         mProgressView.setMax(10000);
    303         mProgressView.setVisibility(GONE);
    304         addView(mProgressView);
    305     }
    306 
    307     public void initIndeterminateProgress() {
    308         mIndeterminateProgressView = new ProgressBar(mContext, null, 0,
    309                 mIndeterminateProgressStyle);
    310         mIndeterminateProgressView.setId(R.id.progress_circular);
    311         mIndeterminateProgressView.setVisibility(GONE);
    312         addView(mIndeterminateProgressView);
    313     }
    314 
    315     @Override
    316     public void setSplitActionBar(boolean splitActionBar) {
    317         if (mSplitActionBar != splitActionBar) {
    318             if (mMenuView != null) {
    319                 final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
    320                 if (oldParent != null) {
    321                     oldParent.removeView(mMenuView);
    322                 }
    323                 if (splitActionBar) {
    324                     if (mSplitView != null) {
    325                         mSplitView.addView(mMenuView);
    326                     }
    327                     mMenuView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
    328                 } else {
    329                     addView(mMenuView);
    330                     mMenuView.getLayoutParams().width = LayoutParams.WRAP_CONTENT;
    331                 }
    332                 mMenuView.requestLayout();
    333             }
    334             if (mSplitView != null) {
    335                 mSplitView.setVisibility(splitActionBar ? VISIBLE : GONE);
    336             }
    337 
    338             if (mActionMenuPresenter != null) {
    339                 if (!splitActionBar) {
    340                     mActionMenuPresenter.setExpandedActionViewsExclusive(
    341                             getResources().getBoolean(
    342                                     com.android.internal.R.bool.action_bar_expanded_action_views_exclusive));
    343                 } else {
    344                     mActionMenuPresenter.setExpandedActionViewsExclusive(false);
    345                     // Allow full screen width in split mode.
    346                     mActionMenuPresenter.setWidthLimit(
    347                             getContext().getResources().getDisplayMetrics().widthPixels, true);
    348                     // No limit to the item count; use whatever will fit.
    349                     mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
    350                 }
    351             }
    352             super.setSplitActionBar(splitActionBar);
    353         }
    354     }
    355 
    356     public boolean isSplitActionBar() {
    357         return mSplitActionBar;
    358     }
    359 
    360     public boolean hasEmbeddedTabs() {
    361         return mIncludeTabs;
    362     }
    363 
    364     public void setEmbeddedTabView(ScrollingTabContainerView tabs) {
    365         if (mTabScrollView != null) {
    366             removeView(mTabScrollView);
    367         }
    368         mTabScrollView = tabs;
    369         mIncludeTabs = tabs != null;
    370         if (mIncludeTabs && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) {
    371             addView(mTabScrollView);
    372             ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams();
    373             lp.width = LayoutParams.WRAP_CONTENT;
    374             lp.height = LayoutParams.MATCH_PARENT;
    375             tabs.setAllowCollapse(true);
    376         }
    377     }
    378 
    379     public void setCallback(OnNavigationListener callback) {
    380         mCallback = callback;
    381     }
    382 
    383     public void setMenuPrepared() {
    384         mMenuPrepared = true;
    385     }
    386 
    387     public void setMenu(Menu menu, MenuPresenter.Callback cb) {
    388         if (menu == mOptionsMenu) return;
    389 
    390         if (mOptionsMenu != null) {
    391             mOptionsMenu.removeMenuPresenter(mActionMenuPresenter);
    392             mOptionsMenu.removeMenuPresenter(mExpandedMenuPresenter);
    393         }
    394 
    395         MenuBuilder builder = (MenuBuilder) menu;
    396         mOptionsMenu = builder;
    397         if (mMenuView != null) {
    398             final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
    399             if (oldParent != null) {
    400                 oldParent.removeView(mMenuView);
    401             }
    402         }
    403         if (mActionMenuPresenter == null) {
    404             mActionMenuPresenter = new ActionMenuPresenter(mContext);
    405             mActionMenuPresenter.setCallback(cb);
    406             mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter);
    407             mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
    408         }
    409 
    410         ActionMenuView menuView;
    411         final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
    412                 LayoutParams.MATCH_PARENT);
    413         if (!mSplitActionBar) {
    414             mActionMenuPresenter.setExpandedActionViewsExclusive(
    415                     getResources().getBoolean(
    416                     com.android.internal.R.bool.action_bar_expanded_action_views_exclusive));
    417             configPresenters(builder);
    418             menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
    419             final ViewGroup oldParent = (ViewGroup) menuView.getParent();
    420             if (oldParent != null && oldParent != this) {
    421                 oldParent.removeView(menuView);
    422             }
    423             addView(menuView, layoutParams);
    424         } else {
    425             mActionMenuPresenter.setExpandedActionViewsExclusive(false);
    426             // Allow full screen width in split mode.
    427             mActionMenuPresenter.setWidthLimit(
    428                     getContext().getResources().getDisplayMetrics().widthPixels, true);
    429             // No limit to the item count; use whatever will fit.
    430             mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
    431             // Span the whole width
    432             layoutParams.width = LayoutParams.MATCH_PARENT;
    433             configPresenters(builder);
    434             menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
    435             if (mSplitView != null) {
    436                 final ViewGroup oldParent = (ViewGroup) menuView.getParent();
    437                 if (oldParent != null && oldParent != mSplitView) {
    438                     oldParent.removeView(menuView);
    439                 }
    440                 menuView.setVisibility(getAnimatedVisibility());
    441                 mSplitView.addView(menuView, layoutParams);
    442             } else {
    443                 // We'll add this later if we missed it this time.
    444                 menuView.setLayoutParams(layoutParams);
    445             }
    446         }
    447         mMenuView = menuView;
    448     }
    449 
    450     private void configPresenters(MenuBuilder builder) {
    451         if (builder != null) {
    452             builder.addMenuPresenter(mActionMenuPresenter);
    453             builder.addMenuPresenter(mExpandedMenuPresenter);
    454         } else {
    455             mActionMenuPresenter.initForMenu(mContext, null);
    456             mExpandedMenuPresenter.initForMenu(mContext, null);
    457             mActionMenuPresenter.updateMenuView(true);
    458             mExpandedMenuPresenter.updateMenuView(true);
    459         }
    460     }
    461 
    462     public boolean hasExpandedActionView() {
    463         return mExpandedMenuPresenter != null &&
    464                 mExpandedMenuPresenter.mCurrentExpandedItem != null;
    465     }
    466 
    467     public void collapseActionView() {
    468         final MenuItemImpl item = mExpandedMenuPresenter == null ? null :
    469                 mExpandedMenuPresenter.mCurrentExpandedItem;
    470         if (item != null) {
    471             item.collapseActionView();
    472         }
    473     }
    474 
    475     public void setCustomNavigationView(View view) {
    476         final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0;
    477         if (showCustom) {
    478             ActionBarTransition.beginDelayedTransition(this);
    479         }
    480         if (mCustomNavView != null && showCustom) {
    481             removeView(mCustomNavView);
    482         }
    483         mCustomNavView = view;
    484         if (mCustomNavView != null && showCustom) {
    485             addView(mCustomNavView);
    486         }
    487     }
    488 
    489     public CharSequence getTitle() {
    490         return mTitle;
    491     }
    492 
    493     /**
    494      * Set the action bar title. This will always replace or override window titles.
    495      * @param title Title to set
    496      *
    497      * @see #setWindowTitle(CharSequence)
    498      */
    499     public void setTitle(CharSequence title) {
    500         mUserTitle = true;
    501         setTitleImpl(title);
    502     }
    503 
    504     /**
    505      * Set the window title. A window title will always be replaced or overridden by a user title.
    506      * @param title Title to set
    507      *
    508      * @see #setTitle(CharSequence)
    509      */
    510     public void setWindowTitle(CharSequence title) {
    511         if (!mUserTitle) {
    512             setTitleImpl(title);
    513         }
    514     }
    515 
    516     private void setTitleImpl(CharSequence title) {
    517         ActionBarTransition.beginDelayedTransition(this);
    518         mTitle = title;
    519         if (mTitleView != null) {
    520             mTitleView.setText(title);
    521             final boolean visible = mExpandedActionView == null &&
    522                     (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 &&
    523                     (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle));
    524             mTitleLayout.setVisibility(visible ? VISIBLE : GONE);
    525         }
    526         if (mLogoNavItem != null) {
    527             mLogoNavItem.setTitle(title);
    528         }
    529         mUpGoerFive.setContentDescription(buildHomeContentDescription());
    530     }
    531 
    532     public CharSequence getSubtitle() {
    533         return mSubtitle;
    534     }
    535 
    536     public void setSubtitle(CharSequence subtitle) {
    537         ActionBarTransition.beginDelayedTransition(this);
    538         mSubtitle = subtitle;
    539         if (mSubtitleView != null) {
    540             mSubtitleView.setText(subtitle);
    541             mSubtitleView.setVisibility(subtitle != null ? VISIBLE : GONE);
    542             final boolean visible = mExpandedActionView == null &&
    543                     (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 &&
    544                     (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle));
    545             mTitleLayout.setVisibility(visible ? VISIBLE : GONE);
    546         }
    547         mUpGoerFive.setContentDescription(buildHomeContentDescription());
    548     }
    549 
    550     public void setHomeButtonEnabled(boolean enable) {
    551         setHomeButtonEnabled(enable, true);
    552     }
    553 
    554     private void setHomeButtonEnabled(boolean enable, boolean recordState) {
    555         if (recordState) {
    556             mWasHomeEnabled = enable;
    557         }
    558 
    559         if (mExpandedActionView != null) {
    560             // There's an action view currently showing and we want to keep the state
    561             // configured for the action view at the moment. If we needed to record the
    562             // new state for later we will have done so above.
    563             return;
    564         }
    565 
    566         mUpGoerFive.setEnabled(enable);
    567         mUpGoerFive.setFocusable(enable);
    568         // Make sure the home button has an accurate content description for accessibility.
    569         if (!enable) {
    570             mUpGoerFive.setContentDescription(null);
    571             mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
    572         } else {
    573             mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO);
    574             mUpGoerFive.setContentDescription(buildHomeContentDescription());
    575         }
    576     }
    577 
    578     /**
    579      * Compose a content description for the Home/Up affordance.
    580      *
    581      * <p>As this encompasses the icon/logo, title and subtitle all in one, we need
    582      * a description for the whole wad of stuff that can be localized properly.</p>
    583      */
    584     private CharSequence buildHomeContentDescription() {
    585         final CharSequence homeDesc;
    586         if (mHomeDescription != null) {
    587             homeDesc = mHomeDescription;
    588         } else {
    589             if ((mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
    590                 homeDesc = mContext.getResources().getText(R.string.action_bar_up_description);
    591             } else {
    592                 homeDesc = mContext.getResources().getText(R.string.action_bar_home_description);
    593             }
    594         }
    595 
    596         final CharSequence title = getTitle();
    597         final CharSequence subtitle = getSubtitle();
    598         if (!TextUtils.isEmpty(title)) {
    599             final String result;
    600             if (!TextUtils.isEmpty(subtitle)) {
    601                 result = getResources().getString(
    602                         R.string.action_bar_home_subtitle_description_format,
    603                         title, subtitle, homeDesc);
    604             } else {
    605                 result = getResources().getString(R.string.action_bar_home_description_format,
    606                         title, homeDesc);
    607             }
    608             return result;
    609         }
    610         return homeDesc;
    611     }
    612 
    613     public void setDisplayOptions(int options) {
    614         final int flagsChanged = mDisplayOptions == -1 ? -1 : options ^ mDisplayOptions;
    615         mDisplayOptions = options;
    616 
    617         if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) {
    618             ActionBarTransition.beginDelayedTransition(this);
    619 
    620             if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
    621                 final boolean setUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0;
    622                 mHomeLayout.setShowUp(setUp);
    623 
    624                 // Showing home as up implicitly enables interaction with it.
    625                 // In honeycomb it was always enabled, so make this transition
    626                 // a bit easier for developers in the common case.
    627                 // (It would be silly to show it as up without responding to it.)
    628                 if (setUp) {
    629                     setHomeButtonEnabled(true);
    630                 }
    631             }
    632 
    633             if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) {
    634                 final boolean logoVis = mLogo != null && (options & ActionBar.DISPLAY_USE_LOGO) != 0;
    635                 mHomeLayout.setIcon(logoVis ? mLogo : mIcon);
    636             }
    637 
    638             if ((flagsChanged & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
    639                 if ((options & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
    640                     initTitle();
    641                 } else {
    642                     mUpGoerFive.removeView(mTitleLayout);
    643                 }
    644             }
    645 
    646             final boolean showHome = (options & ActionBar.DISPLAY_SHOW_HOME) != 0;
    647             final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0;
    648             final boolean titleUp = !showHome && homeAsUp;
    649             mHomeLayout.setShowIcon(showHome);
    650 
    651             final int homeVis = (showHome || titleUp) && mExpandedActionView == null ?
    652                     VISIBLE : GONE;
    653             mHomeLayout.setVisibility(homeVis);
    654 
    655             if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) {
    656                 if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
    657                     addView(mCustomNavView);
    658                 } else {
    659                     removeView(mCustomNavView);
    660                 }
    661             }
    662 
    663             if (mTitleLayout != null &&
    664                     (flagsChanged & ActionBar.DISPLAY_TITLE_MULTIPLE_LINES) != 0) {
    665                 if ((options & ActionBar.DISPLAY_TITLE_MULTIPLE_LINES) != 0) {
    666                     mTitleView.setSingleLine(false);
    667                     mTitleView.setMaxLines(2);
    668                 } else {
    669                     mTitleView.setMaxLines(1);
    670                     mTitleView.setSingleLine(true);
    671                 }
    672             }
    673 
    674             requestLayout();
    675         } else {
    676             invalidate();
    677         }
    678 
    679         // Make sure the home button has an accurate content description for accessibility.
    680         if (!mHomeLayout.isEnabled()) {
    681             mHomeLayout.setContentDescription(null);
    682             mHomeLayout.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
    683         } else {
    684             mHomeLayout.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO);
    685             if ((options & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
    686                 mHomeLayout.setContentDescription(mContext.getResources().getText(
    687                         R.string.action_bar_up_description));
    688             } else {
    689                 mHomeLayout.setContentDescription(mContext.getResources().getText(
    690                         R.string.action_bar_home_description));
    691             }
    692         }
    693     }
    694 
    695     public void setIcon(Drawable icon) {
    696         mIcon = icon;
    697         if (icon != null &&
    698                 ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null)) {
    699             mHomeLayout.setIcon(icon);
    700         }
    701         if (mExpandedActionView != null) {
    702             mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources()));
    703         }
    704     }
    705 
    706     public void setIcon(int resId) {
    707         setIcon(resId != 0 ? mContext.getResources().getDrawable(resId) : null);
    708     }
    709 
    710     public boolean hasIcon() {
    711         return mIcon != null;
    712     }
    713 
    714     public void setLogo(Drawable logo) {
    715         mLogo = logo;
    716         if (logo != null && (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) {
    717             mHomeLayout.setIcon(logo);
    718         }
    719     }
    720 
    721     public void setLogo(int resId) {
    722         setLogo(resId != 0 ? mContext.getResources().getDrawable(resId) : null);
    723     }
    724 
    725     public boolean hasLogo() {
    726         return mLogo != null;
    727     }
    728 
    729     public void setNavigationMode(int mode) {
    730         final int oldMode = mNavigationMode;
    731         if (mode != oldMode) {
    732             ActionBarTransition.beginDelayedTransition(this);
    733             switch (oldMode) {
    734             case ActionBar.NAVIGATION_MODE_LIST:
    735                 if (mListNavLayout != null) {
    736                     removeView(mListNavLayout);
    737                 }
    738                 break;
    739             case ActionBar.NAVIGATION_MODE_TABS:
    740                 if (mTabScrollView != null && mIncludeTabs) {
    741                     removeView(mTabScrollView);
    742                 }
    743             }
    744 
    745             switch (mode) {
    746             case ActionBar.NAVIGATION_MODE_LIST:
    747                 if (mSpinner == null) {
    748                     mSpinner = new Spinner(mContext, null,
    749                             com.android.internal.R.attr.actionDropDownStyle);
    750                     mSpinner.setId(com.android.internal.R.id.action_bar_spinner);
    751                     mListNavLayout = new LinearLayout(mContext, null,
    752                             com.android.internal.R.attr.actionBarTabBarStyle);
    753                     LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
    754                             LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
    755                     params.gravity = Gravity.CENTER;
    756                     mListNavLayout.addView(mSpinner, params);
    757                 }
    758                 if (mSpinner.getAdapter() != mSpinnerAdapter) {
    759                     mSpinner.setAdapter(mSpinnerAdapter);
    760                 }
    761                 mSpinner.setOnItemSelectedListener(mNavItemSelectedListener);
    762                 addView(mListNavLayout);
    763                 break;
    764             case ActionBar.NAVIGATION_MODE_TABS:
    765                 if (mTabScrollView != null && mIncludeTabs) {
    766                     addView(mTabScrollView);
    767                 }
    768                 break;
    769             }
    770             mNavigationMode = mode;
    771             requestLayout();
    772         }
    773     }
    774 
    775     public void setDropdownAdapter(SpinnerAdapter adapter) {
    776         mSpinnerAdapter = adapter;
    777         if (mSpinner != null) {
    778             mSpinner.setAdapter(adapter);
    779         }
    780     }
    781 
    782     public SpinnerAdapter getDropdownAdapter() {
    783         return mSpinnerAdapter;
    784     }
    785 
    786     public void setDropdownSelectedPosition(int position) {
    787         mSpinner.setSelection(position);
    788     }
    789 
    790     public int getDropdownSelectedPosition() {
    791         return mSpinner.getSelectedItemPosition();
    792     }
    793 
    794     public View getCustomNavigationView() {
    795         return mCustomNavView;
    796     }
    797 
    798     public int getNavigationMode() {
    799         return mNavigationMode;
    800     }
    801 
    802     public int getDisplayOptions() {
    803         return mDisplayOptions;
    804     }
    805 
    806     @Override
    807     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
    808         // Used by custom nav views if they don't supply layout params. Everything else
    809         // added to an ActionBarView should have them already.
    810         return new ActionBar.LayoutParams(DEFAULT_CUSTOM_GRAVITY);
    811     }
    812 
    813     @Override
    814     protected void onFinishInflate() {
    815         super.onFinishInflate();
    816 
    817         mUpGoerFive.addView(mHomeLayout, 0);
    818         addView(mUpGoerFive);
    819 
    820         if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
    821             final ViewParent parent = mCustomNavView.getParent();
    822             if (parent != this) {
    823                 if (parent instanceof ViewGroup) {
    824                     ((ViewGroup) parent).removeView(mCustomNavView);
    825                 }
    826                 addView(mCustomNavView);
    827             }
    828         }
    829     }
    830 
    831     private void initTitle() {
    832         if (mTitleLayout == null) {
    833             LayoutInflater inflater = LayoutInflater.from(getContext());
    834             mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item,
    835                     this, false);
    836             mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
    837             mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
    838 
    839             if (mTitleStyleRes != 0) {
    840                 mTitleView.setTextAppearance(mContext, mTitleStyleRes);
    841             }
    842             if (mTitle != null) {
    843                 mTitleView.setText(mTitle);
    844             }
    845 
    846             if (mSubtitleStyleRes != 0) {
    847                 mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
    848             }
    849             if (mSubtitle != null) {
    850                 mSubtitleView.setText(mSubtitle);
    851                 mSubtitleView.setVisibility(VISIBLE);
    852             }
    853         }
    854 
    855         ActionBarTransition.beginDelayedTransition(this);
    856         mUpGoerFive.addView(mTitleLayout);
    857         if (mExpandedActionView != null ||
    858                 (TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mSubtitle))) {
    859             // Don't show while in expanded mode or with empty text
    860             mTitleLayout.setVisibility(GONE);
    861         } else {
    862             mTitleLayout.setVisibility(VISIBLE);
    863         }
    864     }
    865 
    866     public void setContextView(ActionBarContextView view) {
    867         mContextView = view;
    868     }
    869 
    870     public void setCollapsable(boolean collapsable) {
    871         mIsCollapsable = collapsable;
    872     }
    873 
    874     public boolean isCollapsed() {
    875         return mIsCollapsed;
    876     }
    877 
    878     /**
    879      * @return True if any characters in the title were truncated
    880      */
    881     public boolean isTitleTruncated() {
    882         if (mTitleView == null) {
    883             return false;
    884         }
    885 
    886         final Layout titleLayout = mTitleView.getLayout();
    887         if (titleLayout == null) {
    888             return false;
    889         }
    890 
    891         final int lineCount = titleLayout.getLineCount();
    892         for (int i = 0; i < lineCount; i++) {
    893             if (titleLayout.getEllipsisCount(i) > 0) {
    894                 return true;
    895             }
    896         }
    897         return false;
    898     }
    899 
    900     @Override
    901     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    902         final int childCount = getChildCount();
    903         if (mIsCollapsable) {
    904             int visibleChildren = 0;
    905             for (int i = 0; i < childCount; i++) {
    906                 final View child = getChildAt(i);
    907                 if (child.getVisibility() != GONE &&
    908                         !(child == mMenuView && mMenuView.getChildCount() == 0) &&
    909                         child != mUpGoerFive) {
    910                     visibleChildren++;
    911                 }
    912             }
    913 
    914             final int upChildCount = mUpGoerFive.getChildCount();
    915             for (int i = 0; i < upChildCount; i++) {
    916                 final View child = mUpGoerFive.getChildAt(i);
    917                 if (child.getVisibility() != GONE) {
    918                     visibleChildren++;
    919                 }
    920             }
    921 
    922             if (visibleChildren == 0) {
    923                 // No size for an empty action bar when collapsable.
    924                 setMeasuredDimension(0, 0);
    925                 mIsCollapsed = true;
    926                 return;
    927             }
    928         }
    929         mIsCollapsed = false;
    930 
    931         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    932         if (widthMode != MeasureSpec.EXACTLY) {
    933             throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
    934                     "with android:layout_width=\"match_parent\" (or fill_parent)");
    935         }
    936 
    937         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    938         if (heightMode != MeasureSpec.AT_MOST) {
    939             throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
    940                     "with android:layout_height=\"wrap_content\"");
    941         }
    942 
    943         int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
    944 
    945         int maxHeight = mContentHeight >= 0 ?
    946                 mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
    947 
    948         final int verticalPadding = getPaddingTop() + getPaddingBottom();
    949         final int paddingLeft = getPaddingLeft();
    950         final int paddingRight = getPaddingRight();
    951         final int height = maxHeight - verticalPadding;
    952         final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
    953         final int exactHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    954 
    955         int availableWidth = contentWidth - paddingLeft - paddingRight;
    956         int leftOfCenter = availableWidth / 2;
    957         int rightOfCenter = leftOfCenter;
    958 
    959         final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE &&
    960                 (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
    961 
    962         HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout;
    963 
    964         final ViewGroup.LayoutParams homeLp = homeLayout.getLayoutParams();
    965         int homeWidthSpec;
    966         if (homeLp.width < 0) {
    967             homeWidthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
    968         } else {
    969             homeWidthSpec = MeasureSpec.makeMeasureSpec(homeLp.width, MeasureSpec.EXACTLY);
    970         }
    971 
    972         /*
    973          * This is a little weird.
    974          * We're only measuring the *home* affordance within the Up container here
    975          * on purpose, because we want to give the available space to all other views before
    976          * the title text. We'll remeasure the whole up container again later.
    977          * We need to measure this container so we know the right offset for the up affordance
    978          * no matter what.
    979          */
    980         homeLayout.measure(homeWidthSpec, exactHeightSpec);
    981 
    982         int homeWidth = 0;
    983         if ((homeLayout.getVisibility() != GONE && homeLayout.getParent() == mUpGoerFive)
    984                 || showTitle) {
    985             homeWidth = homeLayout.getMeasuredWidth();
    986             final int homeOffsetWidth = homeWidth + homeLayout.getStartOffset();
    987             availableWidth = Math.max(0, availableWidth - homeOffsetWidth);
    988             leftOfCenter = Math.max(0, availableWidth - homeOffsetWidth);
    989         }
    990 
    991         if (mMenuView != null && mMenuView.getParent() == this) {
    992             availableWidth = measureChildView(mMenuView, availableWidth, exactHeightSpec, 0);
    993             rightOfCenter = Math.max(0, rightOfCenter - mMenuView.getMeasuredWidth());
    994         }
    995 
    996         if (mIndeterminateProgressView != null &&
    997                 mIndeterminateProgressView.getVisibility() != GONE) {
    998             availableWidth = measureChildView(mIndeterminateProgressView, availableWidth,
    999                     childSpecHeight, 0);
   1000             rightOfCenter = Math.max(0,
   1001                     rightOfCenter - mIndeterminateProgressView.getMeasuredWidth());
   1002         }
   1003 
   1004         if (mExpandedActionView == null) {
   1005             switch (mNavigationMode) {
   1006                 case ActionBar.NAVIGATION_MODE_LIST:
   1007                     if (mListNavLayout != null) {
   1008                         final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding;
   1009                         availableWidth = Math.max(0, availableWidth - itemPaddingSize);
   1010                         leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize);
   1011                         mListNavLayout.measure(
   1012                                 MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
   1013                                 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
   1014                         final int listNavWidth = mListNavLayout.getMeasuredWidth();
   1015                         availableWidth = Math.max(0, availableWidth - listNavWidth);
   1016                         leftOfCenter = Math.max(0, leftOfCenter - listNavWidth);
   1017                     }
   1018                     break;
   1019                 case ActionBar.NAVIGATION_MODE_TABS:
   1020                     if (mTabScrollView != null) {
   1021                         final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding;
   1022                         availableWidth = Math.max(0, availableWidth - itemPaddingSize);
   1023                         leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize);
   1024                         mTabScrollView.measure(
   1025                                 MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
   1026                                 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
   1027                         final int tabWidth = mTabScrollView.getMeasuredWidth();
   1028                         availableWidth = Math.max(0, availableWidth - tabWidth);
   1029                         leftOfCenter = Math.max(0, leftOfCenter - tabWidth);
   1030                     }
   1031                     break;
   1032             }
   1033         }
   1034 
   1035         View customView = null;
   1036         if (mExpandedActionView != null) {
   1037             customView = mExpandedActionView;
   1038         } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 &&
   1039                 mCustomNavView != null) {
   1040             customView = mCustomNavView;
   1041         }
   1042 
   1043         if (customView != null) {
   1044             final ViewGroup.LayoutParams lp = generateLayoutParams(customView.getLayoutParams());
   1045             final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
   1046                     (ActionBar.LayoutParams) lp : null;
   1047 
   1048             int horizontalMargin = 0;
   1049             int verticalMargin = 0;
   1050             if (ablp != null) {
   1051                 horizontalMargin = ablp.leftMargin + ablp.rightMargin;
   1052                 verticalMargin = ablp.topMargin + ablp.bottomMargin;
   1053             }
   1054 
   1055             // If the action bar is wrapping to its content height, don't allow a custom
   1056             // view to MATCH_PARENT.
   1057             int customNavHeightMode;
   1058             if (mContentHeight <= 0) {
   1059                 customNavHeightMode = MeasureSpec.AT_MOST;
   1060             } else {
   1061                 customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
   1062                         MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
   1063             }
   1064             final int customNavHeight = Math.max(0,
   1065                     (lp.height >= 0 ? Math.min(lp.height, height) : height) - verticalMargin);
   1066 
   1067             final int customNavWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
   1068                     MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
   1069             int customNavWidth = Math.max(0,
   1070                     (lp.width >= 0 ? Math.min(lp.width, availableWidth) : availableWidth)
   1071                     - horizontalMargin);
   1072             final int hgrav = (ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY) &
   1073                     Gravity.HORIZONTAL_GRAVITY_MASK;
   1074 
   1075             // Centering a custom view is treated specially; we try to center within the whole
   1076             // action bar rather than in the available space.
   1077             if (hgrav == Gravity.CENTER_HORIZONTAL && lp.width == LayoutParams.MATCH_PARENT) {
   1078                 customNavWidth = Math.min(leftOfCenter, rightOfCenter) * 2;
   1079             }
   1080 
   1081             customView.measure(
   1082                     MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode),
   1083                     MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode));
   1084             availableWidth -= horizontalMargin + customView.getMeasuredWidth();
   1085         }
   1086 
   1087         /*
   1088          * Measure the whole up container now, allowing for the full home+title sections.
   1089          * (This will re-measure the home view.)
   1090          */
   1091         availableWidth = measureChildView(mUpGoerFive, availableWidth + homeWidth,
   1092                 MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY), 0);
   1093         if (mTitleLayout != null) {
   1094             leftOfCenter = Math.max(0, leftOfCenter - mTitleLayout.getMeasuredWidth());
   1095         }
   1096 
   1097         if (mContentHeight <= 0) {
   1098             int measuredHeight = 0;
   1099             for (int i = 0; i < childCount; i++) {
   1100                 View v = getChildAt(i);
   1101                 int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
   1102                 if (paddedViewHeight > measuredHeight) {
   1103                     measuredHeight = paddedViewHeight;
   1104                 }
   1105             }
   1106             setMeasuredDimension(contentWidth, measuredHeight);
   1107         } else {
   1108             setMeasuredDimension(contentWidth, maxHeight);
   1109         }
   1110 
   1111         if (mContextView != null) {
   1112             mContextView.setContentHeight(getMeasuredHeight());
   1113         }
   1114 
   1115         if (mProgressView != null && mProgressView.getVisibility() != GONE) {
   1116             mProgressView.measure(MeasureSpec.makeMeasureSpec(
   1117                     contentWidth - mProgressBarPadding * 2, MeasureSpec.EXACTLY),
   1118                     MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST));
   1119         }
   1120     }
   1121 
   1122     @Override
   1123     protected void onLayout(boolean changed, int l, int t, int r, int b) {
   1124         final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
   1125 
   1126         if (contentHeight <= 0) {
   1127             // Nothing to do if we can't see anything.
   1128             return;
   1129         }
   1130 
   1131         final boolean isLayoutRtl = isLayoutRtl();
   1132         final int direction = isLayoutRtl ? 1 : -1;
   1133         int menuStart = isLayoutRtl ? getPaddingLeft() : r - l - getPaddingRight();
   1134         // In LTR mode, we start from left padding and go to the right; in RTL mode, we start
   1135         // from the padding right and go to the left (in reverse way)
   1136         int x = isLayoutRtl ? r - l - getPaddingRight() : getPaddingLeft();
   1137         final int y = getPaddingTop();
   1138 
   1139         HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout;
   1140         final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE &&
   1141                 (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
   1142         int startOffset = 0;
   1143         if (homeLayout.getParent() == mUpGoerFive) {
   1144             if (homeLayout.getVisibility() != GONE) {
   1145                 startOffset = homeLayout.getStartOffset();
   1146             } else if (showTitle) {
   1147                 startOffset = homeLayout.getUpWidth();
   1148             }
   1149         }
   1150 
   1151         // Position the up container based on where the edge of the home layout should go.
   1152         x += positionChild(mUpGoerFive,
   1153                 next(x, startOffset, isLayoutRtl), y, contentHeight, isLayoutRtl);
   1154         x = next(x, startOffset, isLayoutRtl);
   1155 
   1156         if (mExpandedActionView == null) {
   1157             switch (mNavigationMode) {
   1158                 case ActionBar.NAVIGATION_MODE_STANDARD:
   1159                     break;
   1160                 case ActionBar.NAVIGATION_MODE_LIST:
   1161                     if (mListNavLayout != null) {
   1162                         if (showTitle) {
   1163                             x = next(x, mItemPadding, isLayoutRtl);
   1164                         }
   1165                         x += positionChild(mListNavLayout, x, y, contentHeight, isLayoutRtl);
   1166                         x = next(x, mItemPadding, isLayoutRtl);
   1167                     }
   1168                     break;
   1169                 case ActionBar.NAVIGATION_MODE_TABS:
   1170                     if (mTabScrollView != null) {
   1171                         if (showTitle) x = next(x, mItemPadding, isLayoutRtl);
   1172                         x += positionChild(mTabScrollView, x, y, contentHeight, isLayoutRtl);
   1173                         x = next(x, mItemPadding, isLayoutRtl);
   1174                     }
   1175                     break;
   1176             }
   1177         }
   1178 
   1179         if (mMenuView != null && mMenuView.getParent() == this) {
   1180             positionChild(mMenuView, menuStart, y, contentHeight, !isLayoutRtl);
   1181             menuStart += direction * mMenuView.getMeasuredWidth();
   1182         }
   1183 
   1184         if (mIndeterminateProgressView != null &&
   1185                 mIndeterminateProgressView.getVisibility() != GONE) {
   1186             positionChild(mIndeterminateProgressView, menuStart, y, contentHeight, !isLayoutRtl);
   1187             menuStart += direction * mIndeterminateProgressView.getMeasuredWidth();
   1188         }
   1189 
   1190         View customView = null;
   1191         if (mExpandedActionView != null) {
   1192             customView = mExpandedActionView;
   1193         } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 &&
   1194                 mCustomNavView != null) {
   1195             customView = mCustomNavView;
   1196         }
   1197         if (customView != null) {
   1198             final int layoutDirection = getLayoutDirection();
   1199             ViewGroup.LayoutParams lp = customView.getLayoutParams();
   1200             final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
   1201                     (ActionBar.LayoutParams) lp : null;
   1202             final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY;
   1203             final int navWidth = customView.getMeasuredWidth();
   1204 
   1205             int topMargin = 0;
   1206             int bottomMargin = 0;
   1207             if (ablp != null) {
   1208                 x = next(x, ablp.getMarginStart(), isLayoutRtl);
   1209                 menuStart += direction * ablp.getMarginEnd();
   1210                 topMargin = ablp.topMargin;
   1211                 bottomMargin = ablp.bottomMargin;
   1212             }
   1213 
   1214             int hgravity = gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
   1215             // See if we actually have room to truly center; if not push against left or right.
   1216             if (hgravity == Gravity.CENTER_HORIZONTAL) {
   1217                 final int centeredLeft = ((mRight - mLeft) - navWidth) / 2;
   1218                 if (isLayoutRtl) {
   1219                     final int centeredStart = centeredLeft + navWidth;
   1220                     final int centeredEnd = centeredLeft;
   1221                     if (centeredStart > x) {
   1222                         hgravity = Gravity.RIGHT;
   1223                     } else if (centeredEnd < menuStart) {
   1224                         hgravity = Gravity.LEFT;
   1225                     }
   1226                 } else {
   1227                     final int centeredStart = centeredLeft;
   1228                     final int centeredEnd = centeredLeft + navWidth;
   1229                     if (centeredStart < x) {
   1230                         hgravity = Gravity.LEFT;
   1231                     } else if (centeredEnd > menuStart) {
   1232                         hgravity = Gravity.RIGHT;
   1233                     }
   1234                 }
   1235             } else if (gravity == Gravity.NO_GRAVITY) {
   1236                 hgravity = Gravity.START;
   1237             }
   1238 
   1239             int xpos = 0;
   1240             switch (Gravity.getAbsoluteGravity(hgravity, layoutDirection)) {
   1241                 case Gravity.CENTER_HORIZONTAL:
   1242                     xpos = ((mRight - mLeft) - navWidth) / 2;
   1243                     break;
   1244                 case Gravity.LEFT:
   1245                     xpos = isLayoutRtl ? menuStart : x;
   1246                     break;
   1247                 case Gravity.RIGHT:
   1248                     xpos = isLayoutRtl ? x - navWidth : menuStart - navWidth;
   1249                     break;
   1250             }
   1251 
   1252             int vgravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
   1253 
   1254             if (gravity == Gravity.NO_GRAVITY) {
   1255                 vgravity = Gravity.CENTER_VERTICAL;
   1256             }
   1257 
   1258             int ypos = 0;
   1259             switch (vgravity) {
   1260                 case Gravity.CENTER_VERTICAL:
   1261                     final int paddedTop = getPaddingTop();
   1262                     final int paddedBottom = mBottom - mTop - getPaddingBottom();
   1263                     ypos = ((paddedBottom - paddedTop) - customView.getMeasuredHeight()) / 2;
   1264                     break;
   1265                 case Gravity.TOP:
   1266                     ypos = getPaddingTop() + topMargin;
   1267                     break;
   1268                 case Gravity.BOTTOM:
   1269                     ypos = getHeight() - getPaddingBottom() - customView.getMeasuredHeight()
   1270                             - bottomMargin;
   1271                     break;
   1272             }
   1273             final int customWidth = customView.getMeasuredWidth();
   1274             customView.layout(xpos, ypos, xpos + customWidth,
   1275                     ypos + customView.getMeasuredHeight());
   1276             x = next(x, customWidth, isLayoutRtl);
   1277         }
   1278 
   1279         if (mProgressView != null) {
   1280             mProgressView.bringToFront();
   1281             final int halfProgressHeight = mProgressView.getMeasuredHeight() / 2;
   1282             mProgressView.layout(mProgressBarPadding, -halfProgressHeight,
   1283                     mProgressBarPadding + mProgressView.getMeasuredWidth(), halfProgressHeight);
   1284         }
   1285     }
   1286 
   1287     @Override
   1288     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
   1289         return new ActionBar.LayoutParams(getContext(), attrs);
   1290     }
   1291 
   1292     @Override
   1293     public ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
   1294         if (lp == null) {
   1295             lp = generateDefaultLayoutParams();
   1296         }
   1297         return lp;
   1298     }
   1299 
   1300     @Override
   1301     public Parcelable onSaveInstanceState() {
   1302         Parcelable superState = super.onSaveInstanceState();
   1303         SavedState state = new SavedState(superState);
   1304 
   1305         if (mExpandedMenuPresenter != null && mExpandedMenuPresenter.mCurrentExpandedItem != null) {
   1306             state.expandedMenuItemId = mExpandedMenuPresenter.mCurrentExpandedItem.getItemId();
   1307         }
   1308 
   1309         state.isOverflowOpen = isOverflowMenuShowing();
   1310 
   1311         return state;
   1312     }
   1313 
   1314     @Override
   1315     public void onRestoreInstanceState(Parcelable p) {
   1316         SavedState state = (SavedState) p;
   1317 
   1318         super.onRestoreInstanceState(state.getSuperState());
   1319 
   1320         if (state.expandedMenuItemId != 0 &&
   1321                 mExpandedMenuPresenter != null && mOptionsMenu != null) {
   1322             final MenuItem item = mOptionsMenu.findItem(state.expandedMenuItemId);
   1323             if (item != null) {
   1324                 item.expandActionView();
   1325             }
   1326         }
   1327 
   1328         if (state.isOverflowOpen) {
   1329             postShowOverflowMenu();
   1330         }
   1331     }
   1332 
   1333     public void setHomeAsUpIndicator(Drawable indicator) {
   1334         mHomeLayout.setUpIndicator(indicator);
   1335     }
   1336 
   1337     public void setHomeAsUpIndicator(int resId) {
   1338         mHomeLayout.setUpIndicator(resId);
   1339     }
   1340 
   1341     public void setHomeActionContentDescription(CharSequence description) {
   1342         mHomeDescription = description;
   1343     }
   1344 
   1345     public void setHomeActionContentDescription(int resId) {
   1346         mHomeDescriptionRes = resId;
   1347         mHomeDescription = resId != 0 ? getResources().getText(resId) : null;
   1348     }
   1349 
   1350     static class SavedState extends BaseSavedState {
   1351         int expandedMenuItemId;
   1352         boolean isOverflowOpen;
   1353 
   1354         SavedState(Parcelable superState) {
   1355             super(superState);
   1356         }
   1357 
   1358         private SavedState(Parcel in) {
   1359             super(in);
   1360             expandedMenuItemId = in.readInt();
   1361             isOverflowOpen = in.readInt() != 0;
   1362         }
   1363 
   1364         @Override
   1365         public void writeToParcel(Parcel out, int flags) {
   1366             super.writeToParcel(out, flags);
   1367             out.writeInt(expandedMenuItemId);
   1368             out.writeInt(isOverflowOpen ? 1 : 0);
   1369         }
   1370 
   1371         public static final Parcelable.Creator<SavedState> CREATOR =
   1372                 new Parcelable.Creator<SavedState>() {
   1373             public SavedState createFromParcel(Parcel in) {
   1374                 return new SavedState(in);
   1375             }
   1376 
   1377             public SavedState[] newArray(int size) {
   1378                 return new SavedState[size];
   1379             }
   1380         };
   1381     }
   1382 
   1383     private static class HomeView extends FrameLayout {
   1384         private ImageView mUpView;
   1385         private ImageView mIconView;
   1386         private int mUpWidth;
   1387         private int mStartOffset;
   1388         private int mUpIndicatorRes;
   1389         private Drawable mDefaultUpIndicator;
   1390 
   1391         private static final long DEFAULT_TRANSITION_DURATION = 150;
   1392 
   1393         public HomeView(Context context) {
   1394             this(context, null);
   1395         }
   1396 
   1397         public HomeView(Context context, AttributeSet attrs) {
   1398             super(context, attrs);
   1399             LayoutTransition t = getLayoutTransition();
   1400             if (t != null) {
   1401                 // Set a lower duration than the default
   1402                 t.setDuration(DEFAULT_TRANSITION_DURATION);
   1403             }
   1404         }
   1405 
   1406         public void setShowUp(boolean isUp) {
   1407             mUpView.setVisibility(isUp ? VISIBLE : GONE);
   1408         }
   1409 
   1410         public void setShowIcon(boolean showIcon) {
   1411             mIconView.setVisibility(showIcon ? VISIBLE : GONE);
   1412         }
   1413 
   1414         public void setIcon(Drawable icon) {
   1415             mIconView.setImageDrawable(icon);
   1416         }
   1417 
   1418         public void setUpIndicator(Drawable d) {
   1419             mUpView.setImageDrawable(d != null ? d : mDefaultUpIndicator);
   1420             mUpIndicatorRes = 0;
   1421         }
   1422 
   1423         public void setUpIndicator(int resId) {
   1424             mUpIndicatorRes = resId;
   1425             mUpView.setImageDrawable(resId != 0 ? getResources().getDrawable(resId) : null);
   1426         }
   1427 
   1428         @Override
   1429         protected void onConfigurationChanged(Configuration newConfig) {
   1430             super.onConfigurationChanged(newConfig);
   1431             if (mUpIndicatorRes != 0) {
   1432                 // Reload for config change
   1433                 setUpIndicator(mUpIndicatorRes);
   1434             }
   1435         }
   1436 
   1437         @Override
   1438         public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
   1439             onPopulateAccessibilityEvent(event);
   1440             return true;
   1441         }
   1442 
   1443         @Override
   1444         public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
   1445             super.onPopulateAccessibilityEvent(event);
   1446             final CharSequence cdesc = getContentDescription();
   1447             if (!TextUtils.isEmpty(cdesc)) {
   1448                 event.getText().add(cdesc);
   1449             }
   1450         }
   1451 
   1452         @Override
   1453         public boolean dispatchHoverEvent(MotionEvent event) {
   1454             // Don't allow children to hover; we want this to be treated as a single component.
   1455             return onHoverEvent(event);
   1456         }
   1457 
   1458         @Override
   1459         protected void onFinishInflate() {
   1460             mUpView = (ImageView) findViewById(com.android.internal.R.id.up);
   1461             mIconView = (ImageView) findViewById(com.android.internal.R.id.home);
   1462             mDefaultUpIndicator = mUpView.getDrawable();
   1463         }
   1464 
   1465         public int getStartOffset() {
   1466             return mUpView.getVisibility() == GONE ? mStartOffset : 0;
   1467         }
   1468 
   1469         public int getUpWidth() {
   1470             return mUpWidth;
   1471         }
   1472 
   1473         @Override
   1474         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   1475             measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0);
   1476             final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
   1477             final int upMargins = upLp.leftMargin + upLp.rightMargin;
   1478             mUpWidth = mUpView.getMeasuredWidth();
   1479             mStartOffset = mUpWidth + upMargins;
   1480             int width = mUpView.getVisibility() == GONE ? 0 : mStartOffset;
   1481             int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin;
   1482 
   1483             if (mIconView.getVisibility() != GONE) {
   1484                 measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0);
   1485                 final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
   1486                 width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin;
   1487                 height = Math.max(height,
   1488                         iconLp.topMargin + mIconView.getMeasuredHeight() + iconLp.bottomMargin);
   1489             } else if (upMargins < 0) {
   1490                 // Remove the measurement effects of negative margins used for offsets
   1491                 width -= upMargins;
   1492             }
   1493 
   1494             final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
   1495             final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
   1496             final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
   1497             final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
   1498 
   1499             switch (widthMode) {
   1500                 case MeasureSpec.AT_MOST:
   1501                     width = Math.min(width, widthSize);
   1502                     break;
   1503                 case MeasureSpec.EXACTLY:
   1504                     width = widthSize;
   1505                     break;
   1506                 case MeasureSpec.UNSPECIFIED:
   1507                 default:
   1508                     break;
   1509             }
   1510             switch (heightMode) {
   1511                 case MeasureSpec.AT_MOST:
   1512                     height = Math.min(height, heightSize);
   1513                     break;
   1514                 case MeasureSpec.EXACTLY:
   1515                     height = heightSize;
   1516                     break;
   1517                 case MeasureSpec.UNSPECIFIED:
   1518                 default:
   1519                     break;
   1520             }
   1521             setMeasuredDimension(width, height);
   1522         }
   1523 
   1524         @Override
   1525         protected void onLayout(boolean changed, int l, int t, int r, int b) {
   1526             final int vCenter = (b - t) / 2;
   1527             final boolean isLayoutRtl = isLayoutRtl();
   1528             final int width = getWidth();
   1529             int upOffset = 0;
   1530             if (mUpView.getVisibility() != GONE) {
   1531                 final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
   1532                 final int upHeight = mUpView.getMeasuredHeight();
   1533                 final int upWidth = mUpView.getMeasuredWidth();
   1534                 upOffset = upLp.leftMargin + upWidth + upLp.rightMargin;
   1535                 final int upTop = vCenter - upHeight / 2;
   1536                 final int upBottom = upTop + upHeight;
   1537                 final int upRight;
   1538                 final int upLeft;
   1539                 if (isLayoutRtl) {
   1540                     upRight = width;
   1541                     upLeft = upRight - upWidth;
   1542                     r -= upOffset;
   1543                 } else {
   1544                     upRight = upWidth;
   1545                     upLeft = 0;
   1546                     l += upOffset;
   1547                 }
   1548                 mUpView.layout(upLeft, upTop, upRight, upBottom);
   1549             }
   1550 
   1551             final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
   1552             final int iconHeight = mIconView.getMeasuredHeight();
   1553             final int iconWidth = mIconView.getMeasuredWidth();
   1554             final int hCenter = (r - l) / 2;
   1555             final int iconTop = Math.max(iconLp.topMargin, vCenter - iconHeight / 2);
   1556             final int iconBottom = iconTop + iconHeight;
   1557             final int iconLeft;
   1558             final int iconRight;
   1559             int marginStart = iconLp.getMarginStart();
   1560             final int delta = Math.max(marginStart, hCenter - iconWidth / 2);
   1561             if (isLayoutRtl) {
   1562                 iconRight = width - upOffset - delta;
   1563                 iconLeft = iconRight - iconWidth;
   1564             } else {
   1565                 iconLeft = upOffset + delta;
   1566                 iconRight = iconLeft + iconWidth;
   1567             }
   1568             mIconView.layout(iconLeft, iconTop, iconRight, iconBottom);
   1569         }
   1570     }
   1571 
   1572     private class ExpandedActionViewMenuPresenter implements MenuPresenter {
   1573         MenuBuilder mMenu;
   1574         MenuItemImpl mCurrentExpandedItem;
   1575 
   1576         @Override
   1577         public void initForMenu(Context context, MenuBuilder menu) {
   1578             // Clear the expanded action view when menus change.
   1579             if (mMenu != null && mCurrentExpandedItem != null) {
   1580                 mMenu.collapseItemActionView(mCurrentExpandedItem);
   1581             }
   1582             mMenu = menu;
   1583         }
   1584 
   1585         @Override
   1586         public MenuView getMenuView(ViewGroup root) {
   1587             return null;
   1588         }
   1589 
   1590         @Override
   1591         public void updateMenuView(boolean cleared) {
   1592             // Make sure the expanded item we have is still there.
   1593             if (mCurrentExpandedItem != null) {
   1594                 boolean found = false;
   1595 
   1596                 if (mMenu != null) {
   1597                     final int count = mMenu.size();
   1598                     for (int i = 0; i < count; i++) {
   1599                         final MenuItem item = mMenu.getItem(i);
   1600                         if (item == mCurrentExpandedItem) {
   1601                             found = true;
   1602                             break;
   1603                         }
   1604                     }
   1605                 }
   1606 
   1607                 if (!found) {
   1608                     // The item we had expanded disappeared. Collapse.
   1609                     collapseItemActionView(mMenu, mCurrentExpandedItem);
   1610                 }
   1611             }
   1612         }
   1613 
   1614         @Override
   1615         public void setCallback(Callback cb) {
   1616         }
   1617 
   1618         @Override
   1619         public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
   1620             return false;
   1621         }
   1622 
   1623         @Override
   1624         public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
   1625         }
   1626 
   1627         @Override
   1628         public boolean flagActionItems() {
   1629             return false;
   1630         }
   1631 
   1632         @Override
   1633         public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
   1634             ActionBarTransition.beginDelayedTransition(ActionBarView.this);
   1635 
   1636             mExpandedActionView = item.getActionView();
   1637             mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources()));
   1638             mCurrentExpandedItem = item;
   1639             if (mExpandedActionView.getParent() != ActionBarView.this) {
   1640                 addView(mExpandedActionView);
   1641             }
   1642             if (mExpandedHomeLayout.getParent() != mUpGoerFive) {
   1643                 mUpGoerFive.addView(mExpandedHomeLayout);
   1644             }
   1645             mHomeLayout.setVisibility(GONE);
   1646             if (mTitleLayout != null) mTitleLayout.setVisibility(GONE);
   1647             if (mTabScrollView != null) mTabScrollView.setVisibility(GONE);
   1648             if (mSpinner != null) mSpinner.setVisibility(GONE);
   1649             if (mCustomNavView != null) mCustomNavView.setVisibility(GONE);
   1650             setHomeButtonEnabled(false, false);
   1651             requestLayout();
   1652             item.setActionViewExpanded(true);
   1653 
   1654             if (mExpandedActionView instanceof CollapsibleActionView) {
   1655                 ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded();
   1656             }
   1657 
   1658             return true;
   1659         }
   1660 
   1661         @Override
   1662         public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
   1663             ActionBarTransition.beginDelayedTransition(ActionBarView.this);
   1664 
   1665             // Do this before detaching the actionview from the hierarchy, in case
   1666             // it needs to dismiss the soft keyboard, etc.
   1667             if (mExpandedActionView instanceof CollapsibleActionView) {
   1668                 ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed();
   1669             }
   1670 
   1671             removeView(mExpandedActionView);
   1672             mUpGoerFive.removeView(mExpandedHomeLayout);
   1673             mExpandedActionView = null;
   1674             if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) {
   1675                 mHomeLayout.setVisibility(VISIBLE);
   1676             }
   1677             if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
   1678                 if (mTitleLayout == null) {
   1679                     initTitle();
   1680                 } else {
   1681                     mTitleLayout.setVisibility(VISIBLE);
   1682                 }
   1683             }
   1684             if (mTabScrollView != null) mTabScrollView.setVisibility(VISIBLE);
   1685             if (mSpinner != null) mSpinner.setVisibility(VISIBLE);
   1686             if (mCustomNavView != null) mCustomNavView.setVisibility(VISIBLE);
   1687 
   1688             mExpandedHomeLayout.setIcon(null);
   1689             mCurrentExpandedItem = null;
   1690             setHomeButtonEnabled(mWasHomeEnabled); // Set by expandItemActionView above
   1691             requestLayout();
   1692             item.setActionViewExpanded(false);
   1693 
   1694             return true;
   1695         }
   1696 
   1697         @Override
   1698         public int getId() {
   1699             return 0;
   1700         }
   1701 
   1702         @Override
   1703         public Parcelable onSaveInstanceState() {
   1704             return null;
   1705         }
   1706 
   1707         @Override
   1708         public void onRestoreInstanceState(Parcelable state) {
   1709         }
   1710     }
   1711 }
   1712