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