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