d022a24659d20e6565e86633dd36a34fa7730a7d
[pub/Android/ownCloud.git] / actionbarsherlock / src / com / actionbarsherlock / internal / app / ActionBarImpl.java
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.actionbarsherlock.internal.app;
18
19 import java.lang.ref.WeakReference;
20 import java.util.ArrayList;
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.graphics.drawable.Drawable;
27 import android.os.Build;
28 import android.os.Handler;
29 import android.support.v4.app.FragmentActivity;
30 import android.support.v4.app.FragmentTransaction;
31 import android.util.TypedValue;
32 import android.view.ContextThemeWrapper;
33 import android.view.LayoutInflater;
34 import android.view.View;
35 import android.view.Window;
36 import android.view.accessibility.AccessibilityEvent;
37 import android.widget.SpinnerAdapter;
38 import com.actionbarsherlock.R;
39 import com.actionbarsherlock.app.ActionBar;
40 import com.actionbarsherlock.internal.nineoldandroids.animation.Animator;
41 import com.actionbarsherlock.internal.nineoldandroids.animation.AnimatorListenerAdapter;
42 import com.actionbarsherlock.internal.nineoldandroids.animation.AnimatorSet;
43 import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator;
44 import com.actionbarsherlock.internal.nineoldandroids.animation.Animator.AnimatorListener;
45 import com.actionbarsherlock.internal.nineoldandroids.widget.NineFrameLayout;
46 import com.actionbarsherlock.internal.view.menu.MenuBuilder;
47 import com.actionbarsherlock.internal.view.menu.MenuPopupHelper;
48 import com.actionbarsherlock.internal.view.menu.SubMenuBuilder;
49 import com.actionbarsherlock.internal.widget.ActionBarContainer;
50 import com.actionbarsherlock.internal.widget.ActionBarContextView;
51 import com.actionbarsherlock.internal.widget.ActionBarView;
52 import com.actionbarsherlock.internal.widget.ScrollingTabContainerView;
53 import com.actionbarsherlock.view.ActionMode;
54 import com.actionbarsherlock.view.Menu;
55 import com.actionbarsherlock.view.MenuInflater;
56 import com.actionbarsherlock.view.MenuItem;
57 import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean;
58
59 /**
60 * ActionBarImpl is the ActionBar implementation used
61 * by devices of all screen sizes. If it detects a compatible decor,
62 * it will split contextual modes across both the ActionBarView at
63 * the top of the screen and a horizontal LinearLayout at the bottom
64 * which is normally hidden.
65 */
66 public class ActionBarImpl extends ActionBar {
67 //UNUSED private static final String TAG = "ActionBarImpl";
68
69 private Context mContext;
70 private Context mThemedContext;
71 private Activity mActivity;
72 //UNUSED private Dialog mDialog;
73
74 private ActionBarContainer mContainerView;
75 private ActionBarView mActionView;
76 private ActionBarContextView mContextView;
77 private ActionBarContainer mSplitView;
78 private NineFrameLayout mContentView;
79 private ScrollingTabContainerView mTabScrollView;
80
81 private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
82
83 private TabImpl mSelectedTab;
84 private int mSavedTabPosition = INVALID_POSITION;
85
86 ActionModeImpl mActionMode;
87 ActionMode mDeferredDestroyActionMode;
88 ActionMode.Callback mDeferredModeDestroyCallback;
89
90 private boolean mLastMenuVisibility;
91 private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners =
92 new ArrayList<OnMenuVisibilityListener>();
93
94 private static final int CONTEXT_DISPLAY_NORMAL = 0;
95 private static final int CONTEXT_DISPLAY_SPLIT = 1;
96
97 private static final int INVALID_POSITION = -1;
98
99 private int mContextDisplayMode;
100 private boolean mHasEmbeddedTabs;
101
102 final Handler mHandler = new Handler();
103 Runnable mTabSelector;
104
105 private Animator mCurrentShowAnim;
106 private Animator mCurrentModeAnim;
107 private boolean mShowHideAnimationEnabled;
108 boolean mWasHiddenBeforeMode;
109
110 final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
111 @Override
112 public void onAnimationEnd(Animator animation) {
113 if (mContentView != null) {
114 mContentView.setTranslationY(0);
115 mContainerView.setTranslationY(0);
116 }
117 if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
118 mSplitView.setVisibility(View.GONE);
119 }
120 mContainerView.setVisibility(View.GONE);
121 mContainerView.setTransitioning(false);
122 mCurrentShowAnim = null;
123 completeDeferredDestroyActionMode();
124 }
125 };
126
127 final AnimatorListener mShowListener = new AnimatorListenerAdapter() {
128 @Override
129 public void onAnimationEnd(Animator animation) {
130 mCurrentShowAnim = null;
131 mContainerView.requestLayout();
132 }
133 };
134
135 public ActionBarImpl(Activity activity, int features) {
136 mActivity = activity;
137 Window window = activity.getWindow();
138 View decor = window.getDecorView();
139 init(decor);
140
141 //window.hasFeature() workaround for pre-3.0
142 if ((features & (1 << Window.FEATURE_ACTION_BAR_OVERLAY)) == 0) {
143 mContentView = (NineFrameLayout)decor.findViewById(android.R.id.content);
144 }
145 }
146
147 public ActionBarImpl(Dialog dialog) {
148 //UNUSED mDialog = dialog;
149 init(dialog.getWindow().getDecorView());
150 }
151
152 private void init(View decor) {
153 mContext = decor.getContext();
154 mActionView = (ActionBarView) decor.findViewById(R.id.abs__action_bar);
155 mContextView = (ActionBarContextView) decor.findViewById(
156 R.id.abs__action_context_bar);
157 mContainerView = (ActionBarContainer) decor.findViewById(
158 R.id.abs__action_bar_container);
159 mSplitView = (ActionBarContainer) decor.findViewById(
160 R.id.abs__split_action_bar);
161
162 if (mActionView == null || mContextView == null || mContainerView == null) {
163 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
164 "with a compatible window decor layout");
165 }
166
167 mActionView.setContextView(mContextView);
168 mContextDisplayMode = mActionView.isSplitActionBar() ?
169 CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;
170
171 // Older apps get the home button interaction enabled by default.
172 // Newer apps need to enable it explicitly.
173 setHomeButtonEnabled(mContext.getApplicationInfo().targetSdkVersion < 14);
174
175 setHasEmbeddedTabs(getResources_getBoolean(mContext,
176 R.bool.abs__action_bar_embed_tabs));
177 }
178
179 public void onConfigurationChanged(Configuration newConfig) {
180 setHasEmbeddedTabs(getResources_getBoolean(mContext,
181 R.bool.abs__action_bar_embed_tabs));
182
183 //Manually dispatch a configuration change to the action bar view on pre-2.2
184 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
185 mActionView.onConfigurationChanged(newConfig);
186 if (mContextView != null) {
187 mContextView.onConfigurationChanged(newConfig);
188 }
189 }
190 }
191
192 private void setHasEmbeddedTabs(boolean hasEmbeddedTabs) {
193 mHasEmbeddedTabs = hasEmbeddedTabs;
194 // Switch tab layout configuration if needed
195 if (!mHasEmbeddedTabs) {
196 mActionView.setEmbeddedTabView(null);
197 mContainerView.setTabContainer(mTabScrollView);
198 } else {
199 mContainerView.setTabContainer(null);
200 mActionView.setEmbeddedTabView(mTabScrollView);
201 }
202 final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS;
203 if (mTabScrollView != null) {
204 mTabScrollView.setVisibility(isInTabMode ? View.VISIBLE : View.GONE);
205 }
206 mActionView.setCollapsable(!mHasEmbeddedTabs && isInTabMode);
207 }
208
209 private void ensureTabsExist() {
210 if (mTabScrollView != null) {
211 return;
212 }
213
214 ScrollingTabContainerView tabScroller = new ScrollingTabContainerView(mContext);
215
216 if (mHasEmbeddedTabs) {
217 tabScroller.setVisibility(View.VISIBLE);
218 mActionView.setEmbeddedTabView(tabScroller);
219 } else {
220 tabScroller.setVisibility(getNavigationMode() == NAVIGATION_MODE_TABS ?
221 View.VISIBLE : View.GONE);
222 mContainerView.setTabContainer(tabScroller);
223 }
224 mTabScrollView = tabScroller;
225 }
226
227 void completeDeferredDestroyActionMode() {
228 if (mDeferredModeDestroyCallback != null) {
229 mDeferredModeDestroyCallback.onDestroyActionMode(mDeferredDestroyActionMode);
230 mDeferredDestroyActionMode = null;
231 mDeferredModeDestroyCallback = null;
232 }
233 }
234
235 /**
236 * Enables or disables animation between show/hide states.
237 * If animation is disabled using this method, animations in progress
238 * will be finished.
239 *
240 * @param enabled true to animate, false to not animate.
241 */
242 public void setShowHideAnimationEnabled(boolean enabled) {
243 mShowHideAnimationEnabled = enabled;
244 if (!enabled && mCurrentShowAnim != null) {
245 mCurrentShowAnim.end();
246 }
247 }
248
249 public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
250 mMenuVisibilityListeners.add(listener);
251 }
252
253 public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
254 mMenuVisibilityListeners.remove(listener);
255 }
256
257 public void dispatchMenuVisibilityChanged(boolean isVisible) {
258 if (isVisible == mLastMenuVisibility) {
259 return;
260 }
261 mLastMenuVisibility = isVisible;
262
263 final int count = mMenuVisibilityListeners.size();
264 for (int i = 0; i < count; i++) {
265 mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible);
266 }
267 }
268
269 @Override
270 public void setCustomView(int resId) {
271 setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId, mActionView, false));
272 }
273
274 @Override
275 public void setDisplayUseLogoEnabled(boolean useLogo) {
276 setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO);
277 }
278
279 @Override
280 public void setDisplayShowHomeEnabled(boolean showHome) {
281 setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME);
282 }
283
284 @Override
285 public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) {
286 setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP);
287 }
288
289 @Override
290 public void setDisplayShowTitleEnabled(boolean showTitle) {
291 setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE);
292 }
293
294 @Override
295 public void setDisplayShowCustomEnabled(boolean showCustom) {
296 setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM);
297 }
298
299 @Override
300 public void setHomeButtonEnabled(boolean enable) {
301 mActionView.setHomeButtonEnabled(enable);
302 }
303
304 @Override
305 public void setTitle(int resId) {
306 setTitle(mContext.getString(resId));
307 }
308
309 @Override
310 public void setSubtitle(int resId) {
311 setSubtitle(mContext.getString(resId));
312 }
313
314 public void setSelectedNavigationItem(int position) {
315 switch (mActionView.getNavigationMode()) {
316 case NAVIGATION_MODE_TABS:
317 selectTab(mTabs.get(position));
318 break;
319 case NAVIGATION_MODE_LIST:
320 mActionView.setDropdownSelectedPosition(position);
321 break;
322 default:
323 throw new IllegalStateException(
324 "setSelectedNavigationIndex not valid for current navigation mode");
325 }
326 }
327
328 public void removeAllTabs() {
329 cleanupTabs();
330 }
331
332 private void cleanupTabs() {
333 if (mSelectedTab != null) {
334 selectTab(null);
335 }
336 mTabs.clear();
337 if (mTabScrollView != null) {
338 mTabScrollView.removeAllTabs();
339 }
340 mSavedTabPosition = INVALID_POSITION;
341 }
342
343 public void setTitle(CharSequence title) {
344 mActionView.setTitle(title);
345 }
346
347 public void setSubtitle(CharSequence subtitle) {
348 mActionView.setSubtitle(subtitle);
349 }
350
351 public void setDisplayOptions(int options) {
352 mActionView.setDisplayOptions(options);
353 }
354
355 public void setDisplayOptions(int options, int mask) {
356 final int current = mActionView.getDisplayOptions();
357 mActionView.setDisplayOptions((options & mask) | (current & ~mask));
358 }
359
360 public void setBackgroundDrawable(Drawable d) {
361 mContainerView.setPrimaryBackground(d);
362 }
363
364 public void setStackedBackgroundDrawable(Drawable d) {
365 mContainerView.setStackedBackground(d);
366 }
367
368 public void setSplitBackgroundDrawable(Drawable d) {
369 if (mSplitView != null) {
370 mSplitView.setSplitBackground(d);
371 }
372 }
373
374 public View getCustomView() {
375 return mActionView.getCustomNavigationView();
376 }
377
378 public CharSequence getTitle() {
379 return mActionView.getTitle();
380 }
381
382 public CharSequence getSubtitle() {
383 return mActionView.getSubtitle();
384 }
385
386 public int getNavigationMode() {
387 return mActionView.getNavigationMode();
388 }
389
390 public int getDisplayOptions() {
391 return mActionView.getDisplayOptions();
392 }
393
394 public ActionMode startActionMode(ActionMode.Callback callback) {
395 boolean wasHidden = false;
396 if (mActionMode != null) {
397 wasHidden = mWasHiddenBeforeMode;
398 mActionMode.finish();
399 }
400
401 mContextView.killMode();
402 ActionModeImpl mode = new ActionModeImpl(callback);
403 if (mode.dispatchOnCreate()) {
404 mWasHiddenBeforeMode = !isShowing() || wasHidden;
405 mode.invalidate();
406 mContextView.initForMode(mode);
407 animateToMode(true);
408 if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
409 // TODO animate this
410 mSplitView.setVisibility(View.VISIBLE);
411 }
412 mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
413 mActionMode = mode;
414 return mode;
415 }
416 return null;
417 }
418
419 private void configureTab(Tab tab, int position) {
420 final TabImpl tabi = (TabImpl) tab;
421 final ActionBar.TabListener callback = tabi.getCallback();
422
423 if (callback == null) {
424 throw new IllegalStateException("Action Bar Tab must have a Callback");
425 }
426
427 tabi.setPosition(position);
428 mTabs.add(position, tabi);
429
430 final int count = mTabs.size();
431 for (int i = position + 1; i < count; i++) {
432 mTabs.get(i).setPosition(i);
433 }
434 }
435
436 @Override
437 public void addTab(Tab tab) {
438 addTab(tab, mTabs.isEmpty());
439 }
440
441 @Override
442 public void addTab(Tab tab, int position) {
443 addTab(tab, position, mTabs.isEmpty());
444 }
445
446 @Override
447 public void addTab(Tab tab, boolean setSelected) {
448 ensureTabsExist();
449 mTabScrollView.addTab(tab, setSelected);
450 configureTab(tab, mTabs.size());
451 if (setSelected) {
452 selectTab(tab);
453 }
454 }
455
456 @Override
457 public void addTab(Tab tab, int position, boolean setSelected) {
458 ensureTabsExist();
459 mTabScrollView.addTab(tab, position, setSelected);
460 configureTab(tab, position);
461 if (setSelected) {
462 selectTab(tab);
463 }
464 }
465
466 @Override
467 public Tab newTab() {
468 return new TabImpl();
469 }
470
471 @Override
472 public void removeTab(Tab tab) {
473 removeTabAt(tab.getPosition());
474 }
475
476 @Override
477 public void removeTabAt(int position) {
478 if (mTabScrollView == null) {
479 // No tabs around to remove
480 return;
481 }
482
483 int selectedTabPosition = mSelectedTab != null
484 ? mSelectedTab.getPosition() : mSavedTabPosition;
485 mTabScrollView.removeTabAt(position);
486 TabImpl removedTab = mTabs.remove(position);
487 if (removedTab != null) {
488 removedTab.setPosition(-1);
489 }
490
491 final int newTabCount = mTabs.size();
492 for (int i = position; i < newTabCount; i++) {
493 mTabs.get(i).setPosition(i);
494 }
495
496 if (selectedTabPosition == position) {
497 selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
498 }
499 }
500
501 @Override
502 public void selectTab(Tab tab) {
503 if (getNavigationMode() != NAVIGATION_MODE_TABS) {
504 mSavedTabPosition = tab != null ? tab.getPosition() : INVALID_POSITION;
505 return;
506 }
507
508 FragmentTransaction trans = null;
509 if (mActivity instanceof FragmentActivity) {
510 trans = ((FragmentActivity)mActivity).getSupportFragmentManager().beginTransaction()
511 .disallowAddToBackStack();
512 }
513
514 if (mSelectedTab == tab) {
515 if (mSelectedTab != null) {
516 mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans);
517 mTabScrollView.animateToTab(tab.getPosition());
518 }
519 } else {
520 mTabScrollView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
521 if (mSelectedTab != null) {
522 mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans);
523 }
524 mSelectedTab = (TabImpl) tab;
525 if (mSelectedTab != null) {
526 mSelectedTab.getCallback().onTabSelected(mSelectedTab, trans);
527 }
528 }
529
530 if (trans != null && !trans.isEmpty()) {
531 trans.commit();
532 }
533 }
534
535 @Override
536 public Tab getSelectedTab() {
537 return mSelectedTab;
538 }
539
540 @Override
541 public int getHeight() {
542 return mContainerView.getHeight();
543 }
544
545 @Override
546 public void show() {
547 show(true);
548 }
549
550 void show(boolean markHiddenBeforeMode) {
551 if (mCurrentShowAnim != null) {
552 mCurrentShowAnim.end();
553 }
554 if (mContainerView.getVisibility() == View.VISIBLE) {
555 if (markHiddenBeforeMode) mWasHiddenBeforeMode = false;
556 return;
557 }
558 mContainerView.setVisibility(View.VISIBLE);
559
560 if (mShowHideAnimationEnabled) {
561 mContainerView.setAlpha(0);
562 AnimatorSet anim = new AnimatorSet();
563 AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 1));
564 if (mContentView != null) {
565 b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
566 -mContainerView.getHeight(), 0));
567 mContainerView.setTranslationY(-mContainerView.getHeight());
568 b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0));
569 }
570 if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
571 mSplitView.setAlpha(0);
572 mSplitView.setVisibility(View.VISIBLE);
573 b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 1));
574 }
575 anim.addListener(mShowListener);
576 mCurrentShowAnim = anim;
577 anim.start();
578 } else {
579 mContainerView.setAlpha(1);
580 mContainerView.setTranslationY(0);
581 mShowListener.onAnimationEnd(null);
582 }
583 }
584
585 @Override
586 public void hide() {
587 if (mCurrentShowAnim != null) {
588 mCurrentShowAnim.end();
589 }
590 if (mContainerView.getVisibility() == View.GONE) {
591 return;
592 }
593
594 if (mShowHideAnimationEnabled) {
595 mContainerView.setAlpha(1);
596 mContainerView.setTransitioning(true);
597 AnimatorSet anim = new AnimatorSet();
598 AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 0));
599 if (mContentView != null) {
600 b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
601 0, -mContainerView.getHeight()));
602 b.with(ObjectAnimator.ofFloat(mContainerView, "translationY",
603 -mContainerView.getHeight()));
604 }
605 if (mSplitView != null && mSplitView.getVisibility() == View.VISIBLE) {
606 mSplitView.setAlpha(1);
607 b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 0));
608 }
609 anim.addListener(mHideListener);
610 mCurrentShowAnim = anim;
611 anim.start();
612 } else {
613 mHideListener.onAnimationEnd(null);
614 }
615 }
616
617 public boolean isShowing() {
618 return mContainerView.getVisibility() == View.VISIBLE;
619 }
620
621 void animateToMode(boolean toActionMode) {
622 if (toActionMode) {
623 show(false);
624 }
625 if (mCurrentModeAnim != null) {
626 mCurrentModeAnim.end();
627 }
628
629 mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
630 mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE);
631 if (mTabScrollView != null && !mActionView.hasEmbeddedTabs() && mActionView.isCollapsed()) {
632 mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
633 }
634 }
635
636 public Context getThemedContext() {
637 if (mThemedContext == null) {
638 TypedValue outValue = new TypedValue();
639 Resources.Theme currentTheme = mContext.getTheme();
640 currentTheme.resolveAttribute(R.attr.actionBarWidgetTheme,
641 outValue, true);
642 final int targetThemeRes = outValue.resourceId;
643
644 if (targetThemeRes != 0) { //XXX && mContext.getThemeResId() != targetThemeRes) {
645 mThemedContext = new ContextThemeWrapper(mContext, targetThemeRes);
646 } else {
647 mThemedContext = mContext;
648 }
649 }
650 return mThemedContext;
651 }
652
653 /**
654 * @hide
655 */
656 public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback {
657 private ActionMode.Callback mCallback;
658 private MenuBuilder mMenu;
659 private WeakReference<View> mCustomView;
660
661 public ActionModeImpl(ActionMode.Callback callback) {
662 mCallback = callback;
663 mMenu = new MenuBuilder(getThemedContext())
664 .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
665 mMenu.setCallback(this);
666 }
667
668 @Override
669 public MenuInflater getMenuInflater() {
670 return new MenuInflater(getThemedContext());
671 }
672
673 @Override
674 public Menu getMenu() {
675 return mMenu;
676 }
677
678 @Override
679 public void finish() {
680 if (mActionMode != this) {
681 // Not the active action mode - no-op
682 return;
683 }
684
685 // If we were hidden before the mode was shown, defer the onDestroy
686 // callback until the animation is finished and associated relayout
687 // is about to happen. This lets apps better anticipate visibility
688 // and layout behavior.
689 if (mWasHiddenBeforeMode) {
690 mDeferredDestroyActionMode = this;
691 mDeferredModeDestroyCallback = mCallback;
692 } else {
693 mCallback.onDestroyActionMode(this);
694 }
695 mCallback = null;
696 animateToMode(false);
697
698 // Clear out the context mode views after the animation finishes
699 mContextView.closeMode();
700 mActionView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
701
702 mActionMode = null;
703
704 if (mWasHiddenBeforeMode) {
705 hide();
706 }
707 }
708
709 @Override
710 public void invalidate() {
711 mMenu.stopDispatchingItemsChanged();
712 try {
713 mCallback.onPrepareActionMode(this, mMenu);
714 } finally {
715 mMenu.startDispatchingItemsChanged();
716 }
717 }
718
719 public boolean dispatchOnCreate() {
720 mMenu.stopDispatchingItemsChanged();
721 try {
722 return mCallback.onCreateActionMode(this, mMenu);
723 } finally {
724 mMenu.startDispatchingItemsChanged();
725 }
726 }
727
728 @Override
729 public void setCustomView(View view) {
730 mContextView.setCustomView(view);
731 mCustomView = new WeakReference<View>(view);
732 }
733
734 @Override
735 public void setSubtitle(CharSequence subtitle) {
736 mContextView.setSubtitle(subtitle);
737 }
738
739 @Override
740 public void setTitle(CharSequence title) {
741 mContextView.setTitle(title);
742 }
743
744 @Override
745 public void setTitle(int resId) {
746 setTitle(mContext.getResources().getString(resId));
747 }
748
749 @Override
750 public void setSubtitle(int resId) {
751 setSubtitle(mContext.getResources().getString(resId));
752 }
753
754 @Override
755 public CharSequence getTitle() {
756 return mContextView.getTitle();
757 }
758
759 @Override
760 public CharSequence getSubtitle() {
761 return mContextView.getSubtitle();
762 }
763
764 @Override
765 public View getCustomView() {
766 return mCustomView != null ? mCustomView.get() : null;
767 }
768
769 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
770 if (mCallback != null) {
771 return mCallback.onActionItemClicked(this, item);
772 } else {
773 return false;
774 }
775 }
776
777 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
778 }
779
780 public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
781 if (mCallback == null) {
782 return false;
783 }
784
785 if (!subMenu.hasVisibleItems()) {
786 return true;
787 }
788
789 new MenuPopupHelper(getThemedContext(), subMenu).show();
790 return true;
791 }
792
793 public void onCloseSubMenu(SubMenuBuilder menu) {
794 }
795
796 public void onMenuModeChange(MenuBuilder menu) {
797 if (mCallback == null) {
798 return;
799 }
800 invalidate();
801 mContextView.showOverflowMenu();
802 }
803 }
804
805 /**
806 * @hide
807 */
808 public class TabImpl extends ActionBar.Tab {
809 private ActionBar.TabListener mCallback;
810 private Object mTag;
811 private Drawable mIcon;
812 private CharSequence mText;
813 private CharSequence mContentDesc;
814 private int mPosition = -1;
815 private View mCustomView;
816
817 @Override
818 public Object getTag() {
819 return mTag;
820 }
821
822 @Override
823 public Tab setTag(Object tag) {
824 mTag = tag;
825 return this;
826 }
827
828 public ActionBar.TabListener getCallback() {
829 return mCallback;
830 }
831
832 @Override
833 public Tab setTabListener(ActionBar.TabListener callback) {
834 mCallback = callback;
835 return this;
836 }
837
838 @Override
839 public View getCustomView() {
840 return mCustomView;
841 }
842
843 @Override
844 public Tab setCustomView(View view) {
845 mCustomView = view;
846 if (mPosition >= 0) {
847 mTabScrollView.updateTab(mPosition);
848 }
849 return this;
850 }
851
852 @Override
853 public Tab setCustomView(int layoutResId) {
854 return setCustomView(LayoutInflater.from(getThemedContext())
855 .inflate(layoutResId, null));
856 }
857
858 @Override
859 public Drawable getIcon() {
860 return mIcon;
861 }
862
863 @Override
864 public int getPosition() {
865 return mPosition;
866 }
867
868 public void setPosition(int position) {
869 mPosition = position;
870 }
871
872 @Override
873 public CharSequence getText() {
874 return mText;
875 }
876
877 @Override
878 public Tab setIcon(Drawable icon) {
879 mIcon = icon;
880 if (mPosition >= 0) {
881 mTabScrollView.updateTab(mPosition);
882 }
883 return this;
884 }
885
886 @Override
887 public Tab setIcon(int resId) {
888 return setIcon(mContext.getResources().getDrawable(resId));
889 }
890
891 @Override
892 public Tab setText(CharSequence text) {
893 mText = text;
894 if (mPosition >= 0) {
895 mTabScrollView.updateTab(mPosition);
896 }
897 return this;
898 }
899
900 @Override
901 public Tab setText(int resId) {
902 return setText(mContext.getResources().getText(resId));
903 }
904
905 @Override
906 public void select() {
907 selectTab(this);
908 }
909
910 @Override
911 public Tab setContentDescription(int resId) {
912 return setContentDescription(mContext.getResources().getText(resId));
913 }
914
915 @Override
916 public Tab setContentDescription(CharSequence contentDesc) {
917 mContentDesc = contentDesc;
918 if (mPosition >= 0) {
919 mTabScrollView.updateTab(mPosition);
920 }
921 return this;
922 }
923
924 @Override
925 public CharSequence getContentDescription() {
926 return mContentDesc;
927 }
928 }
929
930 @Override
931 public void setCustomView(View view) {
932 mActionView.setCustomNavigationView(view);
933 }
934
935 @Override
936 public void setCustomView(View view, LayoutParams layoutParams) {
937 view.setLayoutParams(layoutParams);
938 mActionView.setCustomNavigationView(view);
939 }
940
941 @Override
942 public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
943 mActionView.setDropdownAdapter(adapter);
944 mActionView.setCallback(callback);
945 }
946
947 @Override
948 public int getSelectedNavigationIndex() {
949 switch (mActionView.getNavigationMode()) {
950 case NAVIGATION_MODE_TABS:
951 return mSelectedTab != null ? mSelectedTab.getPosition() : -1;
952 case NAVIGATION_MODE_LIST:
953 return mActionView.getDropdownSelectedPosition();
954 default:
955 return -1;
956 }
957 }
958
959 @Override
960 public int getNavigationItemCount() {
961 switch (mActionView.getNavigationMode()) {
962 case NAVIGATION_MODE_TABS:
963 return mTabs.size();
964 case NAVIGATION_MODE_LIST:
965 SpinnerAdapter adapter = mActionView.getDropdownAdapter();
966 return adapter != null ? adapter.getCount() : 0;
967 default:
968 return 0;
969 }
970 }
971
972 @Override
973 public int getTabCount() {
974 return mTabs.size();
975 }
976
977 @Override
978 public void setNavigationMode(int mode) {
979 final int oldMode = mActionView.getNavigationMode();
980 switch (oldMode) {
981 case NAVIGATION_MODE_TABS:
982 mSavedTabPosition = getSelectedNavigationIndex();
983 selectTab(null);
984 mTabScrollView.setVisibility(View.GONE);
985 break;
986 }
987 mActionView.setNavigationMode(mode);
988 switch (mode) {
989 case NAVIGATION_MODE_TABS:
990 ensureTabsExist();
991 mTabScrollView.setVisibility(View.VISIBLE);
992 if (mSavedTabPosition != INVALID_POSITION) {
993 setSelectedNavigationItem(mSavedTabPosition);
994 mSavedTabPosition = INVALID_POSITION;
995 }
996 break;
997 }
998 mActionView.setCollapsable(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
999 }
1000
1001 @Override
1002 public Tab getTabAt(int index) {
1003 return mTabs.get(index);
1004 }
1005
1006
1007 @Override
1008 public void setIcon(int resId) {
1009 mActionView.setIcon(resId);
1010 }
1011
1012 @Override
1013 public void setIcon(Drawable icon) {
1014 mActionView.setIcon(icon);
1015 }
1016
1017 @Override
1018 public void setLogo(int resId) {
1019 mActionView.setLogo(resId);
1020 }
1021
1022 @Override
1023 public void setLogo(Drawable logo) {
1024 mActionView.setLogo(logo);
1025 }
1026 }