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