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