MenuItemImpl.java revision b31c3281d870e9abb673db239234d580dcc4feff
1/* 2 * Copyright (C) 2006 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 androidx.appcompat.view.menu; 18 19import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 20 21import android.content.ActivityNotFoundException; 22import android.content.Context; 23import android.content.Intent; 24import android.content.res.ColorStateList; 25import android.content.res.Resources; 26import android.graphics.PorterDuff; 27import android.graphics.drawable.Drawable; 28import android.os.Build; 29import androidx.annotation.Nullable; 30import androidx.annotation.RestrictTo; 31import androidx.core.graphics.drawable.DrawableCompat; 32import androidx.core.internal.view.SupportMenuItem; 33import androidx.core.view.ActionProvider; 34import androidx.appcompat.content.res.AppCompatResources; 35import androidx.appcompat.R; 36import android.util.Log; 37import android.view.ContextMenu.ContextMenuInfo; 38import android.view.KeyEvent; 39import android.view.LayoutInflater; 40import android.view.MenuItem; 41import android.view.SubMenu; 42import android.view.View; 43import android.view.ViewConfiguration; 44import android.view.ViewDebug; 45import android.widget.LinearLayout; 46 47/** 48 * @hide 49 */ 50@RestrictTo(LIBRARY_GROUP) 51public final class MenuItemImpl implements SupportMenuItem { 52 53 private static final String TAG = "MenuItemImpl"; 54 55 private static final int SHOW_AS_ACTION_MASK = SHOW_AS_ACTION_NEVER | 56 SHOW_AS_ACTION_IF_ROOM | 57 SHOW_AS_ACTION_ALWAYS; 58 59 private final int mId; 60 private final int mGroup; 61 private final int mCategoryOrder; 62 private final int mOrdering; 63 private CharSequence mTitle; 64 private CharSequence mTitleCondensed; 65 private Intent mIntent; 66 private char mShortcutNumericChar; 67 private int mShortcutNumericModifiers = KeyEvent.META_CTRL_ON; 68 private char mShortcutAlphabeticChar; 69 private int mShortcutAlphabeticModifiers = KeyEvent.META_CTRL_ON; 70 71 /** The icon's drawable which is only created as needed */ 72 private Drawable mIconDrawable; 73 74 /** 75 * The icon's resource ID which is used to get the Drawable when it is 76 * needed (if the Drawable isn't already obtained--only one of the two is 77 * needed). 78 */ 79 private int mIconResId = NO_ICON; 80 81 /** The menu to which this item belongs */ 82 MenuBuilder mMenu; 83 /** If this item should launch a sub menu, this is the sub menu to launch */ 84 private SubMenuBuilder mSubMenu; 85 86 private Runnable mItemCallback; 87 private SupportMenuItem.OnMenuItemClickListener mClickListener; 88 89 private CharSequence mContentDescription; 90 private CharSequence mTooltipText; 91 92 private ColorStateList mIconTintList = null; 93 private PorterDuff.Mode mIconTintMode = null; 94 private boolean mHasIconTint = false; 95 private boolean mHasIconTintMode = false; 96 private boolean mNeedToApplyIconTint = false; 97 98 private int mFlags = ENABLED; 99 private static final int CHECKABLE = 0x00000001; 100 private static final int CHECKED = 0x00000002; 101 private static final int EXCLUSIVE = 0x00000004; 102 private static final int HIDDEN = 0x00000008; 103 private static final int ENABLED = 0x00000010; 104 private static final int IS_ACTION = 0x00000020; 105 106 private int mShowAsAction = SHOW_AS_ACTION_NEVER; 107 108 private View mActionView; 109 private ActionProvider mActionProvider; 110 private MenuItem.OnActionExpandListener mOnActionExpandListener; 111 private boolean mIsActionViewExpanded = false; 112 113 /** Used for the icon resource ID if this item does not have an icon */ 114 static final int NO_ICON = 0; 115 116 /** 117 * Current use case is for context menu: Extra information linked to the 118 * View that added this item to the context menu. 119 */ 120 private ContextMenuInfo mMenuInfo; 121 122 123 /** 124 * Instantiates this menu item. 125 * 126 * @param menu 127 * @param group Item ordering grouping control. The item will be added after 128 * all other items whose order is <= this number, and before any 129 * that are larger than it. This can also be used to define 130 * groups of items for batch state changes. Normally use 0. 131 * @param id Unique item ID. Use 0 if you do not need a unique ID. 132 * @param categoryOrder The ordering for this item. 133 * @param title The text to display for the item. 134 */ 135 MenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering, 136 CharSequence title, int showAsAction) { 137 138 mMenu = menu; 139 mId = id; 140 mGroup = group; 141 mCategoryOrder = categoryOrder; 142 mOrdering = ordering; 143 mTitle = title; 144 mShowAsAction = showAsAction; 145 } 146 147 /** 148 * Invokes the item by calling various listeners or callbacks. 149 * 150 * @return true if the invocation was handled, false otherwise 151 */ 152 public boolean invoke() { 153 if (mClickListener != null && mClickListener.onMenuItemClick(this)) { 154 return true; 155 } 156 157 if (mMenu.dispatchMenuItemSelected(mMenu, this)) { 158 return true; 159 } 160 161 if (mItemCallback != null) { 162 mItemCallback.run(); 163 return true; 164 } 165 166 if (mIntent != null) { 167 try { 168 mMenu.getContext().startActivity(mIntent); 169 return true; 170 } catch (ActivityNotFoundException e) { 171 Log.e(TAG, "Can't find activity to handle intent; ignoring", e); 172 } 173 } 174 175 if (mActionProvider != null && mActionProvider.onPerformDefaultAction()) { 176 return true; 177 } 178 179 return false; 180 } 181 182 @Override 183 public boolean isEnabled() { 184 return (mFlags & ENABLED) != 0; 185 } 186 187 @Override 188 public MenuItem setEnabled(boolean enabled) { 189 if (enabled) { 190 mFlags |= ENABLED; 191 } else { 192 mFlags &= ~ENABLED; 193 } 194 195 mMenu.onItemsChanged(false); 196 197 return this; 198 } 199 200 @Override 201 public int getGroupId() { 202 return mGroup; 203 } 204 205 @Override 206 @ViewDebug.CapturedViewProperty 207 public int getItemId() { 208 return mId; 209 } 210 211 @Override 212 public int getOrder() { 213 return mCategoryOrder; 214 } 215 216 public int getOrdering() { 217 return mOrdering; 218 } 219 220 @Override 221 public Intent getIntent() { 222 return mIntent; 223 } 224 225 @Override 226 public MenuItem setIntent(Intent intent) { 227 mIntent = intent; 228 return this; 229 } 230 231 Runnable getCallback() { 232 return mItemCallback; 233 } 234 235 public MenuItem setCallback(Runnable callback) { 236 mItemCallback = callback; 237 return this; 238 } 239 240 @Override 241 public char getAlphabeticShortcut() { 242 return mShortcutAlphabeticChar; 243 } 244 245 @Override 246 public MenuItem setAlphabeticShortcut(char alphaChar) { 247 if (mShortcutAlphabeticChar == alphaChar) { 248 return this; 249 } 250 251 mShortcutAlphabeticChar = Character.toLowerCase(alphaChar); 252 253 mMenu.onItemsChanged(false); 254 255 return this; 256 } 257 258 @Override 259 public MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers) { 260 if (mShortcutAlphabeticChar == alphaChar 261 && mShortcutAlphabeticModifiers == alphaModifiers) { 262 return this; 263 } 264 265 mShortcutAlphabeticChar = Character.toLowerCase(alphaChar); 266 mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers); 267 268 mMenu.onItemsChanged(false); 269 return this; 270 } 271 272 @Override 273 public int getAlphabeticModifiers() { 274 return mShortcutAlphabeticModifiers; 275 } 276 277 @Override 278 public char getNumericShortcut() { 279 return mShortcutNumericChar; 280 } 281 282 @Override 283 public int getNumericModifiers() { 284 return mShortcutNumericModifiers; 285 } 286 287 @Override 288 public MenuItem setNumericShortcut(char numericChar) { 289 if (mShortcutNumericChar == numericChar) { 290 return this; 291 } 292 293 mShortcutNumericChar = numericChar; 294 295 mMenu.onItemsChanged(false); 296 297 return this; 298 } 299 300 @Override 301 public MenuItem setNumericShortcut(char numericChar, int numericModifiers) { 302 if (mShortcutNumericChar == numericChar && mShortcutNumericModifiers == numericModifiers) { 303 return this; 304 } 305 306 mShortcutNumericChar = numericChar; 307 mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers); 308 309 mMenu.onItemsChanged(false); 310 311 return this; 312 } 313 314 @Override 315 public MenuItem setShortcut(char numericChar, char alphaChar) { 316 mShortcutNumericChar = numericChar; 317 mShortcutAlphabeticChar = Character.toLowerCase(alphaChar); 318 319 mMenu.onItemsChanged(false); 320 321 return this; 322 } 323 324 @Override 325 public MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers, 326 int alphaModifiers) { 327 mShortcutNumericChar = numericChar; 328 mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers); 329 mShortcutAlphabeticChar = Character.toLowerCase(alphaChar); 330 mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers); 331 332 mMenu.onItemsChanged(false); 333 334 return this; 335 } 336 337 /** 338 * @return The active shortcut (based on QWERTY-mode of the menu). 339 */ 340 char getShortcut() { 341 return (mMenu.isQwertyMode() ? mShortcutAlphabeticChar : mShortcutNumericChar); 342 } 343 344 /** 345 * @return The label to show for the shortcut. This includes the chording key (for example 346 * 'Menu+a'). Also, any non-human readable characters should be human readable (for 347 * example 'Menu+enter'). 348 */ 349 String getShortcutLabel() { 350 351 char shortcut = getShortcut(); 352 if (shortcut == 0) { 353 return ""; 354 } 355 356 Resources res = mMenu.getContext().getResources(); 357 358 StringBuilder sb = new StringBuilder(); 359 if (ViewConfiguration.get(mMenu.getContext()).hasPermanentMenuKey()) { 360 sb.append(res.getString(R.string.abc_prepend_shortcut_label)); 361 } 362 363 final int modifiers = 364 mMenu.isQwertyMode() ? mShortcutAlphabeticModifiers : mShortcutNumericModifiers; 365 appendModifier(sb, modifiers, KeyEvent.META_META_ON, 366 res.getString(R.string.abc_menu_meta_shortcut_label)); 367 appendModifier(sb, modifiers, KeyEvent.META_CTRL_ON, 368 res.getString(R.string.abc_menu_ctrl_shortcut_label)); 369 appendModifier(sb, modifiers, KeyEvent.META_ALT_ON, 370 res.getString(R.string.abc_menu_alt_shortcut_label)); 371 appendModifier(sb, modifiers, KeyEvent.META_SHIFT_ON, 372 res.getString(R.string.abc_menu_shift_shortcut_label)); 373 appendModifier(sb, modifiers, KeyEvent.META_SYM_ON, 374 res.getString(R.string.abc_menu_sym_shortcut_label)); 375 appendModifier(sb, modifiers, KeyEvent.META_FUNCTION_ON, 376 res.getString(R.string.abc_menu_function_shortcut_label)); 377 378 switch (shortcut) { 379 380 case '\n': 381 sb.append(res.getString(R.string.abc_menu_enter_shortcut_label)); 382 break; 383 384 case '\b': 385 sb.append(res.getString(R.string.abc_menu_delete_shortcut_label)); 386 break; 387 388 case ' ': 389 sb.append(res.getString(R.string.abc_menu_space_shortcut_label)); 390 break; 391 392 default: 393 sb.append(shortcut); 394 break; 395 } 396 397 return sb.toString(); 398 } 399 400 private static void appendModifier(StringBuilder sb, int modifiers, int flag, String label) { 401 if ((modifiers & flag) == flag) { 402 sb.append(label); 403 } 404 } 405 406 /** 407 * @return Whether this menu item should be showing shortcuts (depends on 408 * whether the menu should show shortcuts and whether this item has 409 * a shortcut defined) 410 */ 411 boolean shouldShowShortcut() { 412 // Show shortcuts if the menu is supposed to show shortcuts AND this item has a shortcut 413 return mMenu.isShortcutsVisible() && (getShortcut() != 0); 414 } 415 416 @Override 417 public SubMenu getSubMenu() { 418 return mSubMenu; 419 } 420 421 @Override 422 public boolean hasSubMenu() { 423 return mSubMenu != null; 424 } 425 426 public void setSubMenu(SubMenuBuilder subMenu) { 427 mSubMenu = subMenu; 428 429 subMenu.setHeaderTitle(getTitle()); 430 } 431 432 @Override 433 @ViewDebug.CapturedViewProperty 434 public CharSequence getTitle() { 435 return mTitle; 436 } 437 438 /** 439 * Gets the title for a particular {@link MenuView.ItemView} 440 * 441 * @param itemView The ItemView that is receiving the title 442 * @return Either the title or condensed title based on what the ItemView prefers 443 */ 444 CharSequence getTitleForItemView(MenuView.ItemView itemView) { 445 return ((itemView != null) && itemView.prefersCondensedTitle()) 446 ? getTitleCondensed() 447 : getTitle(); 448 } 449 450 @Override 451 public MenuItem setTitle(CharSequence title) { 452 mTitle = title; 453 454 mMenu.onItemsChanged(false); 455 456 if (mSubMenu != null) { 457 mSubMenu.setHeaderTitle(title); 458 } 459 460 return this; 461 } 462 463 @Override 464 public MenuItem setTitle(int title) { 465 return setTitle(mMenu.getContext().getString(title)); 466 } 467 468 @Override 469 public CharSequence getTitleCondensed() { 470 final CharSequence ctitle = mTitleCondensed != null ? mTitleCondensed : mTitle; 471 472 if (Build.VERSION.SDK_INT < 18 && ctitle != null && !(ctitle instanceof String)) { 473 // For devices pre-JB-MR2, where we have a non-String CharSequence, we need to 474 // convert this to a String so that EventLog.writeEvent() does not throw an exception 475 // in Activity.onMenuItemSelected() 476 return ctitle.toString(); 477 } else { 478 // Else, we just return the condensed title 479 return ctitle; 480 } 481 } 482 483 @Override 484 public MenuItem setTitleCondensed(CharSequence title) { 485 mTitleCondensed = title; 486 487 // Could use getTitle() in the loop below, but just cache what it would do here 488 if (title == null) { 489 title = mTitle; 490 } 491 492 mMenu.onItemsChanged(false); 493 494 return this; 495 } 496 497 @Override 498 public Drawable getIcon() { 499 if (mIconDrawable != null) { 500 return applyIconTintIfNecessary(mIconDrawable); 501 } 502 503 if (mIconResId != NO_ICON) { 504 Drawable icon = AppCompatResources.getDrawable(mMenu.getContext(), mIconResId); 505 mIconResId = NO_ICON; 506 mIconDrawable = icon; 507 return applyIconTintIfNecessary(icon); 508 } 509 510 return null; 511 } 512 513 @Override 514 public MenuItem setIcon(Drawable icon) { 515 mIconResId = NO_ICON; 516 mIconDrawable = icon; 517 mNeedToApplyIconTint = true; 518 mMenu.onItemsChanged(false); 519 520 return this; 521 } 522 523 @Override 524 public MenuItem setIcon(int iconResId) { 525 mIconDrawable = null; 526 mIconResId = iconResId; 527 mNeedToApplyIconTint = true; 528 529 // If we have a view, we need to push the Drawable to them 530 mMenu.onItemsChanged(false); 531 532 return this; 533 } 534 535 536 @Override 537 public MenuItem setIconTintList(@Nullable ColorStateList iconTintList) { 538 mIconTintList = iconTintList; 539 mHasIconTint = true; 540 mNeedToApplyIconTint = true; 541 542 mMenu.onItemsChanged(false); 543 544 return this; 545 } 546 547 @Override 548 public ColorStateList getIconTintList() { 549 return mIconTintList; 550 } 551 552 @Override 553 public MenuItem setIconTintMode(PorterDuff.Mode iconTintMode) { 554 mIconTintMode = iconTintMode; 555 mHasIconTintMode = true; 556 mNeedToApplyIconTint = true; 557 558 mMenu.onItemsChanged(false); 559 560 return this; 561 } 562 563 @Override 564 public PorterDuff.Mode getIconTintMode() { 565 return mIconTintMode; 566 } 567 568 private Drawable applyIconTintIfNecessary(Drawable icon) { 569 if (icon != null && mNeedToApplyIconTint && (mHasIconTint || mHasIconTintMode)) { 570 icon = DrawableCompat.wrap(icon); 571 icon = icon.mutate(); 572 573 if (mHasIconTint) { 574 DrawableCompat.setTintList(icon, mIconTintList); 575 } 576 577 if (mHasIconTintMode) { 578 DrawableCompat.setTintMode(icon, mIconTintMode); 579 } 580 581 mNeedToApplyIconTint = false; 582 } 583 584 return icon; 585 } 586 587 @Override 588 public boolean isCheckable() { 589 return (mFlags & CHECKABLE) == CHECKABLE; 590 } 591 592 @Override 593 public MenuItem setCheckable(boolean checkable) { 594 final int oldFlags = mFlags; 595 mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0); 596 if (oldFlags != mFlags) { 597 mMenu.onItemsChanged(false); 598 } 599 600 return this; 601 } 602 603 public void setExclusiveCheckable(boolean exclusive) { 604 mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0); 605 } 606 607 public boolean isExclusiveCheckable() { 608 return (mFlags & EXCLUSIVE) != 0; 609 } 610 611 @Override 612 public boolean isChecked() { 613 return (mFlags & CHECKED) == CHECKED; 614 } 615 616 @Override 617 public MenuItem setChecked(boolean checked) { 618 if ((mFlags & EXCLUSIVE) != 0) { 619 // Call the method on the Menu since it knows about the others in this 620 // exclusive checkable group 621 mMenu.setExclusiveItemChecked(this); 622 } else { 623 setCheckedInt(checked); 624 } 625 626 return this; 627 } 628 629 void setCheckedInt(boolean checked) { 630 final int oldFlags = mFlags; 631 mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0); 632 if (oldFlags != mFlags) { 633 mMenu.onItemsChanged(false); 634 } 635 } 636 637 @Override 638 public boolean isVisible() { 639 if (mActionProvider != null && mActionProvider.overridesItemVisibility()) { 640 return (mFlags & HIDDEN) == 0 && mActionProvider.isVisible(); 641 } 642 return (mFlags & HIDDEN) == 0; 643 } 644 645 /** 646 * Changes the visibility of the item. This method DOES NOT notify the parent menu of a change 647 * in this item, so this should only be called from methods that will eventually trigger this 648 * change. If unsure, use {@link #setVisible(boolean)} instead. 649 * 650 * @param shown Whether to show (true) or hide (false). 651 * @return Whether the item's shown state was changed 652 */ 653 boolean setVisibleInt(boolean shown) { 654 final int oldFlags = mFlags; 655 mFlags = (mFlags & ~HIDDEN) | (shown ? 0 : HIDDEN); 656 return oldFlags != mFlags; 657 } 658 659 @Override 660 public MenuItem setVisible(boolean shown) { 661 // Try to set the shown state to the given state. If the shown state was changed 662 // (i.e. the previous state isn't the same as given state), notify the parent menu that 663 // the shown state has changed for this item 664 if (setVisibleInt(shown)) mMenu.onItemVisibleChanged(this); 665 666 return this; 667 } 668 669 @Override 670 public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener clickListener) { 671 mClickListener = clickListener; 672 return this; 673 } 674 675 @Override 676 public String toString() { 677 return mTitle != null ? mTitle.toString() : null; 678 } 679 680 void setMenuInfo(ContextMenuInfo menuInfo) { 681 mMenuInfo = menuInfo; 682 } 683 684 @Override 685 public ContextMenuInfo getMenuInfo() { 686 return mMenuInfo; 687 } 688 689 public void actionFormatChanged() { 690 mMenu.onItemActionRequestChanged(this); 691 } 692 693 /** 694 * @return Whether the menu should show icons for menu items. 695 */ 696 public boolean shouldShowIcon() { 697 return mMenu.getOptionalIconsVisible(); 698 } 699 700 public boolean isActionButton() { 701 return (mFlags & IS_ACTION) == IS_ACTION; 702 } 703 704 public boolean requestsActionButton() { 705 return (mShowAsAction & SHOW_AS_ACTION_IF_ROOM) == SHOW_AS_ACTION_IF_ROOM; 706 } 707 708 public boolean requiresActionButton() { 709 return (mShowAsAction & SHOW_AS_ACTION_ALWAYS) == SHOW_AS_ACTION_ALWAYS; 710 } 711 712 public void setIsActionButton(boolean isActionButton) { 713 if (isActionButton) { 714 mFlags |= IS_ACTION; 715 } else { 716 mFlags &= ~IS_ACTION; 717 } 718 } 719 720 public boolean showsTextAsAction() { 721 return (mShowAsAction & SHOW_AS_ACTION_WITH_TEXT) == SHOW_AS_ACTION_WITH_TEXT; 722 } 723 724 @Override 725 public void setShowAsAction(int actionEnum) { 726 switch (actionEnum & SHOW_AS_ACTION_MASK) { 727 case SHOW_AS_ACTION_ALWAYS: 728 case SHOW_AS_ACTION_IF_ROOM: 729 case SHOW_AS_ACTION_NEVER: 730 // Looks good! 731 break; 732 733 default: 734 // Mutually exclusive options selected! 735 throw new IllegalArgumentException("SHOW_AS_ACTION_ALWAYS, SHOW_AS_ACTION_IF_ROOM," 736 + " and SHOW_AS_ACTION_NEVER are mutually exclusive."); 737 } 738 mShowAsAction = actionEnum; 739 mMenu.onItemActionRequestChanged(this); 740 } 741 742 @Override 743 public SupportMenuItem setActionView(View view) { 744 mActionView = view; 745 mActionProvider = null; 746 if (view != null && view.getId() == View.NO_ID && mId > 0) { 747 view.setId(mId); 748 } 749 mMenu.onItemActionRequestChanged(this); 750 return this; 751 } 752 753 @Override 754 public SupportMenuItem setActionView(int resId) { 755 final Context context = mMenu.getContext(); 756 final LayoutInflater inflater = LayoutInflater.from(context); 757 setActionView(inflater.inflate(resId, new LinearLayout(context), false)); 758 return this; 759 } 760 761 @Override 762 public View getActionView() { 763 if (mActionView != null) { 764 return mActionView; 765 } else if (mActionProvider != null) { 766 mActionView = mActionProvider.onCreateActionView(this); 767 return mActionView; 768 } else { 769 return null; 770 } 771 } 772 773 @Override 774 public MenuItem setActionProvider(android.view.ActionProvider actionProvider) { 775 throw new UnsupportedOperationException( 776 "This is not supported, use MenuItemCompat.setActionProvider()"); 777 } 778 779 @Override 780 public android.view.ActionProvider getActionProvider() { 781 throw new UnsupportedOperationException( 782 "This is not supported, use MenuItemCompat.getActionProvider()"); 783 } 784 785 @Override 786 public ActionProvider getSupportActionProvider() { 787 return mActionProvider; 788 } 789 790 @Override 791 public SupportMenuItem setSupportActionProvider(ActionProvider actionProvider) { 792 if (mActionProvider != null) { 793 mActionProvider.reset(); 794 } 795 mActionView = null; 796 mActionProvider = actionProvider; 797 mMenu.onItemsChanged(true); // Measurement can be changed 798 if (mActionProvider != null) { 799 mActionProvider.setVisibilityListener(new ActionProvider.VisibilityListener() { 800 @Override 801 public void onActionProviderVisibilityChanged(boolean isVisible) { 802 mMenu.onItemVisibleChanged(MenuItemImpl.this); 803 } 804 }); 805 } 806 return this; 807 } 808 809 @Override 810 public SupportMenuItem setShowAsActionFlags(int actionEnum) { 811 setShowAsAction(actionEnum); 812 return this; 813 } 814 815 @Override 816 public boolean expandActionView() { 817 if (!hasCollapsibleActionView()) { 818 return false; 819 } 820 821 if (mOnActionExpandListener == null || 822 mOnActionExpandListener.onMenuItemActionExpand(this)) { 823 return mMenu.expandItemActionView(this); 824 } 825 826 return false; 827 } 828 829 @Override 830 public boolean collapseActionView() { 831 if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0) { 832 return false; 833 } 834 if (mActionView == null) { 835 // We're already collapsed if we have no action view. 836 return true; 837 } 838 839 if (mOnActionExpandListener == null || 840 mOnActionExpandListener.onMenuItemActionCollapse(this)) { 841 return mMenu.collapseItemActionView(this); 842 } 843 844 return false; 845 } 846 847 public boolean hasCollapsibleActionView() { 848 if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) != 0) { 849 if (mActionView == null && mActionProvider != null) { 850 mActionView = mActionProvider.onCreateActionView(this); 851 } 852 return mActionView != null; 853 } 854 return false; 855 } 856 857 public void setActionViewExpanded(boolean isExpanded) { 858 mIsActionViewExpanded = isExpanded; 859 mMenu.onItemsChanged(false); 860 } 861 862 @Override 863 public boolean isActionViewExpanded() { 864 return mIsActionViewExpanded; 865 } 866 867 @Override 868 public MenuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener) { 869 mOnActionExpandListener = listener; 870 return this; 871 } 872 873 @Override 874 public SupportMenuItem setContentDescription(CharSequence contentDescription) { 875 mContentDescription = contentDescription; 876 877 mMenu.onItemsChanged(false); 878 879 return this; 880 } 881 882 @Override 883 public CharSequence getContentDescription() { 884 return mContentDescription; 885 } 886 887 @Override 888 public SupportMenuItem setTooltipText(CharSequence tooltipText) { 889 mTooltipText = tooltipText; 890 891 mMenu.onItemsChanged(false); 892 893 return this; 894 } 895 896 @Override 897 public CharSequence getTooltipText() { 898 return mTooltipText; 899 } 900} 901