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