1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.appcompat.view.menu; 18 19 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX; 20 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.res.ColorStateList; 24 import android.graphics.PorterDuff; 25 import android.graphics.drawable.Drawable; 26 import android.view.ContextMenu.ContextMenuInfo; 27 import android.view.KeyEvent; 28 import android.view.MenuItem; 29 import android.view.SubMenu; 30 import android.view.View; 31 32 import androidx.annotation.RestrictTo; 33 import androidx.core.content.ContextCompat; 34 import androidx.core.graphics.drawable.DrawableCompat; 35 import androidx.core.internal.view.SupportMenuItem; 36 import androidx.core.view.ActionProvider; 37 38 import org.jspecify.annotations.NonNull; 39 import org.jspecify.annotations.Nullable; 40 41 /** 42 */ 43 @RestrictTo(LIBRARY_GROUP_PREFIX) 44 public class ActionMenuItem implements SupportMenuItem { 45 46 private final int mId; 47 private final int mGroup; 48 private final int mOrdering; 49 50 private CharSequence mTitle; 51 private CharSequence mTitleCondensed; 52 private Intent mIntent; 53 private char mShortcutNumericChar; 54 private int mShortcutNumericModifiers = KeyEvent.META_CTRL_ON; 55 private char mShortcutAlphabeticChar; 56 private int mShortcutAlphabeticModifiers = KeyEvent.META_CTRL_ON; 57 58 private Drawable mIconDrawable; 59 60 private Context mContext; 61 62 private SupportMenuItem.OnMenuItemClickListener mClickListener; 63 64 private CharSequence mContentDescription; 65 private CharSequence mTooltipText; 66 67 private ColorStateList mIconTintList = null; 68 private PorterDuff.Mode mIconTintMode = null; 69 private boolean mHasIconTint = false; 70 private boolean mHasIconTintMode = false; 71 72 private int mFlags = ENABLED; 73 private static final int CHECKABLE = 0x00000001; 74 private static final int CHECKED = 0x00000002; 75 private static final int EXCLUSIVE = 0x00000004; 76 private static final int HIDDEN = 0x00000008; 77 private static final int ENABLED = 0x00000010; 78 ActionMenuItem(Context context, int group, int id, int categoryOrder, int ordering, CharSequence title)79 public ActionMenuItem(Context context, int group, int id, int categoryOrder, int ordering, 80 CharSequence title) { 81 mContext = context; 82 mId = id; 83 mGroup = group; 84 mOrdering = ordering; 85 mTitle = title; 86 } 87 88 @Override getAlphabeticShortcut()89 public char getAlphabeticShortcut() { 90 return mShortcutAlphabeticChar; 91 } 92 93 @Override getAlphabeticModifiers()94 public int getAlphabeticModifiers() { 95 return mShortcutAlphabeticModifiers; 96 } 97 98 @Override getGroupId()99 public int getGroupId() { 100 return mGroup; 101 } 102 103 @Override getIcon()104 public Drawable getIcon() { 105 return mIconDrawable; 106 } 107 108 @Override getIntent()109 public Intent getIntent() { 110 return mIntent; 111 } 112 113 @Override getItemId()114 public int getItemId() { 115 return mId; 116 } 117 118 @Override getMenuInfo()119 public ContextMenuInfo getMenuInfo() { 120 return null; 121 } 122 123 @Override getNumericShortcut()124 public char getNumericShortcut() { 125 return mShortcutNumericChar; 126 } 127 128 @Override getNumericModifiers()129 public int getNumericModifiers() { 130 return mShortcutNumericModifiers; 131 } 132 133 @Override getOrder()134 public int getOrder() { 135 return mOrdering; 136 } 137 138 @Override getSubMenu()139 public SubMenu getSubMenu() { 140 return null; 141 } 142 143 @Override getTitle()144 public CharSequence getTitle() { 145 return mTitle; 146 } 147 148 @Override getTitleCondensed()149 public CharSequence getTitleCondensed() { 150 return mTitleCondensed != null ? mTitleCondensed : mTitle; 151 } 152 153 @Override hasSubMenu()154 public boolean hasSubMenu() { 155 return false; 156 } 157 158 @Override isCheckable()159 public boolean isCheckable() { 160 return (mFlags & CHECKABLE) != 0; 161 } 162 163 @Override isChecked()164 public boolean isChecked() { 165 return (mFlags & CHECKED) != 0; 166 } 167 168 @Override isEnabled()169 public boolean isEnabled() { 170 return (mFlags & ENABLED) != 0; 171 } 172 173 @Override isVisible()174 public boolean isVisible() { 175 return (mFlags & HIDDEN) == 0; 176 } 177 178 @Override setAlphabeticShortcut(char alphaChar)179 public MenuItem setAlphabeticShortcut(char alphaChar) { 180 mShortcutAlphabeticChar = Character.toLowerCase(alphaChar); 181 return this; 182 } 183 184 @Override setAlphabeticShortcut(char alphaChar, int alphaModifiers)185 public @NonNull MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers) { 186 mShortcutAlphabeticChar = Character.toLowerCase(alphaChar); 187 mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers); 188 return this; 189 } 190 191 @Override setCheckable(boolean checkable)192 public MenuItem setCheckable(boolean checkable) { 193 mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0); 194 return this; 195 } 196 setExclusiveCheckable(boolean exclusive)197 public ActionMenuItem setExclusiveCheckable(boolean exclusive) { 198 mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0); 199 return this; 200 } 201 202 @Override setChecked(boolean checked)203 public MenuItem setChecked(boolean checked) { 204 mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0); 205 return this; 206 } 207 208 @Override setEnabled(boolean enabled)209 public MenuItem setEnabled(boolean enabled) { 210 mFlags = (mFlags & ~ENABLED) | (enabled ? ENABLED : 0); 211 return this; 212 } 213 214 @Override setIcon(Drawable icon)215 public MenuItem setIcon(Drawable icon) { 216 mIconDrawable = icon; 217 218 applyIconTint(); 219 return this; 220 } 221 222 @Override setIcon(int iconRes)223 public MenuItem setIcon(int iconRes) { 224 mIconDrawable = ContextCompat.getDrawable(mContext, iconRes); 225 226 applyIconTint(); 227 return this; 228 } 229 230 @Override setIntent(Intent intent)231 public MenuItem setIntent(Intent intent) { 232 mIntent = intent; 233 return this; 234 } 235 236 @Override setNumericShortcut(char numericChar)237 public MenuItem setNumericShortcut(char numericChar) { 238 mShortcutNumericChar = numericChar; 239 return this; 240 } 241 242 @Override setNumericShortcut(char numericChar, int numericModifiers)243 public @NonNull MenuItem setNumericShortcut(char numericChar, int numericModifiers) { 244 mShortcutNumericChar = numericChar; 245 mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers); 246 return this; 247 } 248 249 @Override setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener)250 public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) { 251 mClickListener = menuItemClickListener; 252 return this; 253 } 254 255 @Override setShortcut(char numericChar, char alphaChar)256 public MenuItem setShortcut(char numericChar, char alphaChar) { 257 mShortcutNumericChar = numericChar; 258 mShortcutAlphabeticChar = Character.toLowerCase(alphaChar); 259 return this; 260 } 261 262 @Override setShortcut(char numericChar, char alphaChar, int numericModifiers, int alphaModifiers)263 public @NonNull MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers, 264 int alphaModifiers) { 265 mShortcutNumericChar = numericChar; 266 mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers); 267 mShortcutAlphabeticChar = Character.toLowerCase(alphaChar); 268 mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers); 269 return this; 270 } 271 272 @Override setTitle(CharSequence title)273 public MenuItem setTitle(CharSequence title) { 274 mTitle = title; 275 return this; 276 } 277 278 @Override setTitle(int title)279 public MenuItem setTitle(int title) { 280 mTitle = mContext.getResources().getString(title); 281 return this; 282 } 283 284 @Override setTitleCondensed(CharSequence title)285 public MenuItem setTitleCondensed(CharSequence title) { 286 mTitleCondensed = title; 287 return this; 288 } 289 290 @Override setVisible(boolean visible)291 public MenuItem setVisible(boolean visible) { 292 mFlags = (mFlags & HIDDEN) | (visible ? 0 : HIDDEN); 293 return this; 294 } 295 invoke()296 public boolean invoke() { 297 if (mClickListener != null && mClickListener.onMenuItemClick(this)) { 298 return true; 299 } 300 301 if (mIntent != null) { 302 mContext.startActivity(mIntent); 303 return true; 304 } 305 306 return false; 307 } 308 309 @Override setShowAsAction(int show)310 public void setShowAsAction(int show) { 311 // Do nothing. ActionMenuItems always show as action buttons. 312 } 313 314 @Override requiresActionButton()315 public boolean requiresActionButton() { 316 return true; 317 } 318 319 @Override requiresOverflow()320 public boolean requiresOverflow() { 321 return false; 322 } 323 324 @Override setActionView(View actionView)325 public @NonNull SupportMenuItem setActionView(View actionView) { 326 throw new UnsupportedOperationException(); 327 } 328 329 @Override getActionView()330 public View getActionView() { 331 return null; 332 } 333 334 @Override setActionProvider(android.view.ActionProvider actionProvider)335 public MenuItem setActionProvider(android.view.ActionProvider actionProvider) { 336 throw new UnsupportedOperationException(); 337 } 338 339 @Override getActionProvider()340 public android.view.ActionProvider getActionProvider() { 341 throw new UnsupportedOperationException(); 342 } 343 344 @Override setActionView(int resId)345 public @NonNull SupportMenuItem setActionView(int resId) { 346 throw new UnsupportedOperationException(); 347 } 348 349 @Override getSupportActionProvider()350 public ActionProvider getSupportActionProvider() { 351 return null; 352 } 353 354 @Override setSupportActionProvider(ActionProvider actionProvider)355 public @NonNull SupportMenuItem setSupportActionProvider(ActionProvider actionProvider) { 356 throw new UnsupportedOperationException(); 357 } 358 359 @Override setShowAsActionFlags(int actionEnum)360 public @NonNull SupportMenuItem setShowAsActionFlags(int actionEnum) { 361 setShowAsAction(actionEnum); 362 return this; 363 } 364 365 @Override expandActionView()366 public boolean expandActionView() { 367 return false; 368 } 369 370 @Override collapseActionView()371 public boolean collapseActionView() { 372 return false; 373 } 374 375 @Override isActionViewExpanded()376 public boolean isActionViewExpanded() { 377 return false; 378 } 379 380 @Override setOnActionExpandListener(MenuItem.OnActionExpandListener listener)381 public MenuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener) { 382 throw new UnsupportedOperationException(); 383 } 384 385 @Override setContentDescription(CharSequence contentDescription)386 public @NonNull SupportMenuItem setContentDescription(CharSequence contentDescription) { 387 mContentDescription = contentDescription; 388 return this; 389 } 390 391 @Override getContentDescription()392 public CharSequence getContentDescription() { 393 return mContentDescription; 394 } 395 396 @Override setTooltipText(CharSequence tooltipText)397 public @NonNull SupportMenuItem setTooltipText(CharSequence tooltipText) { 398 mTooltipText = tooltipText; 399 return this; 400 } 401 402 @Override getTooltipText()403 public CharSequence getTooltipText() { 404 return mTooltipText; 405 } 406 407 @Override setIconTintList(@ullable ColorStateList iconTintList)408 public @NonNull MenuItem setIconTintList(@Nullable ColorStateList iconTintList) { 409 mIconTintList = iconTintList; 410 mHasIconTint = true; 411 412 applyIconTint(); 413 414 return this; 415 } 416 417 @Override getIconTintList()418 public ColorStateList getIconTintList() { 419 return mIconTintList; 420 } 421 422 @Override setIconTintMode(PorterDuff.Mode iconTintMode)423 public @NonNull MenuItem setIconTintMode(PorterDuff.Mode iconTintMode) { 424 mIconTintMode = iconTintMode; 425 mHasIconTintMode = true; 426 427 applyIconTint(); 428 429 return this; 430 } 431 432 @Override getIconTintMode()433 public PorterDuff.Mode getIconTintMode() { 434 return mIconTintMode; 435 } 436 applyIconTint()437 private void applyIconTint() { 438 if (mIconDrawable != null && (mHasIconTint || mHasIconTintMode)) { 439 mIconDrawable = DrawableCompat.wrap(mIconDrawable); 440 mIconDrawable = mIconDrawable.mutate(); 441 442 if (mHasIconTint) { 443 DrawableCompat.setTintList(mIconDrawable, mIconTintList); 444 } 445 446 if (mHasIconTintMode) { 447 DrawableCompat.setTintMode(mIconDrawable, mIconTintMode); 448 } 449 } 450 } 451 } 452