WindowDecorActionBar.java revision 4369e7d0b087d777e5012e2706acc5be9be47de7
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 android.animation.ValueAnimator; 20import android.content.res.TypedArray; 21import android.view.ViewParent; 22import com.android.internal.R; 23import com.android.internal.view.ActionBarPolicy; 24import com.android.internal.view.menu.MenuBuilder; 25import com.android.internal.view.menu.MenuPopupHelper; 26import com.android.internal.view.menu.SubMenuBuilder; 27import com.android.internal.widget.ActionBarContainer; 28import com.android.internal.widget.ActionBarContextView; 29import com.android.internal.widget.ActionBarOverlayLayout; 30import com.android.internal.widget.ActionBarView; 31import com.android.internal.widget.ScrollingTabContainerView; 32 33import android.animation.Animator; 34import android.animation.Animator.AnimatorListener; 35import android.animation.AnimatorListenerAdapter; 36import android.animation.AnimatorSet; 37import android.animation.ObjectAnimator; 38import android.app.ActionBar; 39import android.app.Activity; 40import android.app.Dialog; 41import android.app.FragmentTransaction; 42import android.content.Context; 43import android.content.res.Configuration; 44import android.content.res.Resources; 45import android.graphics.drawable.Drawable; 46import android.util.TypedValue; 47import android.view.ActionMode; 48import android.view.ContextThemeWrapper; 49import android.view.LayoutInflater; 50import android.view.Menu; 51import android.view.MenuInflater; 52import android.view.MenuItem; 53import android.view.View; 54import android.view.Window; 55import android.view.accessibility.AccessibilityEvent; 56import android.view.animation.AnimationUtils; 57import android.widget.SpinnerAdapter; 58 59import java.lang.ref.WeakReference; 60import java.util.ArrayList; 61 62/** 63 * WindowDecorActionBar is the ActionBar implementation used 64 * by devices of all screen sizes as part of the window decor layout. 65 * If it detects a compatible decor, it will split contextual modes 66 * across both the ActionBarView at the top of the screen and 67 * a horizontal LinearLayout at the bottom which is normally hidden. 68 */ 69public class WindowDecorActionBar extends ActionBar implements 70 ActionBarOverlayLayout.ActionBarVisibilityCallback { 71 private static final String TAG = "WindowDecorActionBar"; 72 73 private Context mContext; 74 private Context mThemedContext; 75 private Activity mActivity; 76 private Dialog mDialog; 77 78 private ActionBarOverlayLayout mOverlayLayout; 79 private ActionBarContainer mContainerView; 80 private ActionBarView mActionView; 81 private ActionBarContextView mContextView; 82 private ActionBarContainer mSplitView; 83 private View mContentView; 84 private ScrollingTabContainerView mTabScrollView; 85 86 private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>(); 87 88 private TabImpl mSelectedTab; 89 private int mSavedTabPosition = INVALID_POSITION; 90 91 private boolean mDisplayHomeAsUpSet; 92 93 ActionModeImpl mActionMode; 94 ActionMode mDeferredDestroyActionMode; 95 ActionMode.Callback mDeferredModeDestroyCallback; 96 97 private boolean mLastMenuVisibility; 98 private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners = 99 new ArrayList<OnMenuVisibilityListener>(); 100 101 private static final int CONTEXT_DISPLAY_NORMAL = 0; 102 private static final int CONTEXT_DISPLAY_SPLIT = 1; 103 104 private static final int INVALID_POSITION = -1; 105 106 private int mContextDisplayMode; 107 private boolean mHasEmbeddedTabs; 108 109 private int mCurWindowVisibility = View.VISIBLE; 110 111 private boolean mContentAnimations = true; 112 private boolean mHiddenByApp; 113 private boolean mHiddenBySystem; 114 private boolean mShowingForMode; 115 116 private boolean mNowShowing = true; 117 118 private Animator mCurrentShowAnim; 119 private boolean mShowHideAnimationEnabled; 120 boolean mHideOnContentScroll; 121 122 final AnimatorListener mHideListener = new AnimatorListenerAdapter() { 123 @Override 124 public void onAnimationEnd(Animator animation) { 125 if (mContentAnimations && mContentView != null) { 126 mContentView.setTranslationY(0); 127 mContainerView.setTranslationY(0); 128 } 129 if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) { 130 mSplitView.setVisibility(View.GONE); 131 } 132 mContainerView.setVisibility(View.GONE); 133 mContainerView.setTransitioning(false); 134 mCurrentShowAnim = null; 135 completeDeferredDestroyActionMode(); 136 if (mOverlayLayout != null) { 137 mOverlayLayout.requestApplyInsets(); 138 } 139 } 140 }; 141 142 final AnimatorListener mShowListener = new AnimatorListenerAdapter() { 143 @Override 144 public void onAnimationEnd(Animator animation) { 145 mCurrentShowAnim = null; 146 mContainerView.requestLayout(); 147 } 148 }; 149 150 final ValueAnimator.AnimatorUpdateListener mUpdateListener = 151 new ValueAnimator.AnimatorUpdateListener() { 152 @Override 153 public void onAnimationUpdate(ValueAnimator animation) { 154 final ViewParent parent = mContainerView.getParent(); 155 ((View) parent).invalidate(); 156 } 157 }; 158 159 public WindowDecorActionBar(Activity activity) { 160 mActivity = activity; 161 Window window = activity.getWindow(); 162 View decor = window.getDecorView(); 163 boolean overlayMode = mActivity.getWindow().hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY); 164 init(decor); 165 if (!overlayMode) { 166 mContentView = decor.findViewById(android.R.id.content); 167 } 168 } 169 170 public WindowDecorActionBar(Dialog dialog) { 171 mDialog = dialog; 172 init(dialog.getWindow().getDecorView()); 173 } 174 175 /** 176 * Only for edit mode. 177 * @hide 178 */ 179 public WindowDecorActionBar(View layout) { 180 assert layout.isInEditMode(); 181 init(layout); 182 } 183 184 private void init(View decor) { 185 mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById( 186 com.android.internal.R.id.decor_content_parent); 187 if (mOverlayLayout != null) { 188 mOverlayLayout.setActionBarVisibilityCallback(this); 189 } 190 mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar); 191 mContextView = (ActionBarContextView) decor.findViewById( 192 com.android.internal.R.id.action_context_bar); 193 mContainerView = (ActionBarContainer) decor.findViewById( 194 com.android.internal.R.id.action_bar_container); 195 mSplitView = (ActionBarContainer) decor.findViewById( 196 com.android.internal.R.id.split_action_bar); 197 198 if (mActionView == null || mContextView == null || mContainerView == null) { 199 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 200 "with a compatible window decor layout"); 201 } 202 203 mContext = mActionView.getContext(); 204 mActionView.setContextView(mContextView); 205 mContextDisplayMode = mActionView.isSplitActionBar() ? 206 CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL; 207 208 // This was initially read from the action bar style 209 final int current = mActionView.getDisplayOptions(); 210 final boolean homeAsUp = (current & DISPLAY_HOME_AS_UP) != 0; 211 if (homeAsUp) { 212 mDisplayHomeAsUpSet = true; 213 } 214 215 ActionBarPolicy abp = ActionBarPolicy.get(mContext); 216 setHomeButtonEnabled(abp.enableHomeButtonByDefault() || homeAsUp); 217 setHasEmbeddedTabs(abp.hasEmbeddedTabs()); 218 219 final TypedArray a = mContext.obtainStyledAttributes(null, 220 com.android.internal.R.styleable.ActionBar, 221 com.android.internal.R.attr.actionBarStyle, 0); 222 if (a.getBoolean(R.styleable.ActionBar_hideOnContentScroll, false)) { 223 setHideOnContentScrollEnabled(true); 224 } 225 a.recycle(); 226 } 227 228 public void onConfigurationChanged(Configuration newConfig) { 229 setHasEmbeddedTabs(ActionBarPolicy.get(mContext).hasEmbeddedTabs()); 230 } 231 232 private void setHasEmbeddedTabs(boolean hasEmbeddedTabs) { 233 mHasEmbeddedTabs = hasEmbeddedTabs; 234 // Switch tab layout configuration if needed 235 if (!mHasEmbeddedTabs) { 236 mActionView.setEmbeddedTabView(null); 237 mContainerView.setTabContainer(mTabScrollView); 238 } else { 239 mContainerView.setTabContainer(null); 240 mActionView.setEmbeddedTabView(mTabScrollView); 241 } 242 final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS; 243 if (mTabScrollView != null) { 244 if (isInTabMode) { 245 mTabScrollView.setVisibility(View.VISIBLE); 246 if (mOverlayLayout != null) { 247 mOverlayLayout.requestApplyInsets(); 248 } 249 } else { 250 mTabScrollView.setVisibility(View.GONE); 251 } 252 } 253 mActionView.setCollapsable(!mHasEmbeddedTabs && isInTabMode); 254 mOverlayLayout.setHasNonEmbeddedTabs(!mHasEmbeddedTabs && isInTabMode); 255 } 256 257 private void ensureTabsExist() { 258 if (mTabScrollView != null) { 259 return; 260 } 261 262 ScrollingTabContainerView tabScroller = new ScrollingTabContainerView(mContext); 263 264 if (mHasEmbeddedTabs) { 265 tabScroller.setVisibility(View.VISIBLE); 266 mActionView.setEmbeddedTabView(tabScroller); 267 } else { 268 if (getNavigationMode() == NAVIGATION_MODE_TABS) { 269 tabScroller.setVisibility(View.VISIBLE); 270 if (mOverlayLayout != null) { 271 mOverlayLayout.requestApplyInsets(); 272 } 273 } else { 274 tabScroller.setVisibility(View.GONE); 275 } 276 mContainerView.setTabContainer(tabScroller); 277 } 278 mTabScrollView = tabScroller; 279 } 280 281 void completeDeferredDestroyActionMode() { 282 if (mDeferredModeDestroyCallback != null) { 283 mDeferredModeDestroyCallback.onDestroyActionMode(mDeferredDestroyActionMode); 284 mDeferredDestroyActionMode = null; 285 mDeferredModeDestroyCallback = null; 286 } 287 } 288 289 public void onWindowVisibilityChanged(int visibility) { 290 mCurWindowVisibility = visibility; 291 } 292 293 /** 294 * Enables or disables animation between show/hide states. 295 * If animation is disabled using this method, animations in progress 296 * will be finished. 297 * 298 * @param enabled true to animate, false to not animate. 299 */ 300 public void setShowHideAnimationEnabled(boolean enabled) { 301 mShowHideAnimationEnabled = enabled; 302 if (!enabled && mCurrentShowAnim != null) { 303 mCurrentShowAnim.end(); 304 } 305 } 306 307 public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) { 308 mMenuVisibilityListeners.add(listener); 309 } 310 311 public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) { 312 mMenuVisibilityListeners.remove(listener); 313 } 314 315 public void dispatchMenuVisibilityChanged(boolean isVisible) { 316 if (isVisible == mLastMenuVisibility) { 317 return; 318 } 319 mLastMenuVisibility = isVisible; 320 321 final int count = mMenuVisibilityListeners.size(); 322 for (int i = 0; i < count; i++) { 323 mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible); 324 } 325 } 326 327 @Override 328 public void setCustomView(int resId) { 329 setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId, mActionView, false)); 330 } 331 332 @Override 333 public void setDisplayUseLogoEnabled(boolean useLogo) { 334 setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO); 335 } 336 337 @Override 338 public void setDisplayShowHomeEnabled(boolean showHome) { 339 setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME); 340 } 341 342 @Override 343 public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) { 344 setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP); 345 } 346 347 @Override 348 public void setDisplayShowTitleEnabled(boolean showTitle) { 349 setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE); 350 } 351 352 @Override 353 public void setDisplayShowCustomEnabled(boolean showCustom) { 354 setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM); 355 } 356 357 @Override 358 public void setHomeButtonEnabled(boolean enable) { 359 mActionView.setHomeButtonEnabled(enable); 360 } 361 362 @Override 363 public void setTitle(int resId) { 364 setTitle(mContext.getString(resId)); 365 } 366 367 @Override 368 public void setSubtitle(int resId) { 369 setSubtitle(mContext.getString(resId)); 370 } 371 372 public void setSelectedNavigationItem(int position) { 373 switch (mActionView.getNavigationMode()) { 374 case NAVIGATION_MODE_TABS: 375 selectTab(mTabs.get(position)); 376 break; 377 case NAVIGATION_MODE_LIST: 378 mActionView.setDropdownSelectedPosition(position); 379 break; 380 default: 381 throw new IllegalStateException( 382 "setSelectedNavigationIndex not valid for current navigation mode"); 383 } 384 } 385 386 public void removeAllTabs() { 387 cleanupTabs(); 388 } 389 390 private void cleanupTabs() { 391 if (mSelectedTab != null) { 392 selectTab(null); 393 } 394 mTabs.clear(); 395 if (mTabScrollView != null) { 396 mTabScrollView.removeAllTabs(); 397 } 398 mSavedTabPosition = INVALID_POSITION; 399 } 400 401 public void setTitle(CharSequence title) { 402 mActionView.setTitle(title); 403 } 404 405 public void setSubtitle(CharSequence subtitle) { 406 mActionView.setSubtitle(subtitle); 407 } 408 409 public void setDisplayOptions(int options) { 410 if ((options & DISPLAY_HOME_AS_UP) != 0) { 411 mDisplayHomeAsUpSet = true; 412 } 413 mActionView.setDisplayOptions(options); 414 } 415 416 public void setDisplayOptions(int options, int mask) { 417 final int current = mActionView.getDisplayOptions(); 418 if ((mask & DISPLAY_HOME_AS_UP) != 0) { 419 mDisplayHomeAsUpSet = true; 420 } 421 mActionView.setDisplayOptions((options & mask) | (current & ~mask)); 422 } 423 424 public void setBackgroundDrawable(Drawable d) { 425 mContainerView.setPrimaryBackground(d); 426 } 427 428 public void setStackedBackgroundDrawable(Drawable d) { 429 mContainerView.setStackedBackground(d); 430 } 431 432 public void setSplitBackgroundDrawable(Drawable d) { 433 if (mSplitView != null) { 434 mSplitView.setSplitBackground(d); 435 } 436 } 437 438 public View getCustomView() { 439 return mActionView.getCustomNavigationView(); 440 } 441 442 public CharSequence getTitle() { 443 return mActionView.getTitle(); 444 } 445 446 public CharSequence getSubtitle() { 447 return mActionView.getSubtitle(); 448 } 449 450 public int getNavigationMode() { 451 return mActionView.getNavigationMode(); 452 } 453 454 public int getDisplayOptions() { 455 return mActionView.getDisplayOptions(); 456 } 457 458 public ActionMode startActionMode(ActionMode.Callback callback) { 459 if (mActionMode != null) { 460 mActionMode.finish(); 461 } 462 463 mOverlayLayout.setHideOnContentScrollEnabled(false); 464 mContextView.killMode(); 465 ActionModeImpl mode = new ActionModeImpl(callback); 466 if (mode.dispatchOnCreate()) { 467 mode.invalidate(); 468 mContextView.initForMode(mode); 469 animateToMode(true); 470 if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) { 471 // TODO animate this 472 if (mSplitView.getVisibility() != View.VISIBLE) { 473 mSplitView.setVisibility(View.VISIBLE); 474 if (mOverlayLayout != null) { 475 mOverlayLayout.requestApplyInsets(); 476 } 477 } 478 } 479 mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 480 mActionMode = mode; 481 return mode; 482 } 483 return null; 484 } 485 486 private void configureTab(Tab tab, int position) { 487 final TabImpl tabi = (TabImpl) tab; 488 final ActionBar.TabListener callback = tabi.getCallback(); 489 490 if (callback == null) { 491 throw new IllegalStateException("Action Bar Tab must have a Callback"); 492 } 493 494 tabi.setPosition(position); 495 mTabs.add(position, tabi); 496 497 final int count = mTabs.size(); 498 for (int i = position + 1; i < count; i++) { 499 mTabs.get(i).setPosition(i); 500 } 501 } 502 503 @Override 504 public void addTab(Tab tab) { 505 addTab(tab, mTabs.isEmpty()); 506 } 507 508 @Override 509 public void addTab(Tab tab, int position) { 510 addTab(tab, position, mTabs.isEmpty()); 511 } 512 513 @Override 514 public void addTab(Tab tab, boolean setSelected) { 515 ensureTabsExist(); 516 mTabScrollView.addTab(tab, setSelected); 517 configureTab(tab, mTabs.size()); 518 if (setSelected) { 519 selectTab(tab); 520 } 521 } 522 523 @Override 524 public void addTab(Tab tab, int position, boolean setSelected) { 525 ensureTabsExist(); 526 mTabScrollView.addTab(tab, position, setSelected); 527 configureTab(tab, position); 528 if (setSelected) { 529 selectTab(tab); 530 } 531 } 532 533 @Override 534 public Tab newTab() { 535 return new TabImpl(); 536 } 537 538 @Override 539 public void removeTab(Tab tab) { 540 removeTabAt(tab.getPosition()); 541 } 542 543 @Override 544 public void removeTabAt(int position) { 545 if (mTabScrollView == null) { 546 // No tabs around to remove 547 return; 548 } 549 550 int selectedTabPosition = mSelectedTab != null 551 ? mSelectedTab.getPosition() : mSavedTabPosition; 552 mTabScrollView.removeTabAt(position); 553 TabImpl removedTab = mTabs.remove(position); 554 if (removedTab != null) { 555 removedTab.setPosition(-1); 556 } 557 558 final int newTabCount = mTabs.size(); 559 for (int i = position; i < newTabCount; i++) { 560 mTabs.get(i).setPosition(i); 561 } 562 563 if (selectedTabPosition == position) { 564 selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1))); 565 } 566 } 567 568 @Override 569 public void selectTab(Tab tab) { 570 if (getNavigationMode() != NAVIGATION_MODE_TABS) { 571 mSavedTabPosition = tab != null ? tab.getPosition() : INVALID_POSITION; 572 return; 573 } 574 575 final FragmentTransaction trans = mActionView.isInEditMode() ? null : 576 mActivity.getFragmentManager().beginTransaction().disallowAddToBackStack(); 577 578 if (mSelectedTab == tab) { 579 if (mSelectedTab != null) { 580 mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans); 581 mTabScrollView.animateToTab(tab.getPosition()); 582 } 583 } else { 584 mTabScrollView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION); 585 if (mSelectedTab != null) { 586 mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans); 587 } 588 mSelectedTab = (TabImpl) tab; 589 if (mSelectedTab != null) { 590 mSelectedTab.getCallback().onTabSelected(mSelectedTab, trans); 591 } 592 } 593 594 if (trans != null && !trans.isEmpty()) { 595 trans.commit(); 596 } 597 } 598 599 @Override 600 public Tab getSelectedTab() { 601 return mSelectedTab; 602 } 603 604 @Override 605 public int getHeight() { 606 return mContainerView.getHeight(); 607 } 608 609 public void enableContentAnimations(boolean enabled) { 610 mContentAnimations = enabled; 611 } 612 613 @Override 614 public void show() { 615 if (mHiddenByApp) { 616 mHiddenByApp = false; 617 updateVisibility(false); 618 } 619 } 620 621 private void showForActionMode() { 622 if (!mShowingForMode) { 623 mShowingForMode = true; 624 if (mOverlayLayout != null) { 625 mOverlayLayout.setShowingForActionMode(true); 626 } 627 updateVisibility(false); 628 } 629 } 630 631 public void showForSystem() { 632 if (mHiddenBySystem) { 633 mHiddenBySystem = false; 634 updateVisibility(true); 635 } 636 } 637 638 @Override 639 public void hide() { 640 if (!mHiddenByApp) { 641 mHiddenByApp = true; 642 updateVisibility(false); 643 } 644 } 645 646 private void hideForActionMode() { 647 if (mShowingForMode) { 648 mShowingForMode = false; 649 if (mOverlayLayout != null) { 650 mOverlayLayout.setShowingForActionMode(false); 651 } 652 updateVisibility(false); 653 } 654 } 655 656 public void hideForSystem() { 657 if (!mHiddenBySystem) { 658 mHiddenBySystem = true; 659 updateVisibility(true); 660 } 661 } 662 663 @Override 664 public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) { 665 if (hideOnContentScroll && !mOverlayLayout.isInOverlayMode()) { 666 throw new IllegalStateException("Action bar must be in overlay mode " + 667 "(Window.FEATURE_OVERLAY_ACTION_BAR) to enable hide on content scroll"); 668 } 669 mHideOnContentScroll = hideOnContentScroll; 670 mOverlayLayout.setHideOnContentScrollEnabled(hideOnContentScroll); 671 } 672 673 @Override 674 public boolean isHideOnContentScrollEnabled() { 675 return mOverlayLayout.isHideOnContentScrollEnabled(); 676 } 677 678 @Override 679 public int getHideOffset() { 680 return mOverlayLayout.getActionBarHideOffset(); 681 } 682 683 @Override 684 public void setHideOffset(int offset) { 685 if (offset != 0 && !mOverlayLayout.isInOverlayMode()) { 686 throw new IllegalStateException("Action bar must be in overlay mode " + 687 "(Window.FEATURE_OVERLAY_ACTION_BAR) to set a non-zero hide offset"); 688 } 689 mOverlayLayout.setActionBarHideOffset(offset); 690 } 691 692 private static boolean checkShowingFlags(boolean hiddenByApp, boolean hiddenBySystem, 693 boolean showingForMode) { 694 if (showingForMode) { 695 return true; 696 } else if (hiddenByApp || hiddenBySystem) { 697 return false; 698 } else { 699 return true; 700 } 701 } 702 703 private void updateVisibility(boolean fromSystem) { 704 // Based on the current state, should we be hidden or shown? 705 final boolean shown = checkShowingFlags(mHiddenByApp, mHiddenBySystem, 706 mShowingForMode); 707 708 if (shown) { 709 if (!mNowShowing) { 710 mNowShowing = true; 711 doShow(fromSystem); 712 } 713 } else { 714 if (mNowShowing) { 715 mNowShowing = false; 716 doHide(fromSystem); 717 } 718 } 719 } 720 721 public void doShow(boolean fromSystem) { 722 if (mCurrentShowAnim != null) { 723 mCurrentShowAnim.end(); 724 } 725 mContainerView.setVisibility(View.VISIBLE); 726 727 if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled 728 || fromSystem)) { 729 mContainerView.setTranslationY(0); // because we're about to ask its window loc 730 float startingY = -mContainerView.getHeight(); 731 if (fromSystem) { 732 int topLeft[] = {0, 0}; 733 mContainerView.getLocationInWindow(topLeft); 734 startingY -= topLeft[1]; 735 } 736 mContainerView.setTranslationY(startingY); 737 AnimatorSet anim = new AnimatorSet(); 738 ObjectAnimator a = ObjectAnimator.ofFloat(mContainerView, View.TRANSLATION_Y, 0); 739 a.addUpdateListener(mUpdateListener); 740 AnimatorSet.Builder b = anim.play(a); 741 if (mContentAnimations && mContentView != null) { 742 b.with(ObjectAnimator.ofFloat(mContentView, View.TRANSLATION_Y, 743 startingY, 0)); 744 } 745 if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) { 746 mSplitView.setTranslationY(mSplitView.getHeight()); 747 mSplitView.setVisibility(View.VISIBLE); 748 b.with(ObjectAnimator.ofFloat(mSplitView, View.TRANSLATION_Y, 0)); 749 } 750 anim.setInterpolator(AnimationUtils.loadInterpolator(mContext, 751 com.android.internal.R.interpolator.decelerate_cubic)); 752 anim.setDuration(250); 753 // If this is being shown from the system, add a small delay. 754 // This is because we will also be animating in the status bar, 755 // and these two elements can't be done in lock-step. So we give 756 // a little time for the status bar to start its animation before 757 // the action bar animates. (This corresponds to the corresponding 758 // case when hiding, where the status bar has a small delay before 759 // starting.) 760 anim.addListener(mShowListener); 761 mCurrentShowAnim = anim; 762 anim.start(); 763 } else { 764 mContainerView.setAlpha(1); 765 mContainerView.setTranslationY(0); 766 if (mContentAnimations && mContentView != null) { 767 mContentView.setTranslationY(0); 768 } 769 if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) { 770 mSplitView.setAlpha(1); 771 mSplitView.setTranslationY(0); 772 mSplitView.setVisibility(View.VISIBLE); 773 } 774 mShowListener.onAnimationEnd(null); 775 } 776 if (mOverlayLayout != null) { 777 mOverlayLayout.requestApplyInsets(); 778 } 779 } 780 781 public void doHide(boolean fromSystem) { 782 if (mCurrentShowAnim != null) { 783 mCurrentShowAnim.end(); 784 } 785 786 if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled 787 || fromSystem)) { 788 mContainerView.setAlpha(1); 789 mContainerView.setTransitioning(true); 790 AnimatorSet anim = new AnimatorSet(); 791 float endingY = -mContainerView.getHeight(); 792 if (fromSystem) { 793 int topLeft[] = {0, 0}; 794 mContainerView.getLocationInWindow(topLeft); 795 endingY -= topLeft[1]; 796 } 797 ObjectAnimator a = ObjectAnimator.ofFloat(mContainerView, View.TRANSLATION_Y, endingY); 798 a.addUpdateListener(mUpdateListener); 799 AnimatorSet.Builder b = anim.play(a); 800 if (mContentAnimations && mContentView != null) { 801 b.with(ObjectAnimator.ofFloat(mContentView, View.TRANSLATION_Y, 802 0, endingY)); 803 } 804 if (mSplitView != null && mSplitView.getVisibility() == View.VISIBLE) { 805 mSplitView.setAlpha(1); 806 b.with(ObjectAnimator.ofFloat(mSplitView, View.TRANSLATION_Y, 807 mSplitView.getHeight())); 808 } 809 anim.setInterpolator(AnimationUtils.loadInterpolator(mContext, 810 com.android.internal.R.interpolator.accelerate_cubic)); 811 anim.setDuration(250); 812 anim.addListener(mHideListener); 813 mCurrentShowAnim = anim; 814 anim.start(); 815 } else { 816 mHideListener.onAnimationEnd(null); 817 } 818 } 819 820 public boolean isShowing() { 821 return mNowShowing && getHideOffset() < getHeight(); 822 } 823 824 void animateToMode(boolean toActionMode) { 825 if (toActionMode) { 826 showForActionMode(); 827 } else { 828 hideForActionMode(); 829 } 830 831 mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); 832 mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE); 833 if (mTabScrollView != null && !mActionView.hasEmbeddedTabs() && mActionView.isCollapsed()) { 834 mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); 835 } 836 } 837 838 public Context getThemedContext() { 839 if (mThemedContext == null) { 840 TypedValue outValue = new TypedValue(); 841 Resources.Theme currentTheme = mContext.getTheme(); 842 currentTheme.resolveAttribute(com.android.internal.R.attr.actionBarWidgetTheme, 843 outValue, true); 844 final int targetThemeRes = outValue.resourceId; 845 846 if (targetThemeRes != 0 && mContext.getThemeResId() != targetThemeRes) { 847 mThemedContext = new ContextThemeWrapper(mContext, targetThemeRes); 848 } else { 849 mThemedContext = mContext; 850 } 851 } 852 return mThemedContext; 853 } 854 855 @Override 856 public boolean isTitleTruncated() { 857 return mActionView != null && mActionView.isTitleTruncated(); 858 } 859 860 @Override 861 public void setHomeAsUpIndicator(Drawable indicator) { 862 mActionView.setHomeAsUpIndicator(indicator); 863 } 864 865 @Override 866 public void setHomeAsUpIndicator(int resId) { 867 mActionView.setHomeAsUpIndicator(resId); 868 } 869 870 @Override 871 public void setHomeActionContentDescription(CharSequence description) { 872 mActionView.setHomeActionContentDescription(description); 873 } 874 875 @Override 876 public void setHomeActionContentDescription(int resId) { 877 mActionView.setHomeActionContentDescription(resId); 878 } 879 880 @Override 881 public void onContentScrollStarted() { 882 if (mCurrentShowAnim != null) { 883 mCurrentShowAnim.cancel(); 884 mCurrentShowAnim = null; 885 } 886 } 887 888 @Override 889 public void onContentScrollStopped() { 890 } 891 892 /** 893 * @hide 894 */ 895 public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback { 896 private ActionMode.Callback mCallback; 897 private MenuBuilder mMenu; 898 private WeakReference<View> mCustomView; 899 900 public ActionModeImpl(ActionMode.Callback callback) { 901 mCallback = callback; 902 mMenu = new MenuBuilder(getThemedContext()) 903 .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 904 mMenu.setCallback(this); 905 } 906 907 @Override 908 public MenuInflater getMenuInflater() { 909 return new MenuInflater(getThemedContext()); 910 } 911 912 @Override 913 public Menu getMenu() { 914 return mMenu; 915 } 916 917 @Override 918 public void finish() { 919 if (mActionMode != this) { 920 // Not the active action mode - no-op 921 return; 922 } 923 924 // If this change in state is going to cause the action bar 925 // to be hidden, defer the onDestroy callback until the animation 926 // is finished and associated relayout is about to happen. This lets 927 // apps better anticipate visibility and layout behavior. 928 if (!checkShowingFlags(mHiddenByApp, mHiddenBySystem, false)) { 929 // With the current state but the action bar hidden, our 930 // overall showing state is going to be false. 931 mDeferredDestroyActionMode = this; 932 mDeferredModeDestroyCallback = mCallback; 933 } else { 934 mCallback.onDestroyActionMode(this); 935 } 936 mCallback = null; 937 animateToMode(false); 938 939 // Clear out the context mode views after the animation finishes 940 mContextView.closeMode(); 941 mActionView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 942 mOverlayLayout.setHideOnContentScrollEnabled(mHideOnContentScroll); 943 944 mActionMode = null; 945 } 946 947 @Override 948 public void invalidate() { 949 mMenu.stopDispatchingItemsChanged(); 950 try { 951 mCallback.onPrepareActionMode(this, mMenu); 952 } finally { 953 mMenu.startDispatchingItemsChanged(); 954 } 955 } 956 957 public boolean dispatchOnCreate() { 958 mMenu.stopDispatchingItemsChanged(); 959 try { 960 return mCallback.onCreateActionMode(this, mMenu); 961 } finally { 962 mMenu.startDispatchingItemsChanged(); 963 } 964 } 965 966 @Override 967 public void setCustomView(View view) { 968 mContextView.setCustomView(view); 969 mCustomView = new WeakReference<View>(view); 970 } 971 972 @Override 973 public void setSubtitle(CharSequence subtitle) { 974 mContextView.setSubtitle(subtitle); 975 } 976 977 @Override 978 public void setTitle(CharSequence title) { 979 mContextView.setTitle(title); 980 } 981 982 @Override 983 public void setTitle(int resId) { 984 setTitle(mContext.getResources().getString(resId)); 985 } 986 987 @Override 988 public void setSubtitle(int resId) { 989 setSubtitle(mContext.getResources().getString(resId)); 990 } 991 992 @Override 993 public CharSequence getTitle() { 994 return mContextView.getTitle(); 995 } 996 997 @Override 998 public CharSequence getSubtitle() { 999 return mContextView.getSubtitle(); 1000 } 1001 1002 @Override 1003 public void setTitleOptionalHint(boolean titleOptional) { 1004 super.setTitleOptionalHint(titleOptional); 1005 mContextView.setTitleOptional(titleOptional); 1006 } 1007 1008 @Override 1009 public boolean isTitleOptional() { 1010 return mContextView.isTitleOptional(); 1011 } 1012 1013 @Override 1014 public View getCustomView() { 1015 return mCustomView != null ? mCustomView.get() : null; 1016 } 1017 1018 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 1019 if (mCallback != null) { 1020 return mCallback.onActionItemClicked(this, item); 1021 } else { 1022 return false; 1023 } 1024 } 1025 1026 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 1027 } 1028 1029 public boolean onSubMenuSelected(SubMenuBuilder subMenu) { 1030 if (mCallback == null) { 1031 return false; 1032 } 1033 1034 if (!subMenu.hasVisibleItems()) { 1035 return true; 1036 } 1037 1038 new MenuPopupHelper(getThemedContext(), subMenu).show(); 1039 return true; 1040 } 1041 1042 public void onCloseSubMenu(SubMenuBuilder menu) { 1043 } 1044 1045 public void onMenuModeChange(MenuBuilder menu) { 1046 if (mCallback == null) { 1047 return; 1048 } 1049 invalidate(); 1050 mContextView.showOverflowMenu(); 1051 } 1052 } 1053 1054 /** 1055 * @hide 1056 */ 1057 public class TabImpl extends ActionBar.Tab { 1058 private ActionBar.TabListener mCallback; 1059 private Object mTag; 1060 private Drawable mIcon; 1061 private CharSequence mText; 1062 private CharSequence mContentDesc; 1063 private int mPosition = -1; 1064 private View mCustomView; 1065 1066 @Override 1067 public Object getTag() { 1068 return mTag; 1069 } 1070 1071 @Override 1072 public Tab setTag(Object tag) { 1073 mTag = tag; 1074 return this; 1075 } 1076 1077 public ActionBar.TabListener getCallback() { 1078 return mCallback; 1079 } 1080 1081 @Override 1082 public Tab setTabListener(ActionBar.TabListener callback) { 1083 mCallback = callback; 1084 return this; 1085 } 1086 1087 @Override 1088 public View getCustomView() { 1089 return mCustomView; 1090 } 1091 1092 @Override 1093 public Tab setCustomView(View view) { 1094 mCustomView = view; 1095 if (mPosition >= 0) { 1096 mTabScrollView.updateTab(mPosition); 1097 } 1098 return this; 1099 } 1100 1101 @Override 1102 public Tab setCustomView(int layoutResId) { 1103 return setCustomView(LayoutInflater.from(getThemedContext()) 1104 .inflate(layoutResId, null)); 1105 } 1106 1107 @Override 1108 public Drawable getIcon() { 1109 return mIcon; 1110 } 1111 1112 @Override 1113 public int getPosition() { 1114 return mPosition; 1115 } 1116 1117 public void setPosition(int position) { 1118 mPosition = position; 1119 } 1120 1121 @Override 1122 public CharSequence getText() { 1123 return mText; 1124 } 1125 1126 @Override 1127 public Tab setIcon(Drawable icon) { 1128 mIcon = icon; 1129 if (mPosition >= 0) { 1130 mTabScrollView.updateTab(mPosition); 1131 } 1132 return this; 1133 } 1134 1135 @Override 1136 public Tab setIcon(int resId) { 1137 return setIcon(mContext.getDrawable(resId)); 1138 } 1139 1140 @Override 1141 public Tab setText(CharSequence text) { 1142 mText = text; 1143 if (mPosition >= 0) { 1144 mTabScrollView.updateTab(mPosition); 1145 } 1146 return this; 1147 } 1148 1149 @Override 1150 public Tab setText(int resId) { 1151 return setText(mContext.getResources().getText(resId)); 1152 } 1153 1154 @Override 1155 public void select() { 1156 selectTab(this); 1157 } 1158 1159 @Override 1160 public Tab setContentDescription(int resId) { 1161 return setContentDescription(mContext.getResources().getText(resId)); 1162 } 1163 1164 @Override 1165 public Tab setContentDescription(CharSequence contentDesc) { 1166 mContentDesc = contentDesc; 1167 if (mPosition >= 0) { 1168 mTabScrollView.updateTab(mPosition); 1169 } 1170 return this; 1171 } 1172 1173 @Override 1174 public CharSequence getContentDescription() { 1175 return mContentDesc; 1176 } 1177 } 1178 1179 @Override 1180 public void setCustomView(View view) { 1181 mActionView.setCustomNavigationView(view); 1182 } 1183 1184 @Override 1185 public void setCustomView(View view, LayoutParams layoutParams) { 1186 view.setLayoutParams(layoutParams); 1187 mActionView.setCustomNavigationView(view); 1188 } 1189 1190 @Override 1191 public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) { 1192 mActionView.setDropdownAdapter(adapter); 1193 mActionView.setCallback(callback); 1194 } 1195 1196 @Override 1197 public int getSelectedNavigationIndex() { 1198 switch (mActionView.getNavigationMode()) { 1199 case NAVIGATION_MODE_TABS: 1200 return mSelectedTab != null ? mSelectedTab.getPosition() : -1; 1201 case NAVIGATION_MODE_LIST: 1202 return mActionView.getDropdownSelectedPosition(); 1203 default: 1204 return -1; 1205 } 1206 } 1207 1208 @Override 1209 public int getNavigationItemCount() { 1210 switch (mActionView.getNavigationMode()) { 1211 case NAVIGATION_MODE_TABS: 1212 return mTabs.size(); 1213 case NAVIGATION_MODE_LIST: 1214 SpinnerAdapter adapter = mActionView.getDropdownAdapter(); 1215 return adapter != null ? adapter.getCount() : 0; 1216 default: 1217 return 0; 1218 } 1219 } 1220 1221 @Override 1222 public int getTabCount() { 1223 return mTabs.size(); 1224 } 1225 1226 @Override 1227 public void setNavigationMode(int mode) { 1228 final int oldMode = mActionView.getNavigationMode(); 1229 switch (oldMode) { 1230 case NAVIGATION_MODE_TABS: 1231 mSavedTabPosition = getSelectedNavigationIndex(); 1232 selectTab(null); 1233 mTabScrollView.setVisibility(View.GONE); 1234 break; 1235 } 1236 if (oldMode != mode && !mHasEmbeddedTabs) { 1237 if (mOverlayLayout != null) { 1238 mOverlayLayout.requestFitSystemWindows(); 1239 } 1240 } 1241 mActionView.setNavigationMode(mode); 1242 switch (mode) { 1243 case NAVIGATION_MODE_TABS: 1244 ensureTabsExist(); 1245 mTabScrollView.setVisibility(View.VISIBLE); 1246 if (mSavedTabPosition != INVALID_POSITION) { 1247 setSelectedNavigationItem(mSavedTabPosition); 1248 mSavedTabPosition = INVALID_POSITION; 1249 } 1250 break; 1251 } 1252 mActionView.setCollapsable(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs); 1253 mOverlayLayout.setHasNonEmbeddedTabs(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs); 1254 } 1255 1256 @Override 1257 public Tab getTabAt(int index) { 1258 return mTabs.get(index); 1259 } 1260 1261 1262 @Override 1263 public void setIcon(int resId) { 1264 mActionView.setIcon(resId); 1265 } 1266 1267 @Override 1268 public void setIcon(Drawable icon) { 1269 mActionView.setIcon(icon); 1270 } 1271 1272 public boolean hasIcon() { 1273 return mActionView.hasIcon(); 1274 } 1275 1276 @Override 1277 public void setLogo(int resId) { 1278 mActionView.setLogo(resId); 1279 } 1280 1281 @Override 1282 public void setLogo(Drawable logo) { 1283 mActionView.setLogo(logo); 1284 } 1285 1286 public boolean hasLogo() { 1287 return mActionView.hasLogo(); 1288 } 1289 1290 public void setDefaultDisplayHomeAsUpEnabled(boolean enable) { 1291 if (!mDisplayHomeAsUpSet) { 1292 setDisplayHomeAsUpEnabled(enable); 1293 } 1294 } 1295} 1296