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