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