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