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