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