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