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