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