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