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