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