1 /* 2 * Copyright (C) 2017 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.wear.widget.drawer; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.graphics.drawable.Drawable; 23 import android.view.ActionProvider; 24 import android.view.ContextMenu; 25 import android.view.KeyEvent; 26 import android.view.Menu; 27 import android.view.MenuItem; 28 import android.view.SubMenu; 29 import android.view.View; 30 31 import androidx.core.content.ContextCompat; 32 33 import org.jspecify.annotations.Nullable; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 38 /* package */ class WearableActionDrawerMenu implements Menu { 39 40 private final Context mContext; 41 @SuppressWarnings("WeakerAccess") /* synthetic access */ 42 final List<WearableActionDrawerMenuItem> mItems = new ArrayList<>(); 43 @SuppressWarnings("WeakerAccess") /* synthetic access */ 44 final WearableActionDrawerMenuListener mListener; 45 private final WearableActionDrawerMenuItem.MenuItemChangedListener mItemChangedListener = 46 new WearableActionDrawerMenuItem.MenuItemChangedListener() { 47 @Override 48 public void itemChanged(WearableActionDrawerMenuItem item) { 49 for (int i = 0; i < mItems.size(); i++) { 50 if (mItems.get(i) == item) { 51 mListener.menuItemChanged(i); 52 } 53 } 54 } 55 }; 56 WearableActionDrawerMenu(Context context, WearableActionDrawerMenuListener listener)57 WearableActionDrawerMenu(Context context, WearableActionDrawerMenuListener listener) { 58 mContext = context; 59 mListener = listener; 60 } 61 62 @Override add(CharSequence title)63 public MenuItem add(CharSequence title) { 64 return add(0, 0, 0, title); 65 } 66 67 @Override add(int titleRes)68 public MenuItem add(int titleRes) { 69 return add(0, 0, 0, titleRes); 70 } 71 72 @Override add(int groupId, int itemId, int order, int titleRes)73 public MenuItem add(int groupId, int itemId, int order, int titleRes) { 74 return add(groupId, itemId, order, mContext.getResources().getString(titleRes)); 75 } 76 77 @Override add(int groupId, int itemId, int order, CharSequence title)78 public MenuItem add(int groupId, int itemId, int order, CharSequence title) { 79 WearableActionDrawerMenuItem item = 80 new WearableActionDrawerMenuItem(mContext, itemId, title, mItemChangedListener); 81 mItems.add(item); 82 mListener.menuItemAdded(mItems.size() - 1); 83 return item; 84 } 85 86 @Override clear()87 public void clear() { 88 mItems.clear(); 89 mListener.menuChanged(); 90 } 91 92 @Override removeItem(int id)93 public void removeItem(int id) { 94 int index = findItemIndex(id); 95 if ((index < 0) || (index >= mItems.size())) { 96 return; 97 } 98 mItems.remove(index); 99 mListener.menuItemRemoved(index); 100 } 101 102 @Override findItem(int id)103 public MenuItem findItem(int id) { 104 int index = findItemIndex(id); 105 if ((index < 0) || (index >= mItems.size())) { 106 return null; 107 } 108 return mItems.get(index); 109 } 110 111 @Override size()112 public int size() { 113 return mItems.size(); 114 } 115 116 @Override getItem(int index)117 public @Nullable MenuItem getItem(int index) { 118 if ((index < 0) || (index >= mItems.size())) { 119 return null; 120 } 121 return mItems.get(index); 122 } 123 findItemIndex(int id)124 private int findItemIndex(int id) { 125 final List<WearableActionDrawerMenuItem> items = mItems; 126 final int itemCount = items.size(); 127 for (int i = 0; i < itemCount; i++) { 128 if (items.get(i).getItemId() == id) { 129 return i; 130 } 131 } 132 133 return -1; 134 } 135 136 @Override close()137 public void close() { 138 throw new UnsupportedOperationException("close is not implemented"); 139 } 140 141 @Override addSubMenu(CharSequence title)142 public SubMenu addSubMenu(CharSequence title) { 143 throw new UnsupportedOperationException("addSubMenu is not implemented"); 144 } 145 146 @Override addSubMenu(int titleRes)147 public SubMenu addSubMenu(int titleRes) { 148 throw new UnsupportedOperationException("addSubMenu is not implemented"); 149 } 150 151 @Override addSubMenu(int groupId, int itemId, int order, CharSequence title)152 public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) { 153 throw new UnsupportedOperationException("addSubMenu is not implemented"); 154 } 155 156 @Override addSubMenu(int groupId, int itemId, int order, int titleRes)157 public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) { 158 throw new UnsupportedOperationException("addSubMenu is not implemented"); 159 } 160 161 @Override addIntentOptions( int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems)162 public int addIntentOptions( 163 int groupId, 164 int itemId, 165 int order, 166 ComponentName caller, 167 Intent[] specifics, 168 Intent intent, 169 int flags, 170 MenuItem[] outSpecificItems) { 171 throw new UnsupportedOperationException("addIntentOptions is not implemented"); 172 } 173 174 @Override removeGroup(int groupId)175 public void removeGroup(int groupId) { 176 } 177 178 @Override setGroupCheckable(int group, boolean checkable, boolean exclusive)179 public void setGroupCheckable(int group, boolean checkable, boolean exclusive) { 180 throw new UnsupportedOperationException("setGroupCheckable is not implemented"); 181 } 182 183 @Override setGroupVisible(int group, boolean visible)184 public void setGroupVisible(int group, boolean visible) { 185 throw new UnsupportedOperationException("setGroupVisible is not implemented"); 186 } 187 188 @Override setGroupEnabled(int group, boolean enabled)189 public void setGroupEnabled(int group, boolean enabled) { 190 throw new UnsupportedOperationException("setGroupEnabled is not implemented"); 191 } 192 193 @Override hasVisibleItems()194 public boolean hasVisibleItems() { 195 return false; 196 } 197 198 @Override performShortcut(int keyCode, KeyEvent event, int flags)199 public boolean performShortcut(int keyCode, KeyEvent event, int flags) { 200 throw new UnsupportedOperationException("performShortcut is not implemented"); 201 } 202 203 @Override isShortcutKey(int keyCode, KeyEvent event)204 public boolean isShortcutKey(int keyCode, KeyEvent event) { 205 return false; 206 } 207 208 @Override performIdentifierAction(int id, int flags)209 public boolean performIdentifierAction(int id, int flags) { 210 throw new UnsupportedOperationException("performIdentifierAction is not implemented"); 211 } 212 213 @Override setQwertyMode(boolean isQwerty)214 public void setQwertyMode(boolean isQwerty) { 215 } 216 217 /* package */ interface WearableActionDrawerMenuListener { 218 menuItemChanged(int position)219 void menuItemChanged(int position); 220 menuItemAdded(int position)221 void menuItemAdded(int position); 222 menuItemRemoved(int position)223 void menuItemRemoved(int position); 224 menuChanged()225 void menuChanged(); 226 } 227 228 public static final class WearableActionDrawerMenuItem implements MenuItem { 229 230 private final int mId; 231 232 private final Context mContext; 233 private final MenuItemChangedListener mItemChangedListener; 234 private CharSequence mTitle; 235 private Drawable mIconDrawable; 236 private MenuItem.OnMenuItemClickListener mClickListener; 237 WearableActionDrawerMenuItem( Context context, int id, CharSequence title, MenuItemChangedListener listener)238 WearableActionDrawerMenuItem( 239 Context context, int id, CharSequence title, MenuItemChangedListener listener) { 240 mContext = context; 241 mId = id; 242 mTitle = title; 243 mItemChangedListener = listener; 244 } 245 246 @Override getItemId()247 public int getItemId() { 248 return mId; 249 } 250 251 @Override setTitle(CharSequence title)252 public MenuItem setTitle(CharSequence title) { 253 mTitle = title; 254 if (mItemChangedListener != null) { 255 mItemChangedListener.itemChanged(this); 256 } 257 return this; 258 } 259 260 @Override setTitle(int title)261 public MenuItem setTitle(int title) { 262 return setTitle(mContext.getResources().getString(title)); 263 } 264 265 @Override getTitle()266 public CharSequence getTitle() { 267 return mTitle; 268 } 269 270 @Override setIcon(Drawable icon)271 public MenuItem setIcon(Drawable icon) { 272 mIconDrawable = icon; 273 if (mItemChangedListener != null) { 274 mItemChangedListener.itemChanged(this); 275 } 276 return this; 277 } 278 279 @Override setIcon(int iconRes)280 public MenuItem setIcon(int iconRes) { 281 return setIcon(ContextCompat.getDrawable(mContext, iconRes)); 282 } 283 284 @Override getIcon()285 public Drawable getIcon() { 286 return mIconDrawable; 287 } 288 289 @Override setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener)290 public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) { 291 mClickListener = menuItemClickListener; 292 return this; 293 } 294 295 @Override getGroupId()296 public int getGroupId() { 297 return 0; 298 } 299 300 @Override getOrder()301 public int getOrder() { 302 return 0; 303 } 304 305 @Override setTitleCondensed(CharSequence title)306 public MenuItem setTitleCondensed(CharSequence title) { 307 return this; 308 } 309 310 @Override getTitleCondensed()311 public CharSequence getTitleCondensed() { 312 return null; 313 } 314 315 @Override setIntent(Intent intent)316 public MenuItem setIntent(Intent intent) { 317 throw new UnsupportedOperationException("setIntent is not implemented"); 318 } 319 320 @Override getIntent()321 public Intent getIntent() { 322 return null; 323 } 324 325 @Override setShortcut(char numericChar, char alphaChar)326 public MenuItem setShortcut(char numericChar, char alphaChar) { 327 throw new UnsupportedOperationException("setShortcut is not implemented"); 328 } 329 330 @Override setNumericShortcut(char numericChar)331 public MenuItem setNumericShortcut(char numericChar) { 332 return this; 333 } 334 335 @Override getNumericShortcut()336 public char getNumericShortcut() { 337 return 0; 338 } 339 340 @Override setAlphabeticShortcut(char alphaChar)341 public MenuItem setAlphabeticShortcut(char alphaChar) { 342 return this; 343 } 344 345 @Override getAlphabeticShortcut()346 public char getAlphabeticShortcut() { 347 return 0; 348 } 349 350 @Override setCheckable(boolean checkable)351 public MenuItem setCheckable(boolean checkable) { 352 return this; 353 } 354 355 @Override isCheckable()356 public boolean isCheckable() { 357 return false; 358 } 359 360 @Override setChecked(boolean checked)361 public MenuItem setChecked(boolean checked) { 362 return this; 363 } 364 365 @Override isChecked()366 public boolean isChecked() { 367 return false; 368 } 369 370 @Override setVisible(boolean visible)371 public MenuItem setVisible(boolean visible) { 372 return this; 373 } 374 375 @Override isVisible()376 public boolean isVisible() { 377 return false; 378 } 379 380 @Override setEnabled(boolean enabled)381 public MenuItem setEnabled(boolean enabled) { 382 return this; 383 } 384 385 @Override isEnabled()386 public boolean isEnabled() { 387 return false; 388 } 389 390 @Override hasSubMenu()391 public boolean hasSubMenu() { 392 return false; 393 } 394 395 @Override getSubMenu()396 public SubMenu getSubMenu() { 397 return null; 398 } 399 400 @Override getMenuInfo()401 public ContextMenu.ContextMenuInfo getMenuInfo() { 402 return null; 403 } 404 405 @Override setShowAsAction(int actionEnum)406 public void setShowAsAction(int actionEnum) { 407 throw new UnsupportedOperationException("setShowAsAction is not implemented"); 408 } 409 410 @Override setShowAsActionFlags(int actionEnum)411 public MenuItem setShowAsActionFlags(int actionEnum) { 412 throw new UnsupportedOperationException("setShowAsActionFlags is not implemented"); 413 } 414 415 @Override setActionView(View view)416 public MenuItem setActionView(View view) { 417 throw new UnsupportedOperationException("setActionView is not implemented"); 418 } 419 420 @Override setActionView(int resId)421 public MenuItem setActionView(int resId) { 422 throw new UnsupportedOperationException("setActionView is not implemented"); 423 } 424 425 @Override getActionView()426 public View getActionView() { 427 return null; 428 } 429 430 @Override setActionProvider(ActionProvider actionProvider)431 public MenuItem setActionProvider(ActionProvider actionProvider) { 432 throw new UnsupportedOperationException("setActionProvider is not implemented"); 433 } 434 435 @Override getActionProvider()436 public ActionProvider getActionProvider() { 437 return null; 438 } 439 440 @Override expandActionView()441 public boolean expandActionView() { 442 throw new UnsupportedOperationException("expandActionView is not implemented"); 443 } 444 445 @Override collapseActionView()446 public boolean collapseActionView() { 447 throw new UnsupportedOperationException("collapseActionView is not implemented"); 448 } 449 450 @Override isActionViewExpanded()451 public boolean isActionViewExpanded() { 452 throw new UnsupportedOperationException("isActionViewExpanded is not implemented"); 453 } 454 455 @Override setOnActionExpandListener(OnActionExpandListener listener)456 public MenuItem setOnActionExpandListener(OnActionExpandListener listener) { 457 throw new UnsupportedOperationException("setOnActionExpandListener is not implemented"); 458 } 459 460 /** 461 * Invokes the item by calling the listener if set. 462 * 463 * @return true if the invocation was handled, false otherwise 464 */ invoke()465 /* package */ boolean invoke() { 466 return mClickListener != null && mClickListener.onMenuItemClick(this); 467 468 } 469 470 private interface MenuItemChangedListener { 471 itemChanged(WearableActionDrawerMenuItem item)472 void itemChanged(WearableActionDrawerMenuItem item); 473 } 474 } 475 } 476