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