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