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