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