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