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