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