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