1 /* 2 * Copyright 2018 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.core.view; 18 19 import android.content.res.ColorStateList; 20 import android.graphics.PorterDuff; 21 import android.graphics.drawable.Drawable; 22 import android.os.Build; 23 import android.util.Log; 24 import android.view.KeyEvent; 25 import android.view.Menu; 26 import android.view.MenuItem; 27 import android.view.View; 28 29 import androidx.core.internal.view.SupportMenuItem; 30 31 /** 32 * Helper for accessing features in {@link android.view.MenuItem}. 33 * <p class="note"><strong>Note:</strong> You cannot get an instance of this class. Instead, 34 * it provides <em>static</em> methods that correspond to the methods in {@link 35 * android.view.MenuItem}, but take a {@link android.view.MenuItem} object as an additional 36 * argument.</p> 37 */ 38 public final class MenuItemCompat { 39 private static final String TAG = "MenuItemCompat"; 40 41 /** 42 * Never show this item as a button in an Action Bar. 43 * 44 * @deprecated Use {@link MenuItem#SHOW_AS_ACTION_NEVER} directly. 45 */ 46 @Deprecated 47 public static final int SHOW_AS_ACTION_NEVER = 0; 48 49 /** 50 * Show this item as a button in an Action Bar if the system 51 * decides there is room for it. 52 * 53 * @deprecated Use {@link MenuItem#SHOW_AS_ACTION_IF_ROOM} directly. 54 */ 55 @Deprecated 56 public static final int SHOW_AS_ACTION_IF_ROOM = 1; 57 58 /** 59 * Always show this item as a button in an Action Bar. Use sparingly! 60 * If too many items are set to always show in the Action Bar it can 61 * crowd the Action Bar and degrade the user experience on devices with 62 * smaller screens. A good rule of thumb is to have no more than 2 63 * items set to always show at a time. 64 * 65 * @deprecated Use {@link MenuItem#SHOW_AS_ACTION_ALWAYS} directly. 66 */ 67 @Deprecated 68 public static final int SHOW_AS_ACTION_ALWAYS = 2; 69 70 /** 71 * When this item is in the action bar, always show it with a 72 * text label even if it also has an icon specified. 73 * 74 * @deprecated Use {@link MenuItem#SHOW_AS_ACTION_WITH_TEXT} directly. 75 */ 76 @Deprecated 77 public static final int SHOW_AS_ACTION_WITH_TEXT = 4; 78 79 /** 80 * This item's action view collapses to a normal menu item. 81 * When expanded, the action view temporarily takes over 82 * a larger segment of its container. 83 * 84 * @deprecated Use {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} directly. 85 */ 86 @Deprecated 87 public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; 88 89 /** 90 * Interface definition for a callback to be invoked when a menu item marked with {@link 91 * #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} is expanded or collapsed. 92 * 93 * @see #expandActionView(android.view.MenuItem) 94 * @see #collapseActionView(android.view.MenuItem) 95 * @see #setShowAsAction(android.view.MenuItem, int) 96 * 97 * @deprecated Use {@link MenuItem.OnActionExpandListener} directly. 98 */ 99 @Deprecated 100 public interface OnActionExpandListener { 101 102 /** 103 * Called when a menu item with {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} 104 * is expanded. 105 * 106 * @param item Item that was expanded 107 * @return true if the item should expand, false if expansion should be suppressed. 108 */ onMenuItemActionExpand(MenuItem item)109 boolean onMenuItemActionExpand(MenuItem item); 110 111 /** 112 * Called when a menu item with {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} 113 * is collapsed. 114 * 115 * @param item Item that was collapsed 116 * @return true if the item should collapse, false if collapsing should be suppressed. 117 */ onMenuItemActionCollapse(MenuItem item)118 boolean onMenuItemActionCollapse(MenuItem item); 119 } 120 121 // ------------------------------------------------------------------- 122 123 /** 124 * Sets how this item should display in the presence of a compatible Action Bar. If the given 125 * item is compatible, this will call the item's supported implementation of 126 * {@link MenuItem#setShowAsAction(int)}. 127 * 128 * @param item - the item to change 129 * @param actionEnum - How the item should display. 130 * 131 * @deprecated Use {@link MenuItem#setShowAsAction(int)} directly. 132 */ 133 @Deprecated setShowAsAction(MenuItem item, int actionEnum)134 public static void setShowAsAction(MenuItem item, int actionEnum) { 135 item.setShowAsAction(actionEnum); 136 } 137 138 /** 139 * Set an action view for this menu item. An action view will be displayed in place 140 * of an automatically generated menu item element in the UI when this item is shown 141 * as an action within a parent. 142 * 143 * @param item the item to change 144 * @param view View to use for presenting this item to the user. 145 * @return This Item so additional setters can be called. 146 * 147 * @see #setShowAsAction(MenuItem, int) 148 * 149 * @deprecated Use {@link MenuItem#setActionView(View)} directly. 150 */ 151 @Deprecated setActionView(MenuItem item, View view)152 public static MenuItem setActionView(MenuItem item, View view) { 153 return item.setActionView(view); 154 } 155 156 /** 157 * Set an action view for this menu item. An action view will be displayed in place 158 * of an automatically generated menu item element in the UI when this item is shown 159 * as an action within a parent. 160 * <p> 161 * <strong>Note:</strong> Setting an action view overrides the action provider 162 * set via {@link #setActionProvider(MenuItem, ActionProvider)}. 163 * </p> 164 * 165 * @param item the item to change 166 * @param resId Layout resource to use for presenting this item to the user. 167 * @return This Item so additional setters can be called. 168 * 169 * @see #setShowAsAction(MenuItem, int) 170 * 171 * @deprecated Use {@link MenuItem#setActionView(int)} directly. 172 */ 173 @Deprecated setActionView(MenuItem item, int resId)174 public static MenuItem setActionView(MenuItem item, int resId) { 175 return item.setActionView(resId); 176 } 177 178 /** 179 * Returns the currently set action view for this menu item. 180 * 181 * @param item the item to query 182 * @return This item's action view 183 * 184 * @deprecated Use {@link MenuItem#getActionView()} directly. 185 */ 186 @Deprecated getActionView(MenuItem item)187 public static View getActionView(MenuItem item) { 188 return item.getActionView(); 189 } 190 191 /** 192 * Sets the {@link ActionProvider} responsible for creating an action view if 193 * the item is placed on the action bar. The provider also provides a default 194 * action invoked if the item is placed in the overflow menu. 195 * <p> 196 * <strong>Note:</strong> Setting an action provider overrides the action view 197 * set via {@link #setActionView(MenuItem, View)}. 198 * </p> 199 * 200 * @param item item to change 201 * @param provider The action provider. 202 * @return This Item so additional setters can be called. 203 * 204 * @see ActionProvider 205 */ setActionProvider(MenuItem item, ActionProvider provider)206 public static MenuItem setActionProvider(MenuItem item, ActionProvider provider) { 207 if (item instanceof SupportMenuItem) { 208 return ((SupportMenuItem) item).setSupportActionProvider(provider); 209 } 210 // TODO Wrap the support ActionProvider and assign it 211 Log.w(TAG, "setActionProvider: item does not implement SupportMenuItem; ignoring"); 212 return item; 213 } 214 215 /** 216 * Gets the {@link ActionProvider}. 217 * 218 * @return The action provider. 219 * 220 * @see ActionProvider 221 * @see #setActionProvider(MenuItem, ActionProvider) 222 */ getActionProvider(MenuItem item)223 public static ActionProvider getActionProvider(MenuItem item) { 224 if (item instanceof SupportMenuItem) { 225 return ((SupportMenuItem) item).getSupportActionProvider(); 226 } 227 228 // TODO Wrap the framework ActionProvider and return it 229 Log.w(TAG, "getActionProvider: item does not implement SupportMenuItem; returning null"); 230 return null; 231 } 232 233 /** 234 * Expand the action view associated with this menu item. 235 * The menu item must have an action view set, as well as 236 * the showAsAction flag {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. 237 * If a listener has been set using 238 * {@link #setOnActionExpandListener(MenuItem, OnActionExpandListener)} 239 * it will have its {@link OnActionExpandListener#onMenuItemActionExpand(MenuItem)} 240 * method invoked. The listener may return false from this method to prevent expanding 241 * the action view. 242 * 243 * @return true if the action view was expanded, false otherwise. 244 * 245 * @deprecated Use {@link MenuItem#expandActionView()} directly. 246 */ 247 @Deprecated expandActionView(MenuItem item)248 public static boolean expandActionView(MenuItem item) { 249 return item.expandActionView(); 250 } 251 252 /** 253 * Collapse the action view associated with this menu item. The menu item must have an action 254 * view set, as well as the showAsAction flag {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. If a 255 * listener has been set using {@link #setOnActionExpandListener(MenuItem, 256 * androidx.core.view.MenuItemCompat.OnActionExpandListener)} 257 * it will have its {@link 258 * androidx.core.view.MenuItemCompat.OnActionExpandListener#onMenuItemActionCollapse(MenuItem)} 259 * method invoked. The listener may return false from this method to prevent collapsing 260 * the action view. 261 * 262 * @return true if the action view was collapsed, false otherwise. 263 * 264 * @deprecated Use {@link MenuItem#collapseActionView()} directly. 265 */ 266 @Deprecated collapseActionView(MenuItem item)267 public static boolean collapseActionView(MenuItem item) { 268 return item.collapseActionView(); 269 } 270 271 /** 272 * Returns true if this menu item's action view has been expanded. 273 * 274 * @return true if the item's action view is expanded, false otherwise. 275 * @see #expandActionView(MenuItem) 276 * @see #collapseActionView(MenuItem) 277 * @see #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW 278 * @see androidx.core.view.MenuItemCompat.OnActionExpandListener 279 * 280 * @deprecated Use {@link MenuItem#isActionViewExpanded()} directly. 281 */ 282 @Deprecated isActionViewExpanded(MenuItem item)283 public static boolean isActionViewExpanded(MenuItem item) { 284 return item.isActionViewExpanded(); 285 } 286 287 /** 288 * Set an {@link OnActionExpandListener} on this menu 289 * item to be notified when the associated action view is expanded or collapsed. 290 * The menu item must be configured to expand or collapse its action view using the flag 291 * {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. 292 * 293 * @param listener Listener that will respond to expand/collapse events 294 * @return This menu item instance for call chaining 295 * 296 * @deprecated Use {@link MenuItem#setOnActionExpandListener(MenuItem.OnActionExpandListener)} 297 * directly. 298 */ 299 @Deprecated setOnActionExpandListener(MenuItem item, final OnActionExpandListener listener)300 public static MenuItem setOnActionExpandListener(MenuItem item, 301 final OnActionExpandListener listener) { 302 return item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() { 303 @Override 304 public boolean onMenuItemActionExpand(MenuItem item) { 305 return listener.onMenuItemActionExpand(item); 306 } 307 308 @Override 309 public boolean onMenuItemActionCollapse(MenuItem item) { 310 return listener.onMenuItemActionCollapse(item); 311 } 312 }); 313 } 314 315 /** 316 * Change the content description associated with this menu item. 317 * 318 * @param item item to change. 319 * @param contentDescription The new content description. 320 */ 321 public static void setContentDescription(MenuItem item, CharSequence contentDescription) { 322 if (item instanceof SupportMenuItem) { 323 ((SupportMenuItem) item).setContentDescription(contentDescription); 324 } else if (Build.VERSION.SDK_INT >= 26) { 325 item.setContentDescription(contentDescription); 326 } 327 } 328 329 /** 330 * Retrieve the content description associated with this menu item. 331 * 332 * @return The content description. 333 */ 334 public static CharSequence getContentDescription(MenuItem item) { 335 if (item instanceof SupportMenuItem) { 336 return ((SupportMenuItem) item).getContentDescription(); 337 } 338 if (Build.VERSION.SDK_INT >= 26) { 339 return item.getContentDescription(); 340 } 341 return null; 342 } 343 344 /** 345 * Change the tooltip text associated with this menu item. 346 * 347 * @param item item to change. 348 * @param tooltipText The new tooltip text 349 */ 350 public static void setTooltipText(MenuItem item, CharSequence tooltipText) { 351 if (item instanceof SupportMenuItem) { 352 ((SupportMenuItem) item).setTooltipText(tooltipText); 353 } else if (Build.VERSION.SDK_INT >= 26) { 354 item.setTooltipText(tooltipText); 355 } 356 } 357 358 /** 359 * Retrieve the tooltip text associated with this menu item. 360 * 361 * @return The tooltip text. 362 */ 363 public static CharSequence getTooltipText(MenuItem item) { 364 if (item instanceof SupportMenuItem) { 365 return ((SupportMenuItem) item).getTooltipText(); 366 } 367 if (Build.VERSION.SDK_INT >= 26) { 368 return item.getTooltipText(); 369 } 370 return null; 371 } 372 373 /** 374 * Change both the numeric and alphabetic shortcut associated with this 375 * item. Note that the shortcut will be triggered when the key that 376 * generates the given character is pressed along with the corresponding 377 * modifier key. Also note that case is not significant and that alphabetic 378 * shortcut characters will be handled in lower case. 379 * <p> 380 * See {@link Menu} for the menu types that support shortcuts. 381 * 382 * @param numericChar The numeric shortcut key. This is the shortcut when 383 * using a numeric (e.g., 12-key) keyboard. 384 * @param numericModifiers The numeric modifier associated with the shortcut. It should 385 * be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON}, 386 * {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON}, 387 * {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}. 388 * @param alphaChar The alphabetic shortcut key. This is the shortcut when 389 * using a keyboard with alphabetic keys. 390 * @param alphaModifiers The alphabetic modifier associated with the shortcut. It should 391 * be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON}, 392 * {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON}, 393 * {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}. 394 */ 395 public static void setShortcut(MenuItem item, char numericChar, char alphaChar, 396 int numericModifiers, int alphaModifiers) { 397 if (item instanceof SupportMenuItem) { 398 ((SupportMenuItem) item).setShortcut(numericChar, alphaChar, numericModifiers, 399 alphaModifiers); 400 } else if (Build.VERSION.SDK_INT >= 26) { 401 item.setShortcut(numericChar, alphaChar, numericModifiers, alphaModifiers); 402 } 403 } 404 405 /** 406 * Change the numeric shortcut and modifiers associated with this item. 407 * <p> 408 * See {@link Menu} for the menu types that support shortcuts. 409 * 410 * @param numericChar The numeric shortcut key. This is the shortcut when 411 * using a 12-key (numeric) keyboard. 412 * @param numericModifiers The modifier associated with the shortcut. It should 413 * be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON}, 414 * {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON}, 415 * {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}. 416 */ 417 public static void setNumericShortcut(MenuItem item, char numericChar, int numericModifiers) { 418 if (item instanceof SupportMenuItem) { 419 ((SupportMenuItem) item).setNumericShortcut(numericChar, numericModifiers); 420 } else if (Build.VERSION.SDK_INT >= 26) { 421 item.setNumericShortcut(numericChar, numericModifiers); 422 } 423 } 424 425 /** 426 * Return the modifiers for this menu item's numeric (12-key) shortcut. 427 * The modifier is a combination of {@link KeyEvent#META_META_ON}, 428 * {@link KeyEvent#META_CTRL_ON}, {@link KeyEvent#META_ALT_ON}, 429 * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_SYM_ON}, 430 * {@link KeyEvent#META_FUNCTION_ON}. 431 * For example, {@link KeyEvent#META_FUNCTION_ON}|{@link KeyEvent#META_CTRL_ON} 432 * 433 * @return Modifier associated with the numeric shortcut. 434 */ 435 public static int getNumericModifiers(MenuItem item) { 436 if (item instanceof SupportMenuItem) { 437 return ((SupportMenuItem) item).getNumericModifiers(); 438 } 439 if (Build.VERSION.SDK_INT >= 26) { 440 return item.getNumericModifiers(); 441 } 442 return 0; 443 } 444 445 /** 446 * Change the alphabetic shortcut associated with this item. The shortcut 447 * will be triggered when the key that generates the given character is 448 * pressed along with the modifier keys. Case is not significant and shortcut 449 * characters will be displayed in lower case. Note that menu items with 450 * the characters '\b' or '\n' as shortcuts will get triggered by the 451 * Delete key or Carriage Return key, respectively. 452 * <p> 453 * See {@link Menu} for the menu types that support shortcuts. 454 * 455 * @param alphaChar The alphabetic shortcut key. This is the shortcut when 456 * using a keyboard with alphabetic keys. 457 * @param alphaModifiers The modifier associated with the shortcut. It should 458 * be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON}, 459 * {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON}, 460 * {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}. 461 */ 462 public static void setAlphabeticShortcut(MenuItem item, char alphaChar, int alphaModifiers) { 463 if (item instanceof SupportMenuItem) { 464 ((SupportMenuItem) item).setAlphabeticShortcut(alphaChar, alphaModifiers); 465 } else if (Build.VERSION.SDK_INT >= 26) { 466 item.setAlphabeticShortcut(alphaChar, alphaModifiers); 467 } 468 } 469 470 /** 471 * Return the modifier for this menu item's alphabetic shortcut. 472 * The modifier is a combination of {@link KeyEvent#META_META_ON}, 473 * {@link KeyEvent#META_CTRL_ON}, {@link KeyEvent#META_ALT_ON}, 474 * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_SYM_ON}, 475 * {@link KeyEvent#META_FUNCTION_ON}. 476 * For example, {@link KeyEvent#META_FUNCTION_ON}|{@link KeyEvent#META_CTRL_ON} 477 * 478 * @return Modifier associated with the keyboard shortcut. 479 */ 480 public static int getAlphabeticModifiers(MenuItem item) { 481 if (item instanceof SupportMenuItem) { 482 return ((SupportMenuItem) item).getAlphabeticModifiers(); 483 } 484 if (Build.VERSION.SDK_INT >= 26) { 485 return item.getAlphabeticModifiers(); 486 } 487 return 0; 488 } 489 490 /** 491 * Applies a tint to the item's icon. Does not modify the 492 * current tint mode of that item, which is {@link PorterDuff.Mode#SRC_IN} by default. 493 * <p> 494 * Subsequent calls to {@link MenuItem#setIcon(Drawable)} or {@link MenuItem#setIcon(int)} will 495 * automatically mutate the icon and apply the specified tint and 496 * tint mode. 497 * 498 * @param tint the tint to apply, may be {@code null} to clear tint 499 * 500 * @see #getIconTintList(MenuItem) 501 */ 502 public static void setIconTintList(MenuItem item, ColorStateList tint) { 503 if (item instanceof SupportMenuItem) { 504 ((SupportMenuItem) item).setIconTintList(tint); 505 } else if (Build.VERSION.SDK_INT >= 26) { 506 item.setIconTintList(tint); 507 } 508 } 509 510 /** 511 * @return the tint applied to the item's icon 512 * @see #setIconTintList(MenuItem, ColorStateList) 513 */ 514 public static ColorStateList getIconTintList(MenuItem item) { 515 if (item instanceof SupportMenuItem) { 516 return ((SupportMenuItem) item).getIconTintList(); 517 } 518 if (Build.VERSION.SDK_INT >= 26) { 519 return item.getIconTintList(); 520 } 521 return null; 522 } 523 524 /** 525 * Specifies the blending mode used to apply the tint specified by 526 * {@link #setIconTintList(MenuItem, ColorStateList)} to the item's icon. The default mode is 527 * {@link PorterDuff.Mode#SRC_IN}. 528 * 529 * @param tintMode the blending mode used to apply the tint, may be 530 * {@code null} to clear tint 531 * @see #setIconTintList(MenuItem, ColorStateList) 532 */ 533 public static void setIconTintMode(MenuItem item, PorterDuff.Mode tintMode) { 534 if (item instanceof SupportMenuItem) { 535 ((SupportMenuItem) item).setIconTintMode(tintMode); 536 } else if (Build.VERSION.SDK_INT >= 26) { 537 item.setIconTintMode(tintMode); 538 } 539 } 540 541 /** 542 * Returns the blending mode used to apply the tint to the item's icon, if specified. 543 * 544 * @return the blending mode used to apply the tint to the item's icon 545 * @see #setIconTintMode(MenuItem, PorterDuff.Mode) 546 */ 547 public static PorterDuff.Mode getIconTintMode(MenuItem item) { 548 if (item instanceof SupportMenuItem) { 549 return ((SupportMenuItem) item).getIconTintMode(); 550 } 551 if (Build.VERSION.SDK_INT >= 26) { 552 return item.getIconTintMode(); 553 } 554 return null; 555 } 556 557 private MenuItemCompat() {} 558 } 559