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