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