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