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