Toolbar.java revision 41fceb462b6eefbaa3a4d4e56b962fdb2910a6f5
1/*
2 * Copyright (C) 2014 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 android.widget;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.app.ActionBar;
22import android.content.Context;
23import android.content.res.TypedArray;
24import android.graphics.RectF;
25import android.graphics.drawable.Drawable;
26import android.os.Parcel;
27import android.os.Parcelable;
28import android.text.Layout;
29import android.text.TextUtils;
30import android.util.AttributeSet;
31import android.view.CollapsibleActionView;
32import android.view.ContextThemeWrapper;
33import android.view.Gravity;
34import android.view.Menu;
35import android.view.MenuInflater;
36import android.view.MenuItem;
37import android.view.MotionEvent;
38import android.view.View;
39import android.view.ViewGroup;
40
41import com.android.internal.R;
42import com.android.internal.view.menu.MenuBuilder;
43import com.android.internal.view.menu.MenuItemImpl;
44import com.android.internal.view.menu.MenuPresenter;
45import com.android.internal.view.menu.MenuView;
46import com.android.internal.view.menu.SubMenuBuilder;
47import com.android.internal.widget.DecorToolbar;
48import com.android.internal.widget.ToolbarWidgetWrapper;
49
50import java.util.ArrayList;
51import java.util.List;
52
53/**
54 * A standard toolbar for use within application content.
55 *
56 * <p>A Toolbar is a generalization of {@link android.app.ActionBar action bars} for use
57 * within application layouts. While an action bar is traditionally part of an
58 * {@link android.app.Activity Activity's} opaque window decor controlled by the framework,
59 * a Toolbar may be placed at any arbitrary level of nesting within a view hierarchy.
60 * An application may choose to designate a Toolbar as the action bar for an Activity
61 * using the {@link android.app.Activity#setActionBar(Toolbar) setActionBar()} method.</p>
62 *
63 * <p>Toolbar supports a more focused feature set than ActionBar. From start to end, a toolbar
64 * may contain a combination of the following optional elements:
65 *
66 * <ul>
67 *     <li><em>A navigation button.</em> This may be an Up arrow, navigation menu toggle, close,
68 *     collapse, done or another glyph of the app's choosing. This button should always be used
69 *     to access other navigational destinations within the container of the Toolbar and
70 *     its signified content or otherwise leave the current context signified by the Toolbar.
71 *     The navigation button is vertically aligned within the Toolbar's
72 *     {@link android.R.styleable#View_minHeight minimum height}, if set.</li>
73 *     <li><em>A branded logo image.</em> This may extend to the height of the bar and can be
74 *     arbitrarily wide.</li>
75 *     <li><em>A title and subtitle.</em> The title should be a signpost for the Toolbar's current
76 *     position in the navigation hierarchy and the content contained there. The subtitle,
77 *     if present should indicate any extended information about the current content.
78 *     If an app uses a logo image it should strongly consider omitting a title and subtitle.</li>
79 *     <li><em>One or more custom views.</em> The application may add arbitrary child views
80 *     to the Toolbar. They will appear at this position within the layout. If a child view's
81 *     {@link LayoutParams} indicates a {@link Gravity} value of
82 *     {@link Gravity#CENTER_HORIZONTAL CENTER_HORIZONTAL} the view will attempt to center
83 *     within the available space remaining in the Toolbar after all other elements have been
84 *     measured.</li>
85 *     <li><em>An {@link ActionMenuView action menu}.</em> The menu of actions will pin to the
86 *     end of the Toolbar offering a few
87 *     <a href="http://developer.android.com/design/patterns/actionbar.html#ActionButtons">
88 *     frequent, important or typical</a> actions along with an optional overflow menu for
89 *     additional actions. Action buttons are vertically aligned within the Toolbar's
90 *     {@link android.R.styleable#View_minHeight minimum height}, if set.</li>
91 * </ul>
92 * </p>
93 *
94 * <p>In modern Android UIs developers should lean more on a visually distinct color scheme for
95 * toolbars than on their application icon. The use of application icon plus title as a standard
96 * layout is discouraged on API 21 devices and newer.</p>
97 */
98public class Toolbar extends ViewGroup {
99    private static final String TAG = "Toolbar";
100
101    private ActionMenuView mMenuView;
102    private TextView mTitleTextView;
103    private TextView mSubtitleTextView;
104    private ImageButton mNavButtonView;
105    private ImageView mLogoView;
106
107    private Drawable mCollapseIcon;
108    private CharSequence mCollapseDescription;
109    private ImageButton mCollapseButtonView;
110    View mExpandedActionView;
111
112    /** Context against which to inflate popup menus. */
113    private Context mPopupContext;
114
115    /** Theme resource against which to inflate popup menus. */
116    private int mPopupTheme;
117
118    private int mTitleTextAppearance;
119    private int mSubtitleTextAppearance;
120    private int mNavButtonStyle;
121
122    private int mButtonGravity;
123
124    private int mMaxButtonHeight;
125
126    private int mTitleMarginStart;
127    private int mTitleMarginEnd;
128    private int mTitleMarginTop;
129    private int mTitleMarginBottom;
130
131    private final RtlSpacingHelper mContentInsets = new RtlSpacingHelper();
132
133    private int mGravity = Gravity.START | Gravity.CENTER_VERTICAL;
134
135    private CharSequence mTitleText;
136    private CharSequence mSubtitleText;
137
138    private int mTitleTextColor;
139    private int mSubtitleTextColor;
140
141    private boolean mEatingTouch;
142
143    // Clear me after use.
144    private final ArrayList<View> mTempViews = new ArrayList<View>();
145
146    private final int[] mTempMargins = new int[2];
147
148    private OnMenuItemClickListener mOnMenuItemClickListener;
149
150    private final ActionMenuView.OnMenuItemClickListener mMenuViewItemClickListener =
151            new ActionMenuView.OnMenuItemClickListener() {
152                @Override
153                public boolean onMenuItemClick(MenuItem item) {
154                    if (mOnMenuItemClickListener != null) {
155                        return mOnMenuItemClickListener.onMenuItemClick(item);
156                    }
157                    return false;
158                }
159            };
160
161    private ToolbarWidgetWrapper mWrapper;
162    private ActionMenuPresenter mOuterActionMenuPresenter;
163    private ExpandedActionViewMenuPresenter mExpandedMenuPresenter;
164    private MenuPresenter.Callback mActionMenuPresenterCallback;
165    private MenuBuilder.Callback mMenuBuilderCallback;
166
167    private boolean mCollapsible;
168
169    private final Runnable mShowOverflowMenuRunnable = new Runnable() {
170        @Override public void run() {
171            showOverflowMenu();
172        }
173    };
174
175    public Toolbar(Context context) {
176        this(context, null);
177    }
178
179    public Toolbar(Context context, AttributeSet attrs) {
180        this(context, attrs, com.android.internal.R.attr.toolbarStyle);
181    }
182
183    public Toolbar(Context context, AttributeSet attrs, int defStyleAttr) {
184        this(context, attrs, defStyleAttr, 0);
185    }
186
187    public Toolbar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
188        super(context, attrs, defStyleAttr, defStyleRes);
189
190        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Toolbar,
191                defStyleAttr, defStyleRes);
192
193        mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0);
194        mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0);
195        mNavButtonStyle = a.getResourceId(R.styleable.Toolbar_navigationButtonStyle, 0);
196        mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity);
197        mButtonGravity = a.getInteger(R.styleable.Toolbar_buttonGravity, Gravity.TOP);
198        mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom =
199                a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, 0);
200
201        final int marginStart = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginStart, -1);
202        if (marginStart >= 0) {
203            mTitleMarginStart = marginStart;
204        }
205
206        final int marginEnd = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginEnd, -1);
207        if (marginEnd >= 0) {
208            mTitleMarginEnd = marginEnd;
209        }
210
211        final int marginTop = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginTop, -1);
212        if (marginTop >= 0) {
213            mTitleMarginTop = marginTop;
214        }
215
216        final int marginBottom = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginBottom,
217                -1);
218        if (marginBottom >= 0) {
219            mTitleMarginBottom = marginBottom;
220        }
221
222        mMaxButtonHeight = a.getDimensionPixelSize(R.styleable.Toolbar_maxButtonHeight, -1);
223
224        final int contentInsetStart =
225                a.getDimensionPixelOffset(R.styleable.Toolbar_contentInsetStart,
226                        RtlSpacingHelper.UNDEFINED);
227        final int contentInsetEnd =
228                a.getDimensionPixelOffset(R.styleable.Toolbar_contentInsetEnd,
229                        RtlSpacingHelper.UNDEFINED);
230        final int contentInsetLeft =
231                a.getDimensionPixelSize(R.styleable.Toolbar_contentInsetLeft, 0);
232        final int contentInsetRight =
233                a.getDimensionPixelSize(R.styleable.Toolbar_contentInsetRight, 0);
234
235        mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight);
236
237        if (contentInsetStart != RtlSpacingHelper.UNDEFINED ||
238                contentInsetEnd != RtlSpacingHelper.UNDEFINED) {
239            mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
240        }
241
242        mCollapseIcon = a.getDrawable(R.styleable.Toolbar_collapseIcon);
243        mCollapseDescription = a.getText(R.styleable.Toolbar_collapseContentDescription);
244
245        final CharSequence title = a.getText(R.styleable.Toolbar_title);
246        if (!TextUtils.isEmpty(title)) {
247            setTitle(title);
248        }
249
250        final CharSequence subtitle = a.getText(R.styleable.Toolbar_subtitle);
251        if (!TextUtils.isEmpty(subtitle)) {
252            setSubtitle(subtitle);
253        }
254
255        // Set the default context, since setPopupTheme() may be a no-op.
256        mPopupContext = mContext;
257        setPopupTheme(a.getResourceId(R.styleable.Toolbar_popupTheme, 0));
258
259        final Drawable navIcon = a.getDrawable(R.styleable.Toolbar_navigationIcon);
260        if (navIcon != null) {
261            setNavigationIcon(navIcon);
262        }
263
264        final CharSequence navDesc = a.getText(
265                R.styleable.Toolbar_navigationContentDescription);
266        if (!TextUtils.isEmpty(navDesc)) {
267            setNavigationContentDescription(navDesc);
268        }
269        a.recycle();
270    }
271
272    /**
273     * Specifies the theme to use when inflating popup menus. By default, uses
274     * the same theme as the toolbar itself.
275     *
276     * @param resId theme used to inflate popup menus
277     * @see #getPopupTheme()
278     */
279    public void setPopupTheme(int resId) {
280        if (mPopupTheme != resId) {
281            mPopupTheme = resId;
282            if (resId == 0) {
283                mPopupContext = mContext;
284            } else {
285                mPopupContext = new ContextThemeWrapper(mContext, resId);
286            }
287        }
288    }
289
290    /**
291     * @return resource identifier of the theme used to inflate popup menus, or
292     *         0 if menus are inflated against the toolbar theme
293     * @see #setPopupTheme(int)
294     */
295    public int getPopupTheme() {
296        return mPopupTheme;
297    }
298
299    @Override
300    public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
301        super.onRtlPropertiesChanged(layoutDirection);
302        mContentInsets.setDirection(layoutDirection == LAYOUT_DIRECTION_RTL);
303    }
304
305    /**
306     * Set a logo drawable from a resource id.
307     *
308     * <p>This drawable should generally take the place of title text. The logo cannot be
309     * clicked. Apps using a logo should also supply a description using
310     * {@link #setLogoDescription(int)}.</p>
311     *
312     * @param resId ID of a drawable resource
313     */
314    public void setLogo(int resId) {
315        setLogo(getContext().getDrawable(resId));
316    }
317
318    /** @hide */
319    public boolean canShowOverflowMenu() {
320        return getVisibility() == VISIBLE && mMenuView != null && mMenuView.isOverflowReserved();
321    }
322
323    /**
324     * Check whether the overflow menu is currently showing. This may not reflect
325     * a pending show operation in progress.
326     *
327     * @return true if the overflow menu is currently showing
328     */
329    public boolean isOverflowMenuShowing() {
330        return mMenuView != null && mMenuView.isOverflowMenuShowing();
331    }
332
333    /** @hide */
334    public boolean isOverflowMenuShowPending() {
335        return mMenuView != null && mMenuView.isOverflowMenuShowPending();
336    }
337
338    /**
339     * Show the overflow items from the associated menu.
340     *
341     * @return true if the menu was able to be shown, false otherwise
342     */
343    public boolean showOverflowMenu() {
344        return mMenuView != null && mMenuView.showOverflowMenu();
345    }
346
347    /**
348     * Hide the overflow items from the associated menu.
349     *
350     * @return true if the menu was able to be hidden, false otherwise
351     */
352    public boolean hideOverflowMenu() {
353        return mMenuView != null && mMenuView.hideOverflowMenu();
354    }
355
356    /** @hide */
357    public void setMenu(MenuBuilder menu, ActionMenuPresenter outerPresenter) {
358        if (menu == null && mMenuView == null) {
359            return;
360        }
361
362        ensureMenuView();
363        final MenuBuilder oldMenu = mMenuView.peekMenu();
364        if (oldMenu == menu) {
365            return;
366        }
367
368        if (oldMenu != null) {
369            oldMenu.removeMenuPresenter(mOuterActionMenuPresenter);
370            oldMenu.removeMenuPresenter(mExpandedMenuPresenter);
371        }
372
373        if (mExpandedMenuPresenter == null) {
374            mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
375        }
376
377        outerPresenter.setExpandedActionViewsExclusive(true);
378        if (menu != null) {
379            menu.addMenuPresenter(outerPresenter, mPopupContext);
380            menu.addMenuPresenter(mExpandedMenuPresenter, mPopupContext);
381        } else {
382            outerPresenter.initForMenu(mPopupContext, null);
383            mExpandedMenuPresenter.initForMenu(mPopupContext, null);
384            outerPresenter.updateMenuView(true);
385            mExpandedMenuPresenter.updateMenuView(true);
386        }
387        mMenuView.setPopupTheme(mPopupTheme);
388        mMenuView.setPresenter(outerPresenter);
389        mOuterActionMenuPresenter = outerPresenter;
390    }
391
392    /**
393     * Dismiss all currently showing popup menus, including overflow or submenus.
394     */
395    public void dismissPopupMenus() {
396        if (mMenuView != null) {
397            mMenuView.dismissPopupMenus();
398        }
399    }
400
401    /** @hide */
402    public boolean isTitleTruncated() {
403        if (mTitleTextView == null) {
404            return false;
405        }
406
407        final Layout titleLayout = mTitleTextView.getLayout();
408        if (titleLayout == null) {
409            return false;
410        }
411
412        final int lineCount = titleLayout.getLineCount();
413        for (int i = 0; i < lineCount; i++) {
414            if (titleLayout.getEllipsisCount(i) > 0) {
415                return true;
416            }
417        }
418        return false;
419    }
420
421    /**
422     * Set a logo drawable.
423     *
424     * <p>This drawable should generally take the place of title text. The logo cannot be
425     * clicked. Apps using a logo should also supply a description using
426     * {@link #setLogoDescription(int)}.</p>
427     *
428     * @param drawable Drawable to use as a logo
429     */
430    public void setLogo(Drawable drawable) {
431        if (drawable != null) {
432            ensureLogoView();
433            if (mLogoView.getParent() == null) {
434                addSystemView(mLogoView);
435                updateChildVisibilityForExpandedActionView(mLogoView);
436            }
437        } else if (mLogoView != null && mLogoView.getParent() != null) {
438            removeView(mLogoView);
439        }
440        if (mLogoView != null) {
441            mLogoView.setImageDrawable(drawable);
442        }
443    }
444
445    /**
446     * Return the current logo drawable.
447     *
448     * @return The current logo drawable
449     * @see #setLogo(int)
450     * @see #setLogo(android.graphics.drawable.Drawable)
451     */
452    public Drawable getLogo() {
453        return mLogoView != null ? mLogoView.getDrawable() : null;
454    }
455
456    /**
457     * Set a description of the toolbar's logo.
458     *
459     * <p>This description will be used for accessibility or other similar descriptions
460     * of the UI.</p>
461     *
462     * @param resId String resource id
463     */
464    public void setLogoDescription(int resId) {
465        setLogoDescription(getContext().getText(resId));
466    }
467
468    /**
469     * Set a description of the toolbar's logo.
470     *
471     * <p>This description will be used for accessibility or other similar descriptions
472     * of the UI.</p>
473     *
474     * @param description Description to set
475     */
476    public void setLogoDescription(CharSequence description) {
477        if (!TextUtils.isEmpty(description)) {
478            ensureLogoView();
479        }
480        if (mLogoView != null) {
481            mLogoView.setContentDescription(description);
482        }
483    }
484
485    /**
486     * Return the description of the toolbar's logo.
487     *
488     * @return A description of the logo
489     */
490    public CharSequence getLogoDescription() {
491        return mLogoView != null ? mLogoView.getContentDescription() : null;
492    }
493
494    private void ensureLogoView() {
495        if (mLogoView == null) {
496            mLogoView = new ImageView(getContext());
497        }
498    }
499
500    /**
501     * Check whether this Toolbar is currently hosting an expanded action view.
502     *
503     * <p>An action view may be expanded either directly from the
504     * {@link android.view.MenuItem MenuItem} it belongs to or by user action. If the Toolbar
505     * has an expanded action view it can be collapsed using the {@link #collapseActionView()}
506     * method.</p>
507     *
508     * @return true if the Toolbar has an expanded action view
509     */
510    public boolean hasExpandedActionView() {
511        return mExpandedMenuPresenter != null &&
512                mExpandedMenuPresenter.mCurrentExpandedItem != null;
513    }
514
515    /**
516     * Collapse a currently expanded action view. If this Toolbar does not have an
517     * expanded action view this method has no effect.
518     *
519     * <p>An action view may be expanded either directly from the
520     * {@link android.view.MenuItem MenuItem} it belongs to or by user action.</p>
521     *
522     * @see #hasExpandedActionView()
523     */
524    public void collapseActionView() {
525        final MenuItemImpl item = mExpandedMenuPresenter == null ? null :
526                mExpandedMenuPresenter.mCurrentExpandedItem;
527        if (item != null) {
528            item.collapseActionView();
529        }
530    }
531
532    /**
533     * Returns the title of this toolbar.
534     *
535     * @return The current title.
536     */
537    public CharSequence getTitle() {
538        return mTitleText;
539    }
540
541    /**
542     * Set the title of this toolbar.
543     *
544     * <p>A title should be used as the anchor for a section of content. It should
545     * describe or name the content being viewed.</p>
546     *
547     * @param resId Resource ID of a string to set as the title
548     */
549    public void setTitle(int resId) {
550        setTitle(getContext().getText(resId));
551    }
552
553    /**
554     * Set the title of this toolbar.
555     *
556     * <p>A title should be used as the anchor for a section of content. It should
557     * describe or name the content being viewed.</p>
558     *
559     * @param title Title to set
560     */
561    public void setTitle(CharSequence title) {
562        if (!TextUtils.isEmpty(title)) {
563            if (mTitleTextView == null) {
564                final Context context = getContext();
565                mTitleTextView = new TextView(context);
566                mTitleTextView.setSingleLine();
567                mTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
568                if (mTitleTextAppearance != 0) {
569                    mTitleTextView.setTextAppearance(context, mTitleTextAppearance);
570                }
571                if (mTitleTextColor != 0) {
572                    mTitleTextView.setTextColor(mTitleTextColor);
573                }
574            }
575            if (mTitleTextView.getParent() == null) {
576                addSystemView(mTitleTextView);
577                updateChildVisibilityForExpandedActionView(mTitleTextView);
578            }
579        } else if (mTitleTextView != null && mTitleTextView.getParent() != null) {
580            removeView(mTitleTextView);
581        }
582        if (mTitleTextView != null) {
583            mTitleTextView.setText(title);
584        }
585        mTitleText = title;
586    }
587
588    /**
589     * Return the subtitle of this toolbar.
590     *
591     * @return The current subtitle
592     */
593    public CharSequence getSubtitle() {
594        return mSubtitleText;
595    }
596
597    /**
598     * Set the subtitle of this toolbar.
599     *
600     * <p>Subtitles should express extended information about the current content.</p>
601     *
602     * @param resId String resource ID
603     */
604    public void setSubtitle(int resId) {
605        setSubtitle(getContext().getText(resId));
606    }
607
608    /**
609     * Set the subtitle of this toolbar.
610     *
611     * <p>Subtitles should express extended information about the current content.</p>
612     *
613     * @param subtitle Subtitle to set
614     */
615    public void setSubtitle(CharSequence subtitle) {
616        if (!TextUtils.isEmpty(subtitle)) {
617            if (mSubtitleTextView == null) {
618                final Context context = getContext();
619                mSubtitleTextView = new TextView(context);
620                mSubtitleTextView.setSingleLine();
621                mSubtitleTextView.setEllipsize(TextUtils.TruncateAt.END);
622                if (mSubtitleTextAppearance != 0) {
623                    mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance);
624                }
625                if (mSubtitleTextColor != 0) {
626                    mSubtitleTextView.setTextColor(mSubtitleTextColor);
627                }
628            }
629            if (mSubtitleTextView.getParent() == null) {
630                addSystemView(mSubtitleTextView);
631                updateChildVisibilityForExpandedActionView(mSubtitleTextView);
632            }
633        } else if (mSubtitleTextView != null && mSubtitleTextView.getParent() != null) {
634            removeView(mSubtitleTextView);
635        }
636        if (mSubtitleTextView != null) {
637            mSubtitleTextView.setText(subtitle);
638        }
639        mSubtitleText = subtitle;
640    }
641
642    /**
643     * Sets the text color, size, style, hint color, and highlight color
644     * from the specified TextAppearance resource.
645     */
646    public void setTitleTextAppearance(Context context, int resId) {
647        mTitleTextAppearance = resId;
648        if (mTitleTextView != null) {
649            mTitleTextView.setTextAppearance(context, resId);
650        }
651    }
652
653    /**
654     * Sets the text color, size, style, hint color, and highlight color
655     * from the specified TextAppearance resource.
656     */
657    public void setSubtitleTextAppearance(Context context, int resId) {
658        mSubtitleTextAppearance = resId;
659        if (mSubtitleTextView != null) {
660            mSubtitleTextView.setTextAppearance(context, resId);
661        }
662    }
663
664    /**
665     * Sets the text color of the title, if present.
666     *
667     * @param color The new text color in 0xAARRGGBB format
668     */
669    public void setTitleTextColor(int color) {
670        mTitleTextColor = color;
671        if (mTitleTextView != null) {
672            mTitleTextView.setTextColor(color);
673        }
674    }
675
676    /**
677     * Sets the text color of the subtitle, if present.
678     *
679     * @param color The new text color in 0xAARRGGBB format
680     */
681    public void setSubtitleTextColor(int color) {
682        mSubtitleTextColor = color;
683        if (mSubtitleTextView != null) {
684            mSubtitleTextView.setTextColor(color);
685        }
686    }
687
688    /**
689     * Retrieve the currently configured content description for the navigation button view.
690     * This will be used to describe the navigation action to users through mechanisms such
691     * as screen readers or tooltips.
692     *
693     * @return The navigation button's content description
694     *
695     * @attr ref android.R.styleable#Toolbar_navigationContentDescription
696     */
697    @Nullable
698    public CharSequence getNavigationContentDescription() {
699        return mNavButtonView != null ? mNavButtonView.getContentDescription() : null;
700    }
701
702    /**
703     * Set a content description for the navigation button if one is present. The content
704     * description will be read via screen readers or other accessibility systems to explain
705     * the action of the navigation button.
706     *
707     * @param resId Resource ID of a content description string to set, or 0 to
708     *              clear the description
709     *
710     * @attr ref android.R.styleable#Toolbar_navigationContentDescription
711     */
712    public void setNavigationContentDescription(int resId) {
713        setNavigationContentDescription(resId != 0 ? getContext().getText(resId) : null);
714    }
715
716    /**
717     * Set a content description for the navigation button if one is present. The content
718     * description will be read via screen readers or other accessibility systems to explain
719     * the action of the navigation button.
720     *
721     * @param description Content description to set, or <code>null</code> to
722     *                    clear the content description
723     *
724     * @attr ref android.R.styleable#Toolbar_navigationContentDescription
725     */
726    public void setNavigationContentDescription(@Nullable CharSequence description) {
727        if (!TextUtils.isEmpty(description)) {
728            ensureNavButtonView();
729        }
730        if (mNavButtonView != null) {
731            mNavButtonView.setContentDescription(description);
732        }
733    }
734
735    /**
736     * Set the icon to use for the toolbar's navigation button.
737     *
738     * <p>The navigation button appears at the start of the toolbar if present. Setting an icon
739     * will make the navigation button visible.</p>
740     *
741     * <p>If you use a navigation icon you should also set a description for its action using
742     * {@link #setNavigationContentDescription(int)}. This is used for accessibility and
743     * tooltips.</p>
744     *
745     * @param resId Resource ID of a drawable to set
746     *
747     * @attr ref android.R.styleable#Toolbar_navigationIcon
748     */
749    public void setNavigationIcon(int resId) {
750        setNavigationIcon(getContext().getDrawable(resId));
751    }
752
753    /**
754     * Set the icon to use for the toolbar's navigation button.
755     *
756     * <p>The navigation button appears at the start of the toolbar if present. Setting an icon
757     * will make the navigation button visible.</p>
758     *
759     * <p>If you use a navigation icon you should also set a description for its action using
760     * {@link #setNavigationContentDescription(int)}. This is used for accessibility and
761     * tooltips.</p>
762     *
763     * @param icon Drawable to set, may be null to clear the icon
764     *
765     * @attr ref android.R.styleable#Toolbar_navigationIcon
766     */
767    public void setNavigationIcon(@Nullable Drawable icon) {
768        if (icon != null) {
769            ensureNavButtonView();
770            if (mNavButtonView.getParent() == null) {
771                addSystemView(mNavButtonView);
772                updateChildVisibilityForExpandedActionView(mNavButtonView);
773            }
774        } else if (mNavButtonView != null && mNavButtonView.getParent() != null) {
775            removeView(mNavButtonView);
776        }
777        if (mNavButtonView != null) {
778            mNavButtonView.setImageDrawable(icon);
779        }
780    }
781
782    /**
783     * Return the current drawable used as the navigation icon.
784     *
785     * @return The navigation icon drawable
786     *
787     * @attr ref android.R.styleable#Toolbar_navigationIcon
788     */
789    @Nullable
790    public Drawable getNavigationIcon() {
791        return mNavButtonView != null ? mNavButtonView.getDrawable() : null;
792    }
793
794    /**
795     * Set a listener to respond to navigation events.
796     *
797     * <p>This listener will be called whenever the user clicks the navigation button
798     * at the start of the toolbar. An icon must be set for the navigation button to appear.</p>
799     *
800     * @param listener Listener to set
801     * @see #setNavigationIcon(android.graphics.drawable.Drawable)
802     */
803    public void setNavigationOnClickListener(OnClickListener listener) {
804        ensureNavButtonView();
805        mNavButtonView.setOnClickListener(listener);
806    }
807
808    /**
809     * Return the Menu shown in the toolbar.
810     *
811     * <p>Applications that wish to populate the toolbar's menu can do so from here. To use
812     * an XML menu resource, use {@link #inflateMenu(int)}.</p>
813     *
814     * @return The toolbar's Menu
815     */
816    public Menu getMenu() {
817        ensureMenu();
818        return mMenuView.getMenu();
819    }
820
821    private void ensureMenu() {
822        ensureMenuView();
823        if (mMenuView.peekMenu() == null) {
824            // Initialize a new menu for the first time.
825            final MenuBuilder menu = (MenuBuilder) mMenuView.getMenu();
826            if (mExpandedMenuPresenter == null) {
827                mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
828            }
829            mMenuView.setExpandedActionViewsExclusive(true);
830            menu.addMenuPresenter(mExpandedMenuPresenter, mPopupContext);
831        }
832    }
833
834    private void ensureMenuView() {
835        if (mMenuView == null) {
836            mMenuView = new ActionMenuView(getContext());
837            mMenuView.setPopupTheme(mPopupTheme);
838            mMenuView.setOnMenuItemClickListener(mMenuViewItemClickListener);
839            mMenuView.setMenuCallbacks(mActionMenuPresenterCallback, mMenuBuilderCallback);
840            final LayoutParams lp = generateDefaultLayoutParams();
841            lp.gravity = Gravity.END | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
842            mMenuView.setLayoutParams(lp);
843            addSystemView(mMenuView);
844        }
845    }
846
847    private MenuInflater getMenuInflater() {
848        return new MenuInflater(getContext());
849    }
850
851    /**
852     * Inflate a menu resource into this toolbar.
853     *
854     * <p>Inflate an XML menu resource into this toolbar. Existing items in the menu will not
855     * be modified or removed.</p>
856     *
857     * @param resId ID of a menu resource to inflate
858     */
859    public void inflateMenu(int resId) {
860        getMenuInflater().inflate(resId, getMenu());
861    }
862
863    /**
864     * Set a listener to respond to menu item click events.
865     *
866     * <p>This listener will be invoked whenever a user selects a menu item from
867     * the action buttons presented at the end of the toolbar or the associated overflow.</p>
868     *
869     * @param listener Listener to set
870     */
871    public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
872        mOnMenuItemClickListener = listener;
873    }
874
875    /**
876     * Set the content insets for this toolbar relative to layout direction.
877     *
878     * <p>The content inset affects the valid area for Toolbar content other than
879     * the navigation button and menu. Insets define the minimum margin for these components
880     * and can be used to effectively align Toolbar content along well-known gridlines.</p>
881     *
882     * @param contentInsetStart Content inset for the toolbar starting edge
883     * @param contentInsetEnd Content inset for the toolbar ending edge
884     *
885     * @see #setContentInsetsAbsolute(int, int)
886     * @see #getContentInsetStart()
887     * @see #getContentInsetEnd()
888     * @see #getContentInsetLeft()
889     * @see #getContentInsetRight()
890     */
891    public void setContentInsetsRelative(int contentInsetStart, int contentInsetEnd) {
892        mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
893    }
894
895    /**
896     * Get the starting content inset for this toolbar.
897     *
898     * <p>The content inset affects the valid area for Toolbar content other than
899     * the navigation button and menu. Insets define the minimum margin for these components
900     * and can be used to effectively align Toolbar content along well-known gridlines.</p>
901     *
902     * @return The starting content inset for this toolbar
903     *
904     * @see #setContentInsetsRelative(int, int)
905     * @see #setContentInsetsAbsolute(int, int)
906     * @see #getContentInsetEnd()
907     * @see #getContentInsetLeft()
908     * @see #getContentInsetRight()
909     */
910    public int getContentInsetStart() {
911        return mContentInsets.getStart();
912    }
913
914    /**
915     * Get the ending content inset for this toolbar.
916     *
917     * <p>The content inset affects the valid area for Toolbar content other than
918     * the navigation button and menu. Insets define the minimum margin for these components
919     * and can be used to effectively align Toolbar content along well-known gridlines.</p>
920     *
921     * @return The ending content inset for this toolbar
922     *
923     * @see #setContentInsetsRelative(int, int)
924     * @see #setContentInsetsAbsolute(int, int)
925     * @see #getContentInsetStart()
926     * @see #getContentInsetLeft()
927     * @see #getContentInsetRight()
928     */
929    public int getContentInsetEnd() {
930        return mContentInsets.getEnd();
931    }
932
933    /**
934     * Set the content insets for this toolbar.
935     *
936     * <p>The content inset affects the valid area for Toolbar content other than
937     * the navigation button and menu. Insets define the minimum margin for these components
938     * and can be used to effectively align Toolbar content along well-known gridlines.</p>
939     *
940     * @param contentInsetLeft Content inset for the toolbar's left edge
941     * @param contentInsetRight Content inset for the toolbar's right edge
942     *
943     * @see #setContentInsetsAbsolute(int, int)
944     * @see #getContentInsetStart()
945     * @see #getContentInsetEnd()
946     * @see #getContentInsetLeft()
947     * @see #getContentInsetRight()
948     */
949    public void setContentInsetsAbsolute(int contentInsetLeft, int contentInsetRight) {
950        mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight);
951    }
952
953    /**
954     * Get the left content inset for this toolbar.
955     *
956     * <p>The content inset affects the valid area for Toolbar content other than
957     * the navigation button and menu. Insets define the minimum margin for these components
958     * and can be used to effectively align Toolbar content along well-known gridlines.</p>
959     *
960     * @return The left content inset for this toolbar
961     *
962     * @see #setContentInsetsRelative(int, int)
963     * @see #setContentInsetsAbsolute(int, int)
964     * @see #getContentInsetStart()
965     * @see #getContentInsetEnd()
966     * @see #getContentInsetRight()
967     */
968    public int getContentInsetLeft() {
969        return mContentInsets.getLeft();
970    }
971
972    /**
973     * Get the right content inset for this toolbar.
974     *
975     * <p>The content inset affects the valid area for Toolbar content other than
976     * the navigation button and menu. Insets define the minimum margin for these components
977     * and can be used to effectively align Toolbar content along well-known gridlines.</p>
978     *
979     * @return The right content inset for this toolbar
980     *
981     * @see #setContentInsetsRelative(int, int)
982     * @see #setContentInsetsAbsolute(int, int)
983     * @see #getContentInsetStart()
984     * @see #getContentInsetEnd()
985     * @see #getContentInsetLeft()
986     */
987    public int getContentInsetRight() {
988        return mContentInsets.getRight();
989    }
990
991    private void ensureNavButtonView() {
992        if (mNavButtonView == null) {
993            mNavButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle);
994            final LayoutParams lp = generateDefaultLayoutParams();
995            lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
996            mNavButtonView.setLayoutParams(lp);
997        }
998    }
999
1000    private void ensureCollapseButtonView() {
1001        if (mCollapseButtonView == null) {
1002            mCollapseButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle);
1003            mCollapseButtonView.setImageDrawable(mCollapseIcon);
1004            mCollapseButtonView.setContentDescription(mCollapseDescription);
1005            final LayoutParams lp = generateDefaultLayoutParams();
1006            lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
1007            lp.mViewType = LayoutParams.EXPANDED;
1008            mCollapseButtonView.setLayoutParams(lp);
1009            mCollapseButtonView.setOnClickListener(new OnClickListener() {
1010                @Override
1011                public void onClick(View v) {
1012                    collapseActionView();
1013                }
1014            });
1015        }
1016    }
1017
1018    private void addSystemView(View v) {
1019        final ViewGroup.LayoutParams vlp = v.getLayoutParams();
1020        final LayoutParams lp;
1021        if (vlp == null) {
1022            lp = generateDefaultLayoutParams();
1023        } else if (!checkLayoutParams(vlp)) {
1024            lp = generateLayoutParams(vlp);
1025        } else {
1026            lp = (LayoutParams) vlp;
1027        }
1028        lp.mViewType = LayoutParams.SYSTEM;
1029        addView(v, lp);
1030    }
1031
1032    @Override
1033    protected Parcelable onSaveInstanceState() {
1034        SavedState state = new SavedState(super.onSaveInstanceState());
1035
1036        if (mExpandedMenuPresenter != null && mExpandedMenuPresenter.mCurrentExpandedItem != null) {
1037            state.expandedMenuItemId = mExpandedMenuPresenter.mCurrentExpandedItem.getItemId();
1038        }
1039
1040        state.isOverflowOpen = isOverflowMenuShowing();
1041
1042        return state;
1043    }
1044
1045    @Override
1046    protected void onRestoreInstanceState(Parcelable state) {
1047        final SavedState ss = (SavedState) state;
1048        super.onRestoreInstanceState(ss.getSuperState());
1049
1050        final Menu menu = mMenuView != null ? mMenuView.peekMenu() : null;
1051        if (ss.expandedMenuItemId != 0 && mExpandedMenuPresenter != null && menu != null) {
1052            final MenuItem item = menu.findItem(ss.expandedMenuItemId);
1053            if (item != null) {
1054                item.expandActionView();
1055            }
1056        }
1057
1058        if (ss.isOverflowOpen) {
1059            postShowOverflowMenu();
1060        }
1061    }
1062
1063    private void postShowOverflowMenu() {
1064        removeCallbacks(mShowOverflowMenuRunnable);
1065        post(mShowOverflowMenuRunnable);
1066    }
1067
1068    @Override
1069    protected void onDetachedFromWindow() {
1070        super.onDetachedFromWindow();
1071        removeCallbacks(mShowOverflowMenuRunnable);
1072    }
1073
1074    @Override
1075    public boolean onTouchEvent(MotionEvent ev) {
1076        // Toolbars always eat touch events, but should still respect the touch event dispatch
1077        // contract. If the normal View implementation doesn't want the events, we'll just silently
1078        // eat the rest of the gesture without reporting the events to the default implementation
1079        // since that's what it expects.
1080
1081        final int action = ev.getActionMasked();
1082        if (action == MotionEvent.ACTION_DOWN) {
1083            mEatingTouch = false;
1084        }
1085
1086        if (!mEatingTouch) {
1087            final boolean handled = super.onTouchEvent(ev);
1088            if (action == MotionEvent.ACTION_DOWN && !handled) {
1089                mEatingTouch = true;
1090            }
1091        }
1092
1093        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
1094            mEatingTouch = false;
1095        }
1096
1097        return true;
1098    }
1099
1100    /**
1101     * @hide
1102     */
1103    @Override
1104    public void addClickableRectsForAccessibility(List<RectF> outRects) {
1105        // This class always consumes touch events, therefore if it
1106        // covers a view we do not want to send a click over it.
1107        RectF bounds = new RectF();
1108        bounds.set(0, 0, getWidth(), getHeight());
1109        outRects.add(bounds);
1110    }
1111
1112    /**
1113     * @hide
1114     */
1115    @Override
1116    protected void onSetLayoutParams(View child, ViewGroup.LayoutParams lp) {
1117        /*
1118         * Apps may set ActionBar.LayoutParams on their action bar custom views when
1119         * a Toolbar is actually acting in the role of the action bar. Perform a quick
1120         * switch with Toolbar.LayoutParams whenever this happens. This does leave open
1121         * one potential gotcha: if an app retains the ActionBar.LayoutParams reference
1122         * and attempts to keep making changes to it before layout those changes won't
1123         * be reflected in the final results.
1124         */
1125        if (!checkLayoutParams(lp)) {
1126            child.setLayoutParams(generateLayoutParams(lp));
1127        }
1128    }
1129
1130    private void measureChildConstrained(View child, int parentWidthSpec, int widthUsed,
1131            int parentHeightSpec, int heightUsed, int heightConstraint) {
1132        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
1133
1134        int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
1135                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
1136                        + widthUsed, lp.width);
1137        int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
1138                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
1139                        + heightUsed, lp.height);
1140
1141        final int childHeightMode = MeasureSpec.getMode(childHeightSpec);
1142        if (childHeightMode != MeasureSpec.EXACTLY && heightConstraint >= 0) {
1143            final int size = childHeightMode != MeasureSpec.UNSPECIFIED ?
1144                    Math.min(MeasureSpec.getSize(childHeightSpec), heightConstraint) :
1145                    heightConstraint;
1146            childHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
1147        }
1148        child.measure(childWidthSpec, childHeightSpec);
1149    }
1150
1151    /**
1152     * Returns the width + uncollapsed margins
1153     */
1154    private int measureChildCollapseMargins(View child,
1155            int parentWidthMeasureSpec, int widthUsed,
1156            int parentHeightMeasureSpec, int heightUsed, int[] collapsingMargins) {
1157        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
1158
1159        final int leftDiff = lp.leftMargin - collapsingMargins[0];
1160        final int rightDiff = lp.rightMargin - collapsingMargins[1];
1161        final int leftMargin = Math.max(0, leftDiff);
1162        final int rightMargin = Math.max(0, rightDiff);
1163        final int hMargins = leftMargin + rightMargin;
1164        collapsingMargins[0] = Math.max(0, -leftDiff);
1165        collapsingMargins[1] = Math.max(0, -rightDiff);
1166
1167        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
1168                mPaddingLeft + mPaddingRight + hMargins + widthUsed, lp.width);
1169        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
1170                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
1171                        + heightUsed, lp.height);
1172
1173        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
1174        return child.getMeasuredWidth() + hMargins;
1175    }
1176
1177    /**
1178     * Returns true if the Toolbar is collapsible and has no child views with a measured size > 0.
1179     */
1180    private boolean shouldCollapse() {
1181        if (!mCollapsible) return false;
1182
1183        final int childCount = getChildCount();
1184        for (int i = 0; i < childCount; i++) {
1185            final View child = getChildAt(i);
1186            if (shouldLayout(child) && child.getMeasuredWidth() > 0 &&
1187                    child.getMeasuredHeight() > 0) {
1188                return false;
1189            }
1190        }
1191        return true;
1192    }
1193
1194    @Override
1195    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1196        int width = 0;
1197        int height = 0;
1198        int childState = 0;
1199
1200        final int[] collapsingMargins = mTempMargins;
1201        final int marginStartIndex;
1202        final int marginEndIndex;
1203        if (isLayoutRtl()) {
1204            marginStartIndex = 1;
1205            marginEndIndex = 0;
1206        } else {
1207            marginStartIndex = 0;
1208            marginEndIndex = 1;
1209        }
1210
1211        // System views measure first.
1212
1213        int navWidth = 0;
1214        if (shouldLayout(mNavButtonView)) {
1215            measureChildConstrained(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0,
1216                    mMaxButtonHeight);
1217            navWidth = mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView);
1218            height = Math.max(height, mNavButtonView.getMeasuredHeight() +
1219                    getVerticalMargins(mNavButtonView));
1220            childState = combineMeasuredStates(childState, mNavButtonView.getMeasuredState());
1221        }
1222
1223        if (shouldLayout(mCollapseButtonView)) {
1224            measureChildConstrained(mCollapseButtonView, widthMeasureSpec, width,
1225                    heightMeasureSpec, 0, mMaxButtonHeight);
1226            navWidth = mCollapseButtonView.getMeasuredWidth() +
1227                    getHorizontalMargins(mCollapseButtonView);
1228            height = Math.max(height, mCollapseButtonView.getMeasuredHeight() +
1229                    getVerticalMargins(mCollapseButtonView));
1230            childState = combineMeasuredStates(childState, mCollapseButtonView.getMeasuredState());
1231        }
1232
1233        final int contentInsetStart = getContentInsetStart();
1234        width += Math.max(contentInsetStart, navWidth);
1235        collapsingMargins[marginStartIndex] = Math.max(0, contentInsetStart - navWidth);
1236
1237        int menuWidth = 0;
1238        if (shouldLayout(mMenuView)) {
1239            measureChildConstrained(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0,
1240                    mMaxButtonHeight);
1241            menuWidth = mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView);
1242            height = Math.max(height, mMenuView.getMeasuredHeight() +
1243                    getVerticalMargins(mMenuView));
1244            childState = combineMeasuredStates(childState, mMenuView.getMeasuredState());
1245        }
1246
1247        final int contentInsetEnd = getContentInsetEnd();
1248        width += Math.max(contentInsetEnd, menuWidth);
1249        collapsingMargins[marginEndIndex] = Math.max(0, contentInsetEnd - menuWidth);
1250
1251        if (shouldLayout(mExpandedActionView)) {
1252            width += measureChildCollapseMargins(mExpandedActionView, widthMeasureSpec, width,
1253                    heightMeasureSpec, 0, collapsingMargins);
1254            height = Math.max(height, mExpandedActionView.getMeasuredHeight() +
1255                    getVerticalMargins(mExpandedActionView));
1256            childState = combineMeasuredStates(childState, mExpandedActionView.getMeasuredState());
1257        }
1258
1259        if (shouldLayout(mLogoView)) {
1260            width += measureChildCollapseMargins(mLogoView, widthMeasureSpec, width,
1261                    heightMeasureSpec, 0, collapsingMargins);
1262            height = Math.max(height, mLogoView.getMeasuredHeight() +
1263                    getVerticalMargins(mLogoView));
1264            childState = combineMeasuredStates(childState, mLogoView.getMeasuredState());
1265        }
1266
1267        final int childCount = getChildCount();
1268        for (int i = 0; i < childCount; i++) {
1269            final View child = getChildAt(i);
1270            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1271            if (lp.mViewType != LayoutParams.CUSTOM || !shouldLayout(child)) {
1272                // We already got all system views above. Skip them and GONE views.
1273                continue;
1274            }
1275
1276            width += measureChildCollapseMargins(child, widthMeasureSpec, width,
1277                    heightMeasureSpec, 0, collapsingMargins);
1278            height = Math.max(height, child.getMeasuredHeight() + getVerticalMargins(child));
1279            childState = combineMeasuredStates(childState, child.getMeasuredState());
1280        }
1281
1282        int titleWidth = 0;
1283        int titleHeight = 0;
1284        final int titleVertMargins = mTitleMarginTop + mTitleMarginBottom;
1285        final int titleHorizMargins = mTitleMarginStart + mTitleMarginEnd;
1286        if (shouldLayout(mTitleTextView)) {
1287            titleWidth = measureChildCollapseMargins(mTitleTextView, widthMeasureSpec,
1288                    width + titleHorizMargins, heightMeasureSpec, titleVertMargins,
1289                    collapsingMargins);
1290            titleWidth = mTitleTextView.getMeasuredWidth() + getHorizontalMargins(mTitleTextView);
1291            titleHeight = mTitleTextView.getMeasuredHeight() + getVerticalMargins(mTitleTextView);
1292            childState = combineMeasuredStates(childState, mTitleTextView.getMeasuredState());
1293        }
1294        if (shouldLayout(mSubtitleTextView)) {
1295            titleWidth = Math.max(titleWidth, measureChildCollapseMargins(mSubtitleTextView,
1296                    widthMeasureSpec, width + titleHorizMargins,
1297                    heightMeasureSpec, titleHeight + titleVertMargins,
1298                    collapsingMargins));
1299            titleHeight += mSubtitleTextView.getMeasuredHeight() +
1300                    getVerticalMargins(mSubtitleTextView);
1301            childState = combineMeasuredStates(childState, mSubtitleTextView.getMeasuredState());
1302        }
1303
1304        width += titleWidth;
1305        height = Math.max(height, titleHeight);
1306
1307        // Measurement already took padding into account for available space for the children,
1308        // add it in for the final size.
1309        width += getPaddingLeft() + getPaddingRight();
1310        height += getPaddingTop() + getPaddingBottom();
1311
1312        final int measuredWidth = resolveSizeAndState(
1313                Math.max(width, getSuggestedMinimumWidth()),
1314                widthMeasureSpec, childState & MEASURED_STATE_MASK);
1315        final int measuredHeight = resolveSizeAndState(
1316                Math.max(height, getSuggestedMinimumHeight()),
1317                heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT);
1318
1319        setMeasuredDimension(measuredWidth, shouldCollapse() ? 0 : measuredHeight);
1320    }
1321
1322    @Override
1323    protected void onLayout(boolean changed, int l, int t, int r, int b) {
1324        final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
1325        final int width = getWidth();
1326        final int height = getHeight();
1327        final int paddingLeft = getPaddingLeft();
1328        final int paddingRight = getPaddingRight();
1329        final int paddingTop = getPaddingTop();
1330        final int paddingBottom = getPaddingBottom();
1331        int left = paddingLeft;
1332        int right = width - paddingRight;
1333
1334        final int[] collapsingMargins = mTempMargins;
1335        collapsingMargins[0] = collapsingMargins[1] = 0;
1336
1337        // Align views within the minimum toolbar height, if set.
1338        final int alignmentHeight = getMinimumHeight();
1339
1340        if (shouldLayout(mNavButtonView)) {
1341            if (isRtl) {
1342                right = layoutChildRight(mNavButtonView, right, collapsingMargins,
1343                        alignmentHeight);
1344            } else {
1345                left = layoutChildLeft(mNavButtonView, left, collapsingMargins,
1346                        alignmentHeight);
1347            }
1348        }
1349
1350        if (shouldLayout(mCollapseButtonView)) {
1351            if (isRtl) {
1352                right = layoutChildRight(mCollapseButtonView, right, collapsingMargins,
1353                        alignmentHeight);
1354            } else {
1355                left = layoutChildLeft(mCollapseButtonView, left, collapsingMargins,
1356                        alignmentHeight);
1357            }
1358        }
1359
1360        if (shouldLayout(mMenuView)) {
1361            if (isRtl) {
1362                left = layoutChildLeft(mMenuView, left, collapsingMargins,
1363                        alignmentHeight);
1364            } else {
1365                right = layoutChildRight(mMenuView, right, collapsingMargins,
1366                        alignmentHeight);
1367            }
1368        }
1369
1370        collapsingMargins[0] = Math.max(0, getContentInsetLeft() - left);
1371        collapsingMargins[1] = Math.max(0, getContentInsetRight() - (width - paddingRight - right));
1372        left = Math.max(left, getContentInsetLeft());
1373        right = Math.min(right, width - paddingRight - getContentInsetRight());
1374
1375        if (shouldLayout(mExpandedActionView)) {
1376            if (isRtl) {
1377                right = layoutChildRight(mExpandedActionView, right, collapsingMargins,
1378                        alignmentHeight);
1379            } else {
1380                left = layoutChildLeft(mExpandedActionView, left, collapsingMargins,
1381                        alignmentHeight);
1382            }
1383        }
1384
1385        if (shouldLayout(mLogoView)) {
1386            if (isRtl) {
1387                right = layoutChildRight(mLogoView, right, collapsingMargins,
1388                        alignmentHeight);
1389            } else {
1390                left = layoutChildLeft(mLogoView, left, collapsingMargins,
1391                        alignmentHeight);
1392            }
1393        }
1394
1395        final boolean layoutTitle = shouldLayout(mTitleTextView);
1396        final boolean layoutSubtitle = shouldLayout(mSubtitleTextView);
1397        int titleHeight = 0;
1398        if (layoutTitle) {
1399            final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
1400            titleHeight += lp.topMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin;
1401        }
1402        if (layoutSubtitle) {
1403            final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
1404            titleHeight += lp.topMargin + mSubtitleTextView.getMeasuredHeight() + lp.bottomMargin;
1405        }
1406
1407        if (layoutTitle || layoutSubtitle) {
1408            int titleTop;
1409            final View topChild = layoutTitle ? mTitleTextView : mSubtitleTextView;
1410            final View bottomChild = layoutSubtitle ? mSubtitleTextView : mTitleTextView;
1411            final LayoutParams toplp = (LayoutParams) topChild.getLayoutParams();
1412            final LayoutParams bottomlp = (LayoutParams) bottomChild.getLayoutParams();
1413            final boolean titleHasWidth = layoutTitle && mTitleTextView.getMeasuredWidth() > 0
1414                    || layoutSubtitle && mSubtitleTextView.getMeasuredWidth() > 0;
1415
1416            switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {
1417                case Gravity.TOP:
1418                    titleTop = getPaddingTop() + toplp.topMargin + mTitleMarginTop;
1419                    break;
1420                default:
1421                case Gravity.CENTER_VERTICAL:
1422                    final int space = height - paddingTop - paddingBottom;
1423                    int spaceAbove = (space - titleHeight) / 2;
1424                    if (spaceAbove < toplp.topMargin + mTitleMarginTop) {
1425                        spaceAbove = toplp.topMargin + mTitleMarginTop;
1426                    } else {
1427                        final int spaceBelow = height - paddingBottom - titleHeight -
1428                                spaceAbove - paddingTop;
1429                        if (spaceBelow < toplp.bottomMargin + mTitleMarginBottom) {
1430                            spaceAbove = Math.max(0, spaceAbove -
1431                                    (bottomlp.bottomMargin + mTitleMarginBottom - spaceBelow));
1432                        }
1433                    }
1434                    titleTop = paddingTop + spaceAbove;
1435                    break;
1436                case Gravity.BOTTOM:
1437                    titleTop = height - paddingBottom - bottomlp.bottomMargin - mTitleMarginBottom -
1438                            titleHeight;
1439                    break;
1440            }
1441            if (isRtl) {
1442                final int rd = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[1];
1443                right -= Math.max(0, rd);
1444                collapsingMargins[1] = Math.max(0, -rd);
1445                int titleRight = right;
1446                int subtitleRight = right;
1447
1448                if (layoutTitle) {
1449                    final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
1450                    final int titleLeft = titleRight - mTitleTextView.getMeasuredWidth();
1451                    final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
1452                    mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
1453                    titleRight = titleLeft - mTitleMarginEnd;
1454                    titleTop = titleBottom + lp.bottomMargin;
1455                }
1456                if (layoutSubtitle) {
1457                    final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
1458                    titleTop += lp.topMargin;
1459                    final int subtitleLeft = subtitleRight - mSubtitleTextView.getMeasuredWidth();
1460                    final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
1461                    mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
1462                    subtitleRight = subtitleRight - mTitleMarginEnd;
1463                    titleTop = subtitleBottom + lp.bottomMargin;
1464                }
1465                if (titleHasWidth) {
1466                    right = Math.min(titleRight, subtitleRight);
1467                }
1468            } else {
1469                final int ld = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[0];
1470                left += Math.max(0, ld);
1471                collapsingMargins[0] = Math.max(0, -ld);
1472                int titleLeft = left;
1473                int subtitleLeft = left;
1474
1475                if (layoutTitle) {
1476                    final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
1477                    final int titleRight = titleLeft + mTitleTextView.getMeasuredWidth();
1478                    final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
1479                    mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
1480                    titleLeft = titleRight + mTitleMarginEnd;
1481                    titleTop = titleBottom + lp.bottomMargin;
1482                }
1483                if (layoutSubtitle) {
1484                    final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
1485                    titleTop += lp.topMargin;
1486                    final int subtitleRight = subtitleLeft + mSubtitleTextView.getMeasuredWidth();
1487                    final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
1488                    mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
1489                    subtitleLeft = subtitleRight + mTitleMarginEnd;
1490                    titleTop = subtitleBottom + lp.bottomMargin;
1491                }
1492                if (titleHasWidth) {
1493                    left = Math.max(titleLeft, subtitleLeft);
1494                }
1495            }
1496        }
1497
1498        // Get all remaining children sorted for layout. This is all prepared
1499        // such that absolute layout direction can be used below.
1500
1501        addCustomViewsWithGravity(mTempViews, Gravity.LEFT);
1502        final int leftViewsCount = mTempViews.size();
1503        for (int i = 0; i < leftViewsCount; i++) {
1504            left = layoutChildLeft(mTempViews.get(i), left, collapsingMargins,
1505                    alignmentHeight);
1506        }
1507
1508        addCustomViewsWithGravity(mTempViews, Gravity.RIGHT);
1509        final int rightViewsCount = mTempViews.size();
1510        for (int i = 0; i < rightViewsCount; i++) {
1511            right = layoutChildRight(mTempViews.get(i), right, collapsingMargins,
1512                    alignmentHeight);
1513        }
1514
1515        // Centered views try to center with respect to the whole bar, but views pinned
1516        // to the left or right can push the mass of centered views to one side or the other.
1517        addCustomViewsWithGravity(mTempViews, Gravity.CENTER_HORIZONTAL);
1518        final int centerViewsWidth = getViewListMeasuredWidth(mTempViews, collapsingMargins);
1519        final int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2;
1520        final int halfCenterViewsWidth = centerViewsWidth / 2;
1521        int centerLeft = parentCenter - halfCenterViewsWidth;
1522        final int centerRight = centerLeft + centerViewsWidth;
1523        if (centerLeft < left) {
1524            centerLeft = left;
1525        } else if (centerRight > right) {
1526            centerLeft -= centerRight - right;
1527        }
1528
1529        final int centerViewsCount = mTempViews.size();
1530        for (int i = 0; i < centerViewsCount; i++) {
1531            centerLeft = layoutChildLeft(mTempViews.get(i), centerLeft, collapsingMargins,
1532                    alignmentHeight);
1533        }
1534
1535        mTempViews.clear();
1536    }
1537
1538    private int getViewListMeasuredWidth(List<View> views, int[] collapsingMargins) {
1539        int collapseLeft = collapsingMargins[0];
1540        int collapseRight = collapsingMargins[1];
1541        int width = 0;
1542        final int count = views.size();
1543        for (int i = 0; i < count; i++) {
1544            final View v = views.get(i);
1545            final LayoutParams lp = (LayoutParams) v.getLayoutParams();
1546            final int l = lp.leftMargin - collapseLeft;
1547            final int r = lp.rightMargin - collapseRight;
1548            final int leftMargin = Math.max(0, l);
1549            final int rightMargin = Math.max(0, r);
1550            collapseLeft = Math.max(0, -l);
1551            collapseRight = Math.max(0, -r);
1552            width += leftMargin + v.getMeasuredWidth() + rightMargin;
1553        }
1554        return width;
1555    }
1556
1557    private int layoutChildLeft(View child, int left, int[] collapsingMargins,
1558            int alignmentHeight) {
1559        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1560        final int l = lp.leftMargin - collapsingMargins[0];
1561        left += Math.max(0, l);
1562        collapsingMargins[0] = Math.max(0, -l);
1563        final int top = getChildTop(child, alignmentHeight);
1564        final int childWidth = child.getMeasuredWidth();
1565        child.layout(left, top, left + childWidth, top + child.getMeasuredHeight());
1566        left += childWidth + lp.rightMargin;
1567        return left;
1568    }
1569
1570    private int layoutChildRight(View child, int right, int[] collapsingMargins,
1571            int alignmentHeight) {
1572        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1573        final int r = lp.rightMargin - collapsingMargins[1];
1574        right -= Math.max(0, r);
1575        collapsingMargins[1] = Math.max(0, -r);
1576        final int top = getChildTop(child, alignmentHeight);
1577        final int childWidth = child.getMeasuredWidth();
1578        child.layout(right - childWidth, top, right, top + child.getMeasuredHeight());
1579        right -= childWidth + lp.leftMargin;
1580        return right;
1581    }
1582
1583    private int getChildTop(View child, int alignmentHeight) {
1584        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1585        final int childHeight = child.getMeasuredHeight();
1586        final int alignmentOffset = alignmentHeight > 0 ? (childHeight - alignmentHeight) / 2 : 0;
1587        switch (getChildVerticalGravity(lp.gravity)) {
1588            case Gravity.TOP:
1589                return getPaddingTop() - alignmentOffset;
1590
1591            case Gravity.BOTTOM:
1592                return getHeight() - getPaddingBottom() - childHeight
1593                        - lp.bottomMargin - alignmentOffset;
1594
1595            default:
1596            case Gravity.CENTER_VERTICAL:
1597                final int paddingTop = getPaddingTop();
1598                final int paddingBottom = getPaddingBottom();
1599                final int height = getHeight();
1600                final int space = height - paddingTop - paddingBottom;
1601                int spaceAbove = (space - childHeight) / 2;
1602                if (spaceAbove < lp.topMargin) {
1603                    spaceAbove = lp.topMargin;
1604                } else {
1605                    final int spaceBelow = height - paddingBottom - childHeight -
1606                            spaceAbove - paddingTop;
1607                    if (spaceBelow < lp.bottomMargin) {
1608                        spaceAbove = Math.max(0, spaceAbove - (lp.bottomMargin - spaceBelow));
1609                    }
1610                }
1611                return paddingTop + spaceAbove;
1612        }
1613    }
1614
1615    private int getChildVerticalGravity(int gravity) {
1616        final int vgrav = gravity & Gravity.VERTICAL_GRAVITY_MASK;
1617        switch (vgrav) {
1618            case Gravity.TOP:
1619            case Gravity.BOTTOM:
1620            case Gravity.CENTER_VERTICAL:
1621                return vgrav;
1622            default:
1623                return mGravity & Gravity.VERTICAL_GRAVITY_MASK;
1624        }
1625    }
1626
1627    /**
1628     * Prepare a list of non-SYSTEM child views. If the layout direction is RTL
1629     * this will be in reverse child order.
1630     *
1631     * @param views List to populate. It will be cleared before use.
1632     * @param gravity Horizontal gravity to match against
1633     */
1634    private void addCustomViewsWithGravity(List<View> views, int gravity) {
1635        final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
1636        final int childCount = getChildCount();
1637        final int absGrav = Gravity.getAbsoluteGravity(gravity, getLayoutDirection());
1638
1639        views.clear();
1640
1641        if (isRtl) {
1642            for (int i = childCount - 1; i >= 0; i--) {
1643                final View child = getChildAt(i);
1644                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1645                if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) &&
1646                        getChildHorizontalGravity(lp.gravity) == absGrav) {
1647                    views.add(child);
1648                }
1649            }
1650        } else {
1651            for (int i = 0; i < childCount; i++) {
1652                final View child = getChildAt(i);
1653                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1654                if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) &&
1655                        getChildHorizontalGravity(lp.gravity) == absGrav) {
1656                    views.add(child);
1657                }
1658            }
1659        }
1660    }
1661
1662    private int getChildHorizontalGravity(int gravity) {
1663        final int ld = getLayoutDirection();
1664        final int absGrav = Gravity.getAbsoluteGravity(gravity, ld);
1665        final int hGrav = absGrav & Gravity.HORIZONTAL_GRAVITY_MASK;
1666        switch (hGrav) {
1667            case Gravity.LEFT:
1668            case Gravity.RIGHT:
1669            case Gravity.CENTER_HORIZONTAL:
1670                return hGrav;
1671            default:
1672                return ld == LAYOUT_DIRECTION_RTL ? Gravity.RIGHT : Gravity.LEFT;
1673        }
1674    }
1675
1676    private boolean shouldLayout(View view) {
1677        return view != null && view.getParent() == this && view.getVisibility() != GONE;
1678    }
1679
1680    private int getHorizontalMargins(View v) {
1681        final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
1682        return mlp.getMarginStart() + mlp.getMarginEnd();
1683    }
1684
1685    private int getVerticalMargins(View v) {
1686        final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
1687        return mlp.topMargin + mlp.bottomMargin;
1688    }
1689
1690    @Override
1691    public LayoutParams generateLayoutParams(AttributeSet attrs) {
1692        return new LayoutParams(getContext(), attrs);
1693    }
1694
1695    @Override
1696    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1697        if (p instanceof LayoutParams) {
1698            return new LayoutParams((LayoutParams) p);
1699        } else if (p instanceof ActionBar.LayoutParams) {
1700            return new LayoutParams((ActionBar.LayoutParams) p);
1701        } else if (p instanceof MarginLayoutParams) {
1702            return new LayoutParams((MarginLayoutParams) p);
1703        } else {
1704            return new LayoutParams(p);
1705        }
1706    }
1707
1708    @Override
1709    protected LayoutParams generateDefaultLayoutParams() {
1710        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1711    }
1712
1713    @Override
1714    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1715        return super.checkLayoutParams(p) && p instanceof LayoutParams;
1716    }
1717
1718    private static boolean isCustomView(View child) {
1719        return ((LayoutParams) child.getLayoutParams()).mViewType == LayoutParams.CUSTOM;
1720    }
1721
1722    /** @hide */
1723    public DecorToolbar getWrapper() {
1724        if (mWrapper == null) {
1725            mWrapper = new ToolbarWidgetWrapper(this, true);
1726        }
1727        return mWrapper;
1728    }
1729
1730    private void setChildVisibilityForExpandedActionView(boolean expand) {
1731        final int childCount = getChildCount();
1732        for (int i = 0; i < childCount; i++) {
1733            final View child = getChildAt(i);
1734            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1735            if (lp.mViewType != LayoutParams.EXPANDED && child != mMenuView) {
1736                child.setVisibility(expand ? GONE : VISIBLE);
1737            }
1738        }
1739    }
1740
1741    private void updateChildVisibilityForExpandedActionView(View child) {
1742        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1743        if (lp.mViewType != LayoutParams.EXPANDED && child != mMenuView) {
1744            child.setVisibility(mExpandedActionView != null ? GONE : VISIBLE);
1745        }
1746    }
1747
1748    /**
1749     * Force the toolbar to collapse to zero-height during measurement if
1750     * it could be considered "empty" (no visible elements with nonzero measured size)
1751     * @hide
1752     */
1753    public void setCollapsible(boolean collapsible) {
1754        mCollapsible = collapsible;
1755        requestLayout();
1756    }
1757
1758    /**
1759     * Must be called before the menu is accessed
1760     * @hide
1761     */
1762    public void setMenuCallbacks(MenuPresenter.Callback pcb, MenuBuilder.Callback mcb) {
1763        mActionMenuPresenterCallback = pcb;
1764        mMenuBuilderCallback = mcb;
1765    }
1766
1767    /**
1768     * Accessor to enable LayoutLib to get ActionMenuPresenter directly.
1769     */
1770    ActionMenuPresenter getOuterActionMenuPresenter() {
1771        return mOuterActionMenuPresenter;
1772    }
1773
1774    Context getPopupContext() {
1775        return mPopupContext;
1776    }
1777
1778    /**
1779     * Interface responsible for receiving menu item click events if the items themselves
1780     * do not have individual item click listeners.
1781     */
1782    public interface OnMenuItemClickListener {
1783        /**
1784         * This method will be invoked when a menu item is clicked if the item itself did
1785         * not already handle the event.
1786         *
1787         * @param item {@link MenuItem} that was clicked
1788         * @return <code>true</code> if the event was handled, <code>false</code> otherwise.
1789         */
1790        public boolean onMenuItemClick(MenuItem item);
1791    }
1792
1793    /**
1794     * Layout information for child views of Toolbars.
1795     *
1796     * <p>Toolbar.LayoutParams extends ActionBar.LayoutParams for compatibility with existing
1797     * ActionBar API. See {@link android.app.Activity#setActionBar(Toolbar) Activity.setActionBar}
1798     * for more info on how to use a Toolbar as your Activity's ActionBar.</p>
1799     *
1800     * @attr ref android.R.styleable#Toolbar_LayoutParams_layout_gravity
1801     */
1802    public static class LayoutParams extends ActionBar.LayoutParams {
1803        static final int CUSTOM = 0;
1804        static final int SYSTEM = 1;
1805        static final int EXPANDED = 2;
1806
1807        int mViewType = CUSTOM;
1808
1809        public LayoutParams(@NonNull Context c, AttributeSet attrs) {
1810            super(c, attrs);
1811        }
1812
1813        public LayoutParams(int width, int height) {
1814            super(width, height);
1815            this.gravity = Gravity.CENTER_VERTICAL | Gravity.START;
1816        }
1817
1818        public LayoutParams(int width, int height, int gravity) {
1819            super(width, height);
1820            this.gravity = gravity;
1821        }
1822
1823        public LayoutParams(int gravity) {
1824            this(WRAP_CONTENT, MATCH_PARENT, gravity);
1825        }
1826
1827        public LayoutParams(LayoutParams source) {
1828            super(source);
1829
1830            mViewType = source.mViewType;
1831        }
1832
1833        public LayoutParams(ActionBar.LayoutParams source) {
1834            super(source);
1835        }
1836
1837        public LayoutParams(MarginLayoutParams source) {
1838            super(source);
1839            // ActionBar.LayoutParams doesn't have a MarginLayoutParams constructor.
1840            // Fake it here and copy over the relevant data.
1841            copyMarginsFrom(source);
1842        }
1843
1844        public LayoutParams(ViewGroup.LayoutParams source) {
1845            super(source);
1846        }
1847    }
1848
1849    static class SavedState extends BaseSavedState {
1850        public int expandedMenuItemId;
1851        public boolean isOverflowOpen;
1852
1853        public SavedState(Parcel source) {
1854            super(source);
1855            expandedMenuItemId = source.readInt();
1856            isOverflowOpen = source.readInt() != 0;
1857        }
1858
1859        public SavedState(Parcelable superState) {
1860            super(superState);
1861        }
1862
1863        @Override
1864        public void writeToParcel(Parcel out, int flags) {
1865            super.writeToParcel(out, flags);
1866            out.writeInt(expandedMenuItemId);
1867            out.writeInt(isOverflowOpen ? 1 : 0);
1868        }
1869
1870        public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
1871
1872            @Override
1873            public SavedState createFromParcel(Parcel source) {
1874                return new SavedState(source);
1875            }
1876
1877            @Override
1878            public SavedState[] newArray(int size) {
1879                return new SavedState[size];
1880            }
1881        };
1882    }
1883
1884    private class ExpandedActionViewMenuPresenter implements MenuPresenter {
1885        MenuBuilder mMenu;
1886        MenuItemImpl mCurrentExpandedItem;
1887
1888        @Override
1889        public void initForMenu(Context context, MenuBuilder menu) {
1890            // Clear the expanded action view when menus change.
1891            if (mMenu != null && mCurrentExpandedItem != null) {
1892                mMenu.collapseItemActionView(mCurrentExpandedItem);
1893            }
1894            mMenu = menu;
1895        }
1896
1897        @Override
1898        public MenuView getMenuView(ViewGroup root) {
1899            return null;
1900        }
1901
1902        @Override
1903        public void updateMenuView(boolean cleared) {
1904            // Make sure the expanded item we have is still there.
1905            if (mCurrentExpandedItem != null) {
1906                boolean found = false;
1907
1908                if (mMenu != null) {
1909                    final int count = mMenu.size();
1910                    for (int i = 0; i < count; i++) {
1911                        final MenuItem item = mMenu.getItem(i);
1912                        if (item == mCurrentExpandedItem) {
1913                            found = true;
1914                            break;
1915                        }
1916                    }
1917                }
1918
1919                if (!found) {
1920                    // The item we had expanded disappeared. Collapse.
1921                    collapseItemActionView(mMenu, mCurrentExpandedItem);
1922                }
1923            }
1924        }
1925
1926        @Override
1927        public void setCallback(Callback cb) {
1928        }
1929
1930        @Override
1931        public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
1932            return false;
1933        }
1934
1935        @Override
1936        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
1937        }
1938
1939        @Override
1940        public boolean flagActionItems() {
1941            return false;
1942        }
1943
1944        @Override
1945        public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
1946            ensureCollapseButtonView();
1947            if (mCollapseButtonView.getParent() != Toolbar.this) {
1948                addView(mCollapseButtonView);
1949            }
1950            mExpandedActionView = item.getActionView();
1951            mCurrentExpandedItem = item;
1952            if (mExpandedActionView.getParent() != Toolbar.this) {
1953                final LayoutParams lp = generateDefaultLayoutParams();
1954                lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
1955                lp.mViewType = LayoutParams.EXPANDED;
1956                mExpandedActionView.setLayoutParams(lp);
1957                addView(mExpandedActionView);
1958            }
1959
1960            setChildVisibilityForExpandedActionView(true);
1961            requestLayout();
1962            item.setActionViewExpanded(true);
1963
1964            if (mExpandedActionView instanceof CollapsibleActionView) {
1965                ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded();
1966            }
1967
1968            return true;
1969        }
1970
1971        @Override
1972        public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
1973            // Do this before detaching the actionview from the hierarchy, in case
1974            // it needs to dismiss the soft keyboard, etc.
1975            if (mExpandedActionView instanceof CollapsibleActionView) {
1976                ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed();
1977            }
1978
1979            removeView(mExpandedActionView);
1980            removeView(mCollapseButtonView);
1981            mExpandedActionView = null;
1982
1983            setChildVisibilityForExpandedActionView(false);
1984            mCurrentExpandedItem = null;
1985            requestLayout();
1986            item.setActionViewExpanded(false);
1987
1988            return true;
1989        }
1990
1991        @Override
1992        public int getId() {
1993            return 0;
1994        }
1995
1996        @Override
1997        public Parcelable onSaveInstanceState() {
1998            return null;
1999        }
2000
2001        @Override
2002        public void onRestoreInstanceState(Parcelable state) {
2003        }
2004    }
2005}
2006