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