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