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 
17 package android.support.v7.internal.app;
18 
19 import android.app.Activity;
20 import android.app.Dialog;
21 import android.content.Context;
22 import android.content.res.Configuration;
23 import android.content.res.Resources;
24 import android.content.res.TypedArray;
25 import android.graphics.drawable.Drawable;
26 import android.os.Build;
27 import android.support.v4.app.FragmentActivity;
28 import android.support.v4.app.FragmentTransaction;
29 import android.support.v4.view.ViewCompat;
30 import android.support.v4.view.ViewPropertyAnimatorCompat;
31 import android.support.v4.view.ViewPropertyAnimatorListener;
32 import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
33 import android.support.v4.view.ViewPropertyAnimatorUpdateListener;
34 import android.support.v7.app.ActionBar;
35 import android.support.v7.appcompat.R;
36 import android.support.v7.internal.view.ActionBarPolicy;
37 import android.support.v7.internal.view.SupportMenuInflater;
38 import android.support.v7.internal.view.ViewPropertyAnimatorCompatSet;
39 import android.support.v7.internal.view.menu.MenuBuilder;
40 import android.support.v7.internal.view.menu.MenuPopupHelper;
41 import android.support.v7.internal.view.menu.SubMenuBuilder;
42 import android.support.v7.internal.widget.ActionBarContainer;
43 import android.support.v7.internal.widget.ActionBarContextView;
44 import android.support.v7.internal.widget.ActionBarOverlayLayout;
45 import android.support.v7.internal.widget.DecorToolbar;
46 import android.support.v7.internal.widget.ScrollingTabContainerView;
47 import android.support.v7.internal.widget.TintManager;
48 import android.support.v7.view.ActionMode;
49 import android.support.v7.widget.Toolbar;
50 import android.util.TypedValue;
51 import android.view.ContextThemeWrapper;
52 import android.view.LayoutInflater;
53 import android.view.Menu;
54 import android.view.MenuInflater;
55 import android.view.MenuItem;
56 import android.view.View;
57 import android.view.ViewParent;
58 import android.view.Window;
59 import android.view.accessibility.AccessibilityEvent;
60 import android.view.animation.AccelerateInterpolator;
61 import android.view.animation.AnimationUtils;
62 import android.view.animation.DecelerateInterpolator;
63 import android.view.animation.Interpolator;
64 import android.widget.SpinnerAdapter;
65 
66 import java.lang.ref.WeakReference;
67 import java.util.ArrayList;
68 
69 /**
70  * WindowDecorActionBar is the ActionBar implementation used
71  * by devices of all screen sizes as part of the window decor layout.
72  *
73  * @hide
74  */
75 public class WindowDecorActionBar extends ActionBar implements
76         ActionBarOverlayLayout.ActionBarVisibilityCallback {
77     private static final String TAG = "WindowDecorActionBar";
78 
79     private static final Interpolator sHideInterpolator = new AccelerateInterpolator();
80     private static final Interpolator sShowInterpolator = new DecelerateInterpolator();
81 
82     /**
83      * Only allow show/hide animations on ICS+, as that is what ViewPropertyAnimatorCompat supports
84      */
85     private static final boolean ALLOW_SHOW_HIDE_ANIMATIONS = Build.VERSION.SDK_INT >= 14;
86 
87     private Context mContext;
88     private Context mThemedContext;
89     private Activity mActivity;
90     private Dialog mDialog;
91 
92     private ActionBarOverlayLayout mOverlayLayout;
93     private ActionBarContainer mContainerView;
94     private DecorToolbar mDecorToolbar;
95     private ActionBarContextView mContextView;
96     private View mContentView;
97     private ScrollingTabContainerView mTabScrollView;
98 
99     private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
100 
101     private TabImpl mSelectedTab;
102     private int mSavedTabPosition = INVALID_POSITION;
103 
104     private boolean mDisplayHomeAsUpSet;
105 
106     ActionModeImpl mActionMode;
107     ActionMode mDeferredDestroyActionMode;
108     ActionMode.Callback mDeferredModeDestroyCallback;
109 
110     private boolean mLastMenuVisibility;
111     private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners =
112             new ArrayList<OnMenuVisibilityListener>();
113 
114     private static final int INVALID_POSITION = -1;
115 
116     // The fade duration for toolbar and action bar when entering/exiting action mode.
117     private static final long FADE_OUT_DURATION_MS = 100;
118     private static final long FADE_IN_DURATION_MS = 200;
119 
120     private boolean mHasEmbeddedTabs;
121 
122     private int mCurWindowVisibility = View.VISIBLE;
123 
124     private boolean mContentAnimations = true;
125     private boolean mHiddenByApp;
126     private boolean mHiddenBySystem;
127     private boolean mShowingForMode;
128 
129     private boolean mNowShowing = true;
130 
131     private ViewPropertyAnimatorCompatSet mCurrentShowAnim;
132     private boolean mShowHideAnimationEnabled;
133     boolean mHideOnContentScroll;
134 
135     private TintManager mTintManager;
136 
137     final ViewPropertyAnimatorListener mHideListener = new ViewPropertyAnimatorListenerAdapter() {
138         @Override
139         public void onAnimationEnd(View view) {
140             if (mContentAnimations && mContentView != null) {
141                 ViewCompat.setTranslationY(mContentView, 0f);
142                 ViewCompat.setTranslationY(mContainerView, 0f);
143             }
144             mContainerView.setVisibility(View.GONE);
145             mContainerView.setTransitioning(false);
146             mCurrentShowAnim = null;
147             completeDeferredDestroyActionMode();
148             if (mOverlayLayout != null) {
149                 ViewCompat.requestApplyInsets(mOverlayLayout);
150             }
151         }
152     };
153 
154     final ViewPropertyAnimatorListener mShowListener = new ViewPropertyAnimatorListenerAdapter() {
155         @Override
156         public void onAnimationEnd(View view) {
157             mCurrentShowAnim = null;
158             mContainerView.requestLayout();
159         }
160     };
161 
162     final ViewPropertyAnimatorUpdateListener mUpdateListener =
163             new ViewPropertyAnimatorUpdateListener() {
164                 @Override
165                 public void onAnimationUpdate(View view) {
166                     final ViewParent parent = mContainerView.getParent();
167                     ((View) parent).invalidate();
168                 }
169             };
170 
WindowDecorActionBar(Activity activity, boolean overlayMode)171     public WindowDecorActionBar(Activity activity, boolean overlayMode) {
172         mActivity = activity;
173         Window window = activity.getWindow();
174         View decor = window.getDecorView();
175         init(decor);
176         if (!overlayMode) {
177             mContentView = decor.findViewById(android.R.id.content);
178         }
179     }
180 
WindowDecorActionBar(Dialog dialog)181     public WindowDecorActionBar(Dialog dialog) {
182         mDialog = dialog;
183         init(dialog.getWindow().getDecorView());
184     }
185 
186     /**
187      * Only for edit mode.
188      * @hide
189      */
WindowDecorActionBar(View layout)190     public WindowDecorActionBar(View layout) {
191         assert layout.isInEditMode();
192         init(layout);
193     }
194 
init(View decor)195     private void init(View decor) {
196         mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById(R.id.decor_content_parent);
197         if (mOverlayLayout != null) {
198             mOverlayLayout.setActionBarVisibilityCallback(this);
199         }
200         mDecorToolbar = getDecorToolbar(decor.findViewById(R.id.action_bar));
201         mContextView = (ActionBarContextView) decor.findViewById(
202                 R.id.action_context_bar);
203         mContainerView = (ActionBarContainer) decor.findViewById(
204                 R.id.action_bar_container);
205 
206         if (mDecorToolbar == null || mContextView == null || mContainerView == null) {
207             throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
208                     "with a compatible window decor layout");
209         }
210 
211         mContext = mDecorToolbar.getContext();
212 
213         // This was initially read from the action bar style
214         final int current = mDecorToolbar.getDisplayOptions();
215         final boolean homeAsUp = (current & DISPLAY_HOME_AS_UP) != 0;
216         if (homeAsUp) {
217             mDisplayHomeAsUpSet = true;
218         }
219 
220         ActionBarPolicy abp = ActionBarPolicy.get(mContext);
221         setHomeButtonEnabled(abp.enableHomeButtonByDefault() || homeAsUp);
222         setHasEmbeddedTabs(abp.hasEmbeddedTabs());
223 
224         final TypedArray a = mContext.obtainStyledAttributes(null,
225                 R.styleable.ActionBar,
226                 R.attr.actionBarStyle, 0);
227         if (a.getBoolean(R.styleable.ActionBar_hideOnContentScroll, false)) {
228             setHideOnContentScrollEnabled(true);
229         }
230         final int elevation = a.getDimensionPixelSize(R.styleable.ActionBar_elevation, 0);
231         if (elevation != 0) {
232             setElevation(elevation);
233         }
234         a.recycle();
235     }
236 
getDecorToolbar(View view)237     private DecorToolbar getDecorToolbar(View view) {
238         if (view instanceof DecorToolbar) {
239             return (DecorToolbar) view;
240         } else if (view instanceof Toolbar) {
241             return ((Toolbar) view).getWrapper();
242         } else {
243             throw new IllegalStateException("Can't make a decor toolbar out of " +
244                     view != null ? view.getClass().getSimpleName() : "null");
245         }
246     }
247 
248     @Override
setElevation(float elevation)249     public void setElevation(float elevation) {
250         ViewCompat.setElevation(mContainerView, elevation);
251     }
252 
253     @Override
getElevation()254     public float getElevation() {
255         return ViewCompat.getElevation(mContainerView);
256     }
257 
onConfigurationChanged(Configuration newConfig)258     public void onConfigurationChanged(Configuration newConfig) {
259         setHasEmbeddedTabs(ActionBarPolicy.get(mContext).hasEmbeddedTabs());
260     }
261 
setHasEmbeddedTabs(boolean hasEmbeddedTabs)262     private void setHasEmbeddedTabs(boolean hasEmbeddedTabs) {
263         mHasEmbeddedTabs = hasEmbeddedTabs;
264         // Switch tab layout configuration if needed
265         if (!mHasEmbeddedTabs) {
266             mDecorToolbar.setEmbeddedTabView(null);
267             mContainerView.setTabContainer(mTabScrollView);
268         } else {
269             mContainerView.setTabContainer(null);
270             mDecorToolbar.setEmbeddedTabView(mTabScrollView);
271         }
272         final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS;
273         if (mTabScrollView != null) {
274             if (isInTabMode) {
275                 mTabScrollView.setVisibility(View.VISIBLE);
276                 if (mOverlayLayout != null) {
277                     ViewCompat.requestApplyInsets(mOverlayLayout);
278                 }
279             } else {
280                 mTabScrollView.setVisibility(View.GONE);
281             }
282         }
283         mDecorToolbar.setCollapsible(!mHasEmbeddedTabs && isInTabMode);
284         mOverlayLayout.setHasNonEmbeddedTabs(!mHasEmbeddedTabs && isInTabMode);
285     }
286 
ensureTabsExist()287     private void ensureTabsExist() {
288         if (mTabScrollView != null) {
289             return;
290         }
291 
292         ScrollingTabContainerView tabScroller = new ScrollingTabContainerView(mContext);
293 
294         if (mHasEmbeddedTabs) {
295             tabScroller.setVisibility(View.VISIBLE);
296             mDecorToolbar.setEmbeddedTabView(tabScroller);
297         } else {
298             if (getNavigationMode() == NAVIGATION_MODE_TABS) {
299                 tabScroller.setVisibility(View.VISIBLE);
300                 if (mOverlayLayout != null) {
301                     ViewCompat.requestApplyInsets(mOverlayLayout);
302                 }
303             } else {
304                 tabScroller.setVisibility(View.GONE);
305             }
306             mContainerView.setTabContainer(tabScroller);
307         }
308         mTabScrollView = tabScroller;
309     }
310 
completeDeferredDestroyActionMode()311     void completeDeferredDestroyActionMode() {
312         if (mDeferredModeDestroyCallback != null) {
313             mDeferredModeDestroyCallback.onDestroyActionMode(mDeferredDestroyActionMode);
314             mDeferredDestroyActionMode = null;
315             mDeferredModeDestroyCallback = null;
316         }
317     }
318 
onWindowVisibilityChanged(int visibility)319     public void onWindowVisibilityChanged(int visibility) {
320         mCurWindowVisibility = visibility;
321     }
322 
323     /**
324      * Enables or disables animation between show/hide states.
325      * If animation is disabled using this method, animations in progress
326      * will be finished.
327      *
328      * @param enabled true to animate, false to not animate.
329      */
setShowHideAnimationEnabled(boolean enabled)330     public void setShowHideAnimationEnabled(boolean enabled) {
331         mShowHideAnimationEnabled = enabled;
332         if (!enabled && mCurrentShowAnim != null) {
333             mCurrentShowAnim.cancel();
334         }
335     }
336 
addOnMenuVisibilityListener(OnMenuVisibilityListener listener)337     public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
338         mMenuVisibilityListeners.add(listener);
339     }
340 
removeOnMenuVisibilityListener(OnMenuVisibilityListener listener)341     public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
342         mMenuVisibilityListeners.remove(listener);
343     }
344 
dispatchMenuVisibilityChanged(boolean isVisible)345     public void dispatchMenuVisibilityChanged(boolean isVisible) {
346         if (isVisible == mLastMenuVisibility) {
347             return;
348         }
349         mLastMenuVisibility = isVisible;
350 
351         final int count = mMenuVisibilityListeners.size();
352         for (int i = 0; i < count; i++) {
353             mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible);
354         }
355     }
356 
357     @Override
setCustomView(int resId)358     public void setCustomView(int resId) {
359         setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId,
360                 mDecorToolbar.getViewGroup(), false));
361     }
362 
363     @Override
setDisplayUseLogoEnabled(boolean useLogo)364     public void setDisplayUseLogoEnabled(boolean useLogo) {
365         setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO);
366     }
367 
368     @Override
setDisplayShowHomeEnabled(boolean showHome)369     public void setDisplayShowHomeEnabled(boolean showHome) {
370         setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME);
371     }
372 
373     @Override
setDisplayHomeAsUpEnabled(boolean showHomeAsUp)374     public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) {
375         setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP);
376     }
377 
378     @Override
setDisplayShowTitleEnabled(boolean showTitle)379     public void setDisplayShowTitleEnabled(boolean showTitle) {
380         setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE);
381     }
382 
383     @Override
setDisplayShowCustomEnabled(boolean showCustom)384     public void setDisplayShowCustomEnabled(boolean showCustom) {
385         setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM);
386     }
387 
388     @Override
setHomeButtonEnabled(boolean enable)389     public void setHomeButtonEnabled(boolean enable) {
390         mDecorToolbar.setHomeButtonEnabled(enable);
391     }
392 
393     @Override
setTitle(int resId)394     public void setTitle(int resId) {
395         setTitle(mContext.getString(resId));
396     }
397 
398     @Override
setSubtitle(int resId)399     public void setSubtitle(int resId) {
400         setSubtitle(mContext.getString(resId));
401     }
402 
setSelectedNavigationItem(int position)403     public void setSelectedNavigationItem(int position) {
404         switch (mDecorToolbar.getNavigationMode()) {
405             case NAVIGATION_MODE_TABS:
406                 selectTab(mTabs.get(position));
407                 break;
408             case NAVIGATION_MODE_LIST:
409                 mDecorToolbar.setDropdownSelectedPosition(position);
410                 break;
411             default:
412                 throw new IllegalStateException(
413                         "setSelectedNavigationIndex not valid for current navigation mode");
414         }
415     }
416 
removeAllTabs()417     public void removeAllTabs() {
418         cleanupTabs();
419     }
420 
cleanupTabs()421     private void cleanupTabs() {
422         if (mSelectedTab != null) {
423             selectTab(null);
424         }
425         mTabs.clear();
426         if (mTabScrollView != null) {
427             mTabScrollView.removeAllTabs();
428         }
429         mSavedTabPosition = INVALID_POSITION;
430     }
431 
setTitle(CharSequence title)432     public void setTitle(CharSequence title) {
433         mDecorToolbar.setTitle(title);
434     }
435 
436     @Override
setWindowTitle(CharSequence title)437     public void setWindowTitle(CharSequence title) {
438         mDecorToolbar.setWindowTitle(title);
439     }
440 
setSubtitle(CharSequence subtitle)441     public void setSubtitle(CharSequence subtitle) {
442         mDecorToolbar.setSubtitle(subtitle);
443     }
444 
setDisplayOptions(int options)445     public void setDisplayOptions(int options) {
446         if ((options & DISPLAY_HOME_AS_UP) != 0) {
447             mDisplayHomeAsUpSet = true;
448         }
449         mDecorToolbar.setDisplayOptions(options);
450     }
451 
setDisplayOptions(int options, int mask)452     public void setDisplayOptions(int options, int mask) {
453         final int current = mDecorToolbar.getDisplayOptions();
454         if ((mask & DISPLAY_HOME_AS_UP) != 0) {
455             mDisplayHomeAsUpSet = true;
456         }
457         mDecorToolbar.setDisplayOptions((options & mask) | (current & ~mask));
458     }
459 
setBackgroundDrawable(Drawable d)460     public void setBackgroundDrawable(Drawable d) {
461         mContainerView.setPrimaryBackground(d);
462     }
463 
setStackedBackgroundDrawable(Drawable d)464     public void setStackedBackgroundDrawable(Drawable d) {
465         mContainerView.setStackedBackground(d);
466     }
467 
setSplitBackgroundDrawable(Drawable d)468     public void setSplitBackgroundDrawable(Drawable d) {
469         // no-op. We don't support split action bars
470     }
471 
getCustomView()472     public View getCustomView() {
473         return mDecorToolbar.getCustomView();
474     }
475 
getTitle()476     public CharSequence getTitle() {
477         return mDecorToolbar.getTitle();
478     }
479 
getSubtitle()480     public CharSequence getSubtitle() {
481         return mDecorToolbar.getSubtitle();
482     }
483 
getNavigationMode()484     public int getNavigationMode() {
485         return mDecorToolbar.getNavigationMode();
486     }
487 
getDisplayOptions()488     public int getDisplayOptions() {
489         return mDecorToolbar.getDisplayOptions();
490     }
491 
startActionMode(ActionMode.Callback callback)492     public ActionMode startActionMode(ActionMode.Callback callback) {
493         if (mActionMode != null) {
494             mActionMode.finish();
495         }
496 
497         mOverlayLayout.setHideOnContentScrollEnabled(false);
498         mContextView.killMode();
499         ActionModeImpl mode = new ActionModeImpl(mContextView.getContext(), callback);
500         if (mode.dispatchOnCreate()) {
501             mode.invalidate();
502             mContextView.initForMode(mode);
503             animateToMode(true);
504             mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
505             mActionMode = mode;
506             return mode;
507         }
508         return null;
509     }
510 
configureTab(Tab tab, int position)511     private void configureTab(Tab tab, int position) {
512         final TabImpl tabi = (TabImpl) tab;
513         final ActionBar.TabListener callback = tabi.getCallback();
514 
515         if (callback == null) {
516             throw new IllegalStateException("Action Bar Tab must have a Callback");
517         }
518 
519         tabi.setPosition(position);
520         mTabs.add(position, tabi);
521 
522         final int count = mTabs.size();
523         for (int i = position + 1; i < count; i++) {
524             mTabs.get(i).setPosition(i);
525         }
526     }
527 
528     @Override
addTab(Tab tab)529     public void addTab(Tab tab) {
530         addTab(tab, mTabs.isEmpty());
531     }
532 
533     @Override
addTab(Tab tab, int position)534     public void addTab(Tab tab, int position) {
535         addTab(tab, position, mTabs.isEmpty());
536     }
537 
538     @Override
addTab(Tab tab, boolean setSelected)539     public void addTab(Tab tab, boolean setSelected) {
540         ensureTabsExist();
541         mTabScrollView.addTab(tab, setSelected);
542         configureTab(tab, mTabs.size());
543         if (setSelected) {
544             selectTab(tab);
545         }
546     }
547 
548     @Override
addTab(Tab tab, int position, boolean setSelected)549     public void addTab(Tab tab, int position, boolean setSelected) {
550         ensureTabsExist();
551         mTabScrollView.addTab(tab, position, setSelected);
552         configureTab(tab, position);
553         if (setSelected) {
554             selectTab(tab);
555         }
556     }
557 
558     @Override
newTab()559     public Tab newTab() {
560         return new TabImpl();
561     }
562 
563     @Override
removeTab(Tab tab)564     public void removeTab(Tab tab) {
565         removeTabAt(tab.getPosition());
566     }
567 
568     @Override
removeTabAt(int position)569     public void removeTabAt(int position) {
570         if (mTabScrollView == null) {
571             // No tabs around to remove
572             return;
573         }
574 
575         int selectedTabPosition = mSelectedTab != null
576                 ? mSelectedTab.getPosition() : mSavedTabPosition;
577         mTabScrollView.removeTabAt(position);
578         TabImpl removedTab = mTabs.remove(position);
579         if (removedTab != null) {
580             removedTab.setPosition(-1);
581         }
582 
583         final int newTabCount = mTabs.size();
584         for (int i = position; i < newTabCount; i++) {
585             mTabs.get(i).setPosition(i);
586         }
587 
588         if (selectedTabPosition == position) {
589             selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
590         }
591     }
592 
593     @Override
selectTab(Tab tab)594     public void selectTab(Tab tab) {
595         if (getNavigationMode() != NAVIGATION_MODE_TABS) {
596             mSavedTabPosition = tab != null ? tab.getPosition() : INVALID_POSITION;
597             return;
598         }
599 
600         final FragmentTransaction trans;
601         if (mActivity instanceof FragmentActivity && !mDecorToolbar.getViewGroup().isInEditMode()) {
602             // If we're not in edit mode and our Activity is a FragmentActivity, start a tx
603             trans = ((FragmentActivity) mActivity).getSupportFragmentManager()
604                     .beginTransaction().disallowAddToBackStack();
605         } else {
606             trans = null;
607         }
608 
609         if (mSelectedTab == tab) {
610             if (mSelectedTab != null) {
611                 mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans);
612                 mTabScrollView.animateToTab(tab.getPosition());
613             }
614         } else {
615             mTabScrollView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
616             if (mSelectedTab != null) {
617                 mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans);
618             }
619             mSelectedTab = (TabImpl) tab;
620             if (mSelectedTab != null) {
621                 mSelectedTab.getCallback().onTabSelected(mSelectedTab, trans);
622             }
623         }
624 
625         if (trans != null && !trans.isEmpty()) {
626             trans.commit();
627         }
628     }
629 
630     @Override
getSelectedTab()631     public Tab getSelectedTab() {
632         return mSelectedTab;
633     }
634 
635     @Override
getHeight()636     public int getHeight() {
637         return mContainerView.getHeight();
638     }
639 
enableContentAnimations(boolean enabled)640     public void enableContentAnimations(boolean enabled) {
641         mContentAnimations = enabled;
642     }
643 
644     @Override
show()645     public void show() {
646         if (mHiddenByApp) {
647             mHiddenByApp = false;
648             updateVisibility(false);
649         }
650     }
651 
showForActionMode()652     private void showForActionMode() {
653         if (!mShowingForMode) {
654             mShowingForMode = true;
655             if (mOverlayLayout != null) {
656                 mOverlayLayout.setShowingForActionMode(true);
657             }
658             updateVisibility(false);
659         }
660     }
661 
showForSystem()662     public void showForSystem() {
663         if (mHiddenBySystem) {
664             mHiddenBySystem = false;
665             updateVisibility(true);
666         }
667     }
668 
669     @Override
hide()670     public void hide() {
671         if (!mHiddenByApp) {
672             mHiddenByApp = true;
673             updateVisibility(false);
674         }
675     }
676 
hideForActionMode()677     private void hideForActionMode() {
678         if (mShowingForMode) {
679             mShowingForMode = false;
680             if (mOverlayLayout != null) {
681                 mOverlayLayout.setShowingForActionMode(false);
682             }
683             updateVisibility(false);
684         }
685     }
686 
hideForSystem()687     public void hideForSystem() {
688         if (!mHiddenBySystem) {
689             mHiddenBySystem = true;
690             updateVisibility(true);
691         }
692     }
693 
694     @Override
setHideOnContentScrollEnabled(boolean hideOnContentScroll)695     public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) {
696         if (hideOnContentScroll && !mOverlayLayout.isInOverlayMode()) {
697             throw new IllegalStateException("Action bar must be in overlay mode " +
698                     "(Window.FEATURE_OVERLAY_ACTION_BAR) to enable hide on content scroll");
699         }
700         mHideOnContentScroll = hideOnContentScroll;
701         mOverlayLayout.setHideOnContentScrollEnabled(hideOnContentScroll);
702     }
703 
704     @Override
isHideOnContentScrollEnabled()705     public boolean isHideOnContentScrollEnabled() {
706         return mOverlayLayout.isHideOnContentScrollEnabled();
707     }
708 
709     @Override
getHideOffset()710     public int getHideOffset() {
711         return mOverlayLayout.getActionBarHideOffset();
712     }
713 
714     @Override
setHideOffset(int offset)715     public void setHideOffset(int offset) {
716         if (offset != 0 && !mOverlayLayout.isInOverlayMode()) {
717             throw new IllegalStateException("Action bar must be in overlay mode " +
718                     "(Window.FEATURE_OVERLAY_ACTION_BAR) to set a non-zero hide offset");
719         }
720         mOverlayLayout.setActionBarHideOffset(offset);
721     }
722 
checkShowingFlags(boolean hiddenByApp, boolean hiddenBySystem, boolean showingForMode)723     private static boolean checkShowingFlags(boolean hiddenByApp, boolean hiddenBySystem,
724             boolean showingForMode) {
725         if (showingForMode) {
726             return true;
727         } else if (hiddenByApp || hiddenBySystem) {
728             return false;
729         } else {
730             return true;
731         }
732     }
733 
updateVisibility(boolean fromSystem)734     private void updateVisibility(boolean fromSystem) {
735         // Based on the current state, should we be hidden or shown?
736         final boolean shown = checkShowingFlags(mHiddenByApp, mHiddenBySystem,
737                 mShowingForMode);
738 
739         if (shown) {
740             if (!mNowShowing) {
741                 mNowShowing = true;
742                 doShow(fromSystem);
743             }
744         } else {
745             if (mNowShowing) {
746                 mNowShowing = false;
747                 doHide(fromSystem);
748             }
749         }
750     }
751 
doShow(boolean fromSystem)752     public void doShow(boolean fromSystem) {
753         if (mCurrentShowAnim != null) {
754             mCurrentShowAnim.cancel();
755         }
756         mContainerView.setVisibility(View.VISIBLE);
757 
758         if (mCurWindowVisibility == View.VISIBLE && ALLOW_SHOW_HIDE_ANIMATIONS &&
759                 (mShowHideAnimationEnabled || fromSystem)) {
760             // because we're about to ask its window loc
761             ViewCompat.setTranslationY(mContainerView, 0f);
762             float startingY = -mContainerView.getHeight();
763             if (fromSystem) {
764                 int topLeft[] = {0, 0};
765                 mContainerView.getLocationInWindow(topLeft);
766                 startingY -= topLeft[1];
767             }
768             ViewCompat.setTranslationY(mContainerView, startingY);
769             ViewPropertyAnimatorCompatSet anim = new ViewPropertyAnimatorCompatSet();
770             ViewPropertyAnimatorCompat a = ViewCompat.animate(mContainerView).translationY(0f);
771             a.setUpdateListener(mUpdateListener);
772             anim.play(a);
773             if (mContentAnimations && mContentView != null) {
774                 ViewCompat.setTranslationY(mContentView, startingY);
775                 anim.play(ViewCompat.animate(mContentView).translationY(0f));
776             }
777             anim.setInterpolator(sShowInterpolator);
778             anim.setDuration(250);
779             // If this is being shown from the system, add a small delay.
780             // This is because we will also be animating in the status bar,
781             // and these two elements can't be done in lock-step.  So we give
782             // a little time for the status bar to start its animation before
783             // the action bar animates.  (This corresponds to the corresponding
784             // case when hiding, where the status bar has a small delay before
785             // starting.)
786             anim.setListener(mShowListener);
787             mCurrentShowAnim = anim;
788             anim.start();
789         } else {
790             ViewCompat.setAlpha(mContainerView, 1f);
791             ViewCompat.setTranslationY(mContainerView, 0);
792             if (mContentAnimations && mContentView != null) {
793                 ViewCompat.setTranslationY(mContentView, 0);
794             }
795             mShowListener.onAnimationEnd(null);
796         }
797         if (mOverlayLayout != null) {
798             ViewCompat.requestApplyInsets(mOverlayLayout);
799         }
800     }
801 
doHide(boolean fromSystem)802     public void doHide(boolean fromSystem) {
803         if (mCurrentShowAnim != null) {
804             mCurrentShowAnim.cancel();
805         }
806 
807         if (mCurWindowVisibility == View.VISIBLE && ALLOW_SHOW_HIDE_ANIMATIONS &&
808                 (mShowHideAnimationEnabled || fromSystem)) {
809             ViewCompat.setAlpha(mContainerView, 1f);
810             mContainerView.setTransitioning(true);
811             ViewPropertyAnimatorCompatSet anim = new ViewPropertyAnimatorCompatSet();
812             float endingY = -mContainerView.getHeight();
813             if (fromSystem) {
814                 int topLeft[] = {0, 0};
815                 mContainerView.getLocationInWindow(topLeft);
816                 endingY -= topLeft[1];
817             }
818             ViewPropertyAnimatorCompat a = ViewCompat.animate(mContainerView).translationY(endingY);
819             a.setUpdateListener(mUpdateListener);
820             anim.play(a);
821             if (mContentAnimations && mContentView != null) {
822                 anim.play(ViewCompat.animate(mContentView).translationY(endingY));
823             }
824             anim.setInterpolator(sHideInterpolator);
825             anim.setDuration(250);
826             anim.setListener(mHideListener);
827             mCurrentShowAnim = anim;
828             anim.start();
829         } else {
830             mHideListener.onAnimationEnd(null);
831         }
832     }
833 
isShowing()834     public boolean isShowing() {
835         final int height = getHeight();
836         // Take into account the case where the bar has a 0 height due to not being measured yet.
837         return mNowShowing && (height == 0 || getHideOffset() < height);
838     }
839 
animateToMode(boolean toActionMode)840     public void animateToMode(boolean toActionMode) {
841         if (toActionMode) {
842             showForActionMode();
843         } else {
844             hideForActionMode();
845         }
846 
847         ViewPropertyAnimatorCompat fadeIn, fadeOut;
848         if (toActionMode) {
849             fadeOut = mDecorToolbar.setupAnimatorToVisibility(View.GONE,
850                     FADE_OUT_DURATION_MS);
851             fadeIn = mContextView.setupAnimatorToVisibility(View.VISIBLE,
852                     FADE_IN_DURATION_MS);
853         } else {
854             fadeIn = mDecorToolbar.setupAnimatorToVisibility(View.VISIBLE,
855                     FADE_IN_DURATION_MS);
856             fadeOut = mContextView.setupAnimatorToVisibility(View.GONE,
857                     FADE_OUT_DURATION_MS);
858         }
859         ViewPropertyAnimatorCompatSet set = new ViewPropertyAnimatorCompatSet();
860         set.playSequentially(fadeOut, fadeIn);
861         set.start();
862         // mTabScrollView's visibility is not affected by action mode.
863     }
864 
getThemedContext()865     public Context getThemedContext() {
866         if (mThemedContext == null) {
867             TypedValue outValue = new TypedValue();
868             Resources.Theme currentTheme = mContext.getTheme();
869             currentTheme.resolveAttribute(R.attr.actionBarWidgetTheme, outValue, true);
870             final int targetThemeRes = outValue.resourceId;
871 
872             if (targetThemeRes != 0) {
873                 mThemedContext = new ContextThemeWrapper(mContext, targetThemeRes);
874             } else {
875                 mThemedContext = mContext;
876             }
877         }
878         return mThemedContext;
879     }
880 
881     @Override
isTitleTruncated()882     public boolean isTitleTruncated() {
883         return mDecorToolbar != null && mDecorToolbar.isTitleTruncated();
884     }
885 
886     @Override
setHomeAsUpIndicator(Drawable indicator)887     public void setHomeAsUpIndicator(Drawable indicator) {
888         mDecorToolbar.setNavigationIcon(indicator);
889     }
890 
891     @Override
setHomeAsUpIndicator(int resId)892     public void setHomeAsUpIndicator(int resId) {
893         mDecorToolbar.setNavigationIcon(resId);
894     }
895 
896     @Override
setHomeActionContentDescription(CharSequence description)897     public void setHomeActionContentDescription(CharSequence description) {
898         mDecorToolbar.setNavigationContentDescription(description);
899     }
900 
901     @Override
setHomeActionContentDescription(int resId)902     public void setHomeActionContentDescription(int resId) {
903         mDecorToolbar.setNavigationContentDescription(resId);
904     }
905 
906     @Override
onContentScrollStarted()907     public void onContentScrollStarted() {
908         if (mCurrentShowAnim != null) {
909             mCurrentShowAnim.cancel();
910             mCurrentShowAnim = null;
911         }
912     }
913 
914     @Override
onContentScrollStopped()915     public void onContentScrollStopped() {
916     }
917 
918     @Override
collapseActionView()919     public boolean collapseActionView() {
920         if (mDecorToolbar != null && mDecorToolbar.hasExpandedActionView()) {
921             mDecorToolbar.collapseActionView();
922             return true;
923         }
924         return false;
925     }
926 
927     /**
928      * @hide
929      */
930     public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback {
931         private final Context mActionModeContext;
932         private final MenuBuilder mMenu;
933 
934         private ActionMode.Callback mCallback;
935         private WeakReference<View> mCustomView;
936 
ActionModeImpl(Context context, ActionMode.Callback callback)937         public ActionModeImpl(Context context, ActionMode.Callback callback) {
938             mActionModeContext = context;
939             mCallback = callback;
940             mMenu = new MenuBuilder(context)
941                     .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
942             mMenu.setCallback(this);
943         }
944 
945         @Override
getMenuInflater()946         public MenuInflater getMenuInflater() {
947             return new SupportMenuInflater(mActionModeContext);
948         }
949 
950         @Override
getMenu()951         public Menu getMenu() {
952             return mMenu;
953         }
954 
955         @Override
finish()956         public void finish() {
957             if (mActionMode != this) {
958                 // Not the active action mode - no-op
959                 return;
960             }
961 
962             // If this change in state is going to cause the action bar
963             // to be hidden, defer the onDestroy callback until the animation
964             // is finished and associated relayout is about to happen. This lets
965             // apps better anticipate visibility and layout behavior.
966             if (!checkShowingFlags(mHiddenByApp, mHiddenBySystem, false)) {
967                 // With the current state but the action bar hidden, our
968                 // overall showing state is going to be false.
969                 mDeferredDestroyActionMode = this;
970                 mDeferredModeDestroyCallback = mCallback;
971             } else {
972                 mCallback.onDestroyActionMode(this);
973             }
974             mCallback = null;
975             animateToMode(false);
976 
977             // Clear out the context mode views after the animation finishes
978             mContextView.closeMode();
979             mDecorToolbar.getViewGroup().sendAccessibilityEvent(
980                     AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
981             mOverlayLayout.setHideOnContentScrollEnabled(mHideOnContentScroll);
982 
983             mActionMode = null;
984         }
985 
986         @Override
invalidate()987         public void invalidate() {
988             if (mActionMode != this) {
989                 // Not the active action mode - no-op. It's possible we are
990                 // currently deferring onDestroy, so the app doesn't yet know we
991                 // are going away and is trying to use us. That's also a no-op.
992                 return;
993             }
994 
995             mMenu.stopDispatchingItemsChanged();
996             try {
997                 mCallback.onPrepareActionMode(this, mMenu);
998             } finally {
999                 mMenu.startDispatchingItemsChanged();
1000             }
1001         }
1002 
dispatchOnCreate()1003         public boolean dispatchOnCreate() {
1004             mMenu.stopDispatchingItemsChanged();
1005             try {
1006                 return mCallback.onCreateActionMode(this, mMenu);
1007             } finally {
1008                 mMenu.startDispatchingItemsChanged();
1009             }
1010         }
1011 
1012         @Override
setCustomView(View view)1013         public void setCustomView(View view) {
1014             mContextView.setCustomView(view);
1015             mCustomView = new WeakReference<View>(view);
1016         }
1017 
1018         @Override
setSubtitle(CharSequence subtitle)1019         public void setSubtitle(CharSequence subtitle) {
1020             mContextView.setSubtitle(subtitle);
1021         }
1022 
1023         @Override
setTitle(CharSequence title)1024         public void setTitle(CharSequence title) {
1025             mContextView.setTitle(title);
1026         }
1027 
1028         @Override
setTitle(int resId)1029         public void setTitle(int resId) {
1030             setTitle(mContext.getResources().getString(resId));
1031         }
1032 
1033         @Override
setSubtitle(int resId)1034         public void setSubtitle(int resId) {
1035             setSubtitle(mContext.getResources().getString(resId));
1036         }
1037 
1038         @Override
getTitle()1039         public CharSequence getTitle() {
1040             return mContextView.getTitle();
1041         }
1042 
1043         @Override
getSubtitle()1044         public CharSequence getSubtitle() {
1045             return mContextView.getSubtitle();
1046         }
1047 
1048         @Override
setTitleOptionalHint(boolean titleOptional)1049         public void setTitleOptionalHint(boolean titleOptional) {
1050             super.setTitleOptionalHint(titleOptional);
1051             mContextView.setTitleOptional(titleOptional);
1052         }
1053 
1054         @Override
isTitleOptional()1055         public boolean isTitleOptional() {
1056             return mContextView.isTitleOptional();
1057         }
1058 
1059         @Override
getCustomView()1060         public View getCustomView() {
1061             return mCustomView != null ? mCustomView.get() : null;
1062         }
1063 
onMenuItemSelected(MenuBuilder menu, MenuItem item)1064         public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
1065             if (mCallback != null) {
1066                 return mCallback.onActionItemClicked(this, item);
1067             } else {
1068                 return false;
1069             }
1070         }
1071 
onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing)1072         public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
1073         }
1074 
onSubMenuSelected(SubMenuBuilder subMenu)1075         public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
1076             if (mCallback == null) {
1077                 return false;
1078             }
1079 
1080             if (!subMenu.hasVisibleItems()) {
1081                 return true;
1082             }
1083 
1084             new MenuPopupHelper(getThemedContext(), subMenu).show();
1085             return true;
1086         }
1087 
onCloseSubMenu(SubMenuBuilder menu)1088         public void onCloseSubMenu(SubMenuBuilder menu) {
1089         }
1090 
onMenuModeChange(MenuBuilder menu)1091         public void onMenuModeChange(MenuBuilder menu) {
1092             if (mCallback == null) {
1093                 return;
1094             }
1095             invalidate();
1096             mContextView.showOverflowMenu();
1097         }
1098     }
1099 
1100     /**
1101      * @hide
1102      */
1103     public class TabImpl extends ActionBar.Tab {
1104         private ActionBar.TabListener mCallback;
1105         private Object mTag;
1106         private Drawable mIcon;
1107         private CharSequence mText;
1108         private CharSequence mContentDesc;
1109         private int mPosition = -1;
1110         private View mCustomView;
1111 
1112         @Override
getTag()1113         public Object getTag() {
1114             return mTag;
1115         }
1116 
1117         @Override
setTag(Object tag)1118         public Tab setTag(Object tag) {
1119             mTag = tag;
1120             return this;
1121         }
1122 
getCallback()1123         public ActionBar.TabListener getCallback() {
1124             return mCallback;
1125         }
1126 
1127         @Override
setTabListener(ActionBar.TabListener callback)1128         public Tab setTabListener(ActionBar.TabListener callback) {
1129             mCallback = callback;
1130             return this;
1131         }
1132 
1133         @Override
getCustomView()1134         public View getCustomView() {
1135             return mCustomView;
1136         }
1137 
1138         @Override
setCustomView(View view)1139         public Tab setCustomView(View view) {
1140             mCustomView = view;
1141             if (mPosition >= 0) {
1142                 mTabScrollView.updateTab(mPosition);
1143             }
1144             return this;
1145         }
1146 
1147         @Override
setCustomView(int layoutResId)1148         public Tab setCustomView(int layoutResId) {
1149             return setCustomView(LayoutInflater.from(getThemedContext())
1150                     .inflate(layoutResId, null));
1151         }
1152 
1153         @Override
getIcon()1154         public Drawable getIcon() {
1155             return mIcon;
1156         }
1157 
1158         @Override
getPosition()1159         public int getPosition() {
1160             return mPosition;
1161         }
1162 
setPosition(int position)1163         public void setPosition(int position) {
1164             mPosition = position;
1165         }
1166 
1167         @Override
getText()1168         public CharSequence getText() {
1169             return mText;
1170         }
1171 
1172         @Override
setIcon(Drawable icon)1173         public Tab setIcon(Drawable icon) {
1174             mIcon = icon;
1175             if (mPosition >= 0) {
1176                 mTabScrollView.updateTab(mPosition);
1177             }
1178             return this;
1179         }
1180 
1181         @Override
setIcon(int resId)1182         public Tab setIcon(int resId) {
1183             return setIcon(getTintManager().getDrawable(resId));
1184         }
1185 
1186         @Override
setText(CharSequence text)1187         public Tab setText(CharSequence text) {
1188             mText = text;
1189             if (mPosition >= 0) {
1190                 mTabScrollView.updateTab(mPosition);
1191             }
1192             return this;
1193         }
1194 
1195         @Override
setText(int resId)1196         public Tab setText(int resId) {
1197             return setText(mContext.getResources().getText(resId));
1198         }
1199 
1200         @Override
select()1201         public void select() {
1202             selectTab(this);
1203         }
1204 
1205         @Override
setContentDescription(int resId)1206         public Tab setContentDescription(int resId) {
1207             return setContentDescription(mContext.getResources().getText(resId));
1208         }
1209 
1210         @Override
setContentDescription(CharSequence contentDesc)1211         public Tab setContentDescription(CharSequence contentDesc) {
1212             mContentDesc = contentDesc;
1213             if (mPosition >= 0) {
1214                 mTabScrollView.updateTab(mPosition);
1215             }
1216             return this;
1217         }
1218 
1219         @Override
getContentDescription()1220         public CharSequence getContentDescription() {
1221             return mContentDesc;
1222         }
1223     }
1224 
1225     @Override
setCustomView(View view)1226     public void setCustomView(View view) {
1227         mDecorToolbar.setCustomView(view);
1228     }
1229 
1230     @Override
setCustomView(View view, LayoutParams layoutParams)1231     public void setCustomView(View view, LayoutParams layoutParams) {
1232         view.setLayoutParams(layoutParams);
1233         mDecorToolbar.setCustomView(view);
1234     }
1235 
1236     @Override
setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback)1237     public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
1238         mDecorToolbar.setDropdownParams(adapter, new NavItemSelectedListener(callback));
1239     }
1240 
1241     @Override
getSelectedNavigationIndex()1242     public int getSelectedNavigationIndex() {
1243         switch (mDecorToolbar.getNavigationMode()) {
1244             case NAVIGATION_MODE_TABS:
1245                 return mSelectedTab != null ? mSelectedTab.getPosition() : -1;
1246             case NAVIGATION_MODE_LIST:
1247                 return mDecorToolbar.getDropdownSelectedPosition();
1248             default:
1249                 return -1;
1250         }
1251     }
1252 
1253     @Override
getNavigationItemCount()1254     public int getNavigationItemCount() {
1255         switch (mDecorToolbar.getNavigationMode()) {
1256             case NAVIGATION_MODE_TABS:
1257                 return mTabs.size();
1258             case NAVIGATION_MODE_LIST:
1259                 return mDecorToolbar.getDropdownItemCount();
1260             default:
1261                 return 0;
1262         }
1263     }
1264 
1265     @Override
getTabCount()1266     public int getTabCount() {
1267         return mTabs.size();
1268     }
1269 
1270     @Override
setNavigationMode(int mode)1271     public void setNavigationMode(int mode) {
1272         final int oldMode = mDecorToolbar.getNavigationMode();
1273         switch (oldMode) {
1274             case NAVIGATION_MODE_TABS:
1275                 mSavedTabPosition = getSelectedNavigationIndex();
1276                 selectTab(null);
1277                 mTabScrollView.setVisibility(View.GONE);
1278                 break;
1279         }
1280         if (oldMode != mode && !mHasEmbeddedTabs) {
1281             if (mOverlayLayout != null) {
1282                 ViewCompat.requestApplyInsets(mOverlayLayout);
1283             }
1284         }
1285         mDecorToolbar.setNavigationMode(mode);
1286         switch (mode) {
1287             case NAVIGATION_MODE_TABS:
1288                 ensureTabsExist();
1289                 mTabScrollView.setVisibility(View.VISIBLE);
1290                 if (mSavedTabPosition != INVALID_POSITION) {
1291                     setSelectedNavigationItem(mSavedTabPosition);
1292                     mSavedTabPosition = INVALID_POSITION;
1293                 }
1294                 break;
1295         }
1296         mDecorToolbar.setCollapsible(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
1297         mOverlayLayout.setHasNonEmbeddedTabs(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
1298     }
1299 
1300     @Override
getTabAt(int index)1301     public Tab getTabAt(int index) {
1302         return mTabs.get(index);
1303     }
1304 
1305 
1306     @Override
setIcon(int resId)1307     public void setIcon(int resId) {
1308         mDecorToolbar.setIcon(resId);
1309     }
1310 
1311     @Override
setIcon(Drawable icon)1312     public void setIcon(Drawable icon) {
1313         mDecorToolbar.setIcon(icon);
1314     }
1315 
hasIcon()1316     public boolean hasIcon() {
1317         return mDecorToolbar.hasIcon();
1318     }
1319 
1320     @Override
setLogo(int resId)1321     public void setLogo(int resId) {
1322         mDecorToolbar.setLogo(resId);
1323     }
1324 
1325     @Override
setLogo(Drawable logo)1326     public void setLogo(Drawable logo) {
1327         mDecorToolbar.setLogo(logo);
1328     }
1329 
hasLogo()1330     public boolean hasLogo() {
1331         return mDecorToolbar.hasLogo();
1332     }
1333 
setDefaultDisplayHomeAsUpEnabled(boolean enable)1334     public void setDefaultDisplayHomeAsUpEnabled(boolean enable) {
1335         if (!mDisplayHomeAsUpSet) {
1336             setDisplayHomeAsUpEnabled(enable);
1337         }
1338     }
1339 
getTintManager()1340     TintManager getTintManager() {
1341         if (mTintManager == null) {
1342             mTintManager = TintManager.get(mContext);
1343         }
1344         return mTintManager;
1345     }
1346 
1347 }
1348