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