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