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