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