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