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