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