blob: b6b11ab9249ba2353cc202bcf0e7e4f13a05c18f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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.view;
18
Tor Norbye7b9c9122013-05-30 16:48:33 -070019import android.annotation.MenuRes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.app.Activity;
21import android.content.Context;
Yigit Boyarb8c19b12014-09-17 19:23:21 -070022import android.content.ContextWrapper;
Kirill Grouchnikov6eea0d22017-03-21 13:52:09 -040023import android.content.res.ColorStateList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.res.TypedArray;
25import android.content.res.XmlResourceParser;
Nader Jawaddadf2512019-03-06 17:29:22 -080026import android.graphics.BlendMode;
Kirill Grouchnikov6eea0d22017-03-21 13:52:09 -040027import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.util.AttributeSet;
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -070029import android.util.Log;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.util.Xml;
31
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070032import com.android.internal.view.menu.MenuItemImpl;
33
34import org.xmlpull.v1.XmlPullParser;
35import org.xmlpull.v1.XmlPullParserException;
36
Adam Powellcf78b3e2010-09-12 18:25:23 -070037import java.io.IOException;
38import java.lang.reflect.Constructor;
39import java.lang.reflect.Method;
Adam Powell33b97432010-04-20 10:01:14 -070040
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041/**
42 * This class is used to instantiate menu XML files into Menu objects.
43 * <p>
44 * For performance reasons, menu inflation relies heavily on pre-processing of
45 * XML files that is done at build time. Therefore, it is not currently possible
46 * to use MenuInflater with an XmlPullParser over a plain XML file at runtime;
47 * it only works with an XmlPullParser returned from a compiled resource (R.
48 * <em>something</em> file.)
49 */
50public class MenuInflater {
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -070051 private static final String LOG_TAG = "MenuInflater";
52
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 /** Menu tag name in XML. */
54 private static final String XML_MENU = "menu";
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070055
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 /** Group tag name in XML. */
57 private static final String XML_GROUP = "group";
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070058
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 /** Item tag name in XML. */
60 private static final String XML_ITEM = "item";
61
62 private static final int NO_ID = 0;
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070063
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -070064 private static final Class<?>[] ACTION_VIEW_CONSTRUCTOR_SIGNATURE = new Class[] {Context.class};
65
66 private static final Class<?>[] ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE = ACTION_VIEW_CONSTRUCTOR_SIGNATURE;
67
68 private final Object[] mActionViewConstructorArguments;
69
70 private final Object[] mActionProviderConstructorArguments;
Adam Powellcf78b3e2010-09-12 18:25:23 -070071
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 private Context mContext;
Dianne Hackborn92751972012-05-18 19:22:14 -070073 private Object mRealOwner;
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -070074
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 /**
76 * Constructs a menu inflater.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070077 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 * @see Activity#getMenuInflater()
79 */
80 public MenuInflater(Context context) {
81 mContext = context;
Dianne Hackborn92751972012-05-18 19:22:14 -070082 mActionViewConstructorArguments = new Object[] {context};
83 mActionProviderConstructorArguments = mActionViewConstructorArguments;
84 }
85
86 /**
87 * Constructs a menu inflater.
88 *
89 * @see Activity#getMenuInflater()
90 * @hide
91 */
92 public MenuInflater(Context context, Object realOwner) {
93 mContext = context;
94 mRealOwner = realOwner;
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -070095 mActionViewConstructorArguments = new Object[] {context};
96 mActionProviderConstructorArguments = mActionViewConstructorArguments;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 }
98
99 /**
100 * Inflate a menu hierarchy from the specified XML resource. Throws
101 * {@link InflateException} if there is an error.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700102 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 * @param menuRes Resource ID for an XML layout resource to load (e.g.,
104 * <code>R.menu.main_activity</code>)
105 * @param menu The Menu to inflate into. The items and submenus will be
106 * added to this Menu.
107 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700108 public void inflate(@MenuRes int menuRes, Menu menu) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 XmlResourceParser parser = null;
110 try {
111 parser = mContext.getResources().getLayout(menuRes);
112 AttributeSet attrs = Xml.asAttributeSet(parser);
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700113
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 parseMenu(parser, attrs, menu);
115 } catch (XmlPullParserException e) {
116 throw new InflateException("Error inflating menu XML", e);
117 } catch (IOException e) {
118 throw new InflateException("Error inflating menu XML", e);
119 } finally {
120 if (parser != null) parser.close();
121 }
122 }
123
124 /**
125 * Called internally to fill the given menu. If a sub menu is seen, it will
126 * call this recursively.
127 */
128 private void parseMenu(XmlPullParser parser, AttributeSet attrs, Menu menu)
129 throws XmlPullParserException, IOException {
130 MenuState menuState = new MenuState(menu);
131
132 int eventType = parser.getEventType();
133 String tagName;
134 boolean lookingForEndOfUnknownTag = false;
135 String unknownTagName = null;
136
137 // This loop will skip to the menu start tag
138 do {
139 if (eventType == XmlPullParser.START_TAG) {
140 tagName = parser.getName();
141 if (tagName.equals(XML_MENU)) {
142 // Go to next tag
143 eventType = parser.next();
144 break;
145 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700146
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 throw new RuntimeException("Expecting menu, got " + tagName);
148 }
149 eventType = parser.next();
150 } while (eventType != XmlPullParser.END_DOCUMENT);
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 boolean reachedEndOfMenu = false;
153 while (!reachedEndOfMenu) {
154 switch (eventType) {
155 case XmlPullParser.START_TAG:
156 if (lookingForEndOfUnknownTag) {
157 break;
158 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700159
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 tagName = parser.getName();
161 if (tagName.equals(XML_GROUP)) {
162 menuState.readGroup(attrs);
163 } else if (tagName.equals(XML_ITEM)) {
164 menuState.readItem(attrs);
165 } else if (tagName.equals(XML_MENU)) {
166 // A menu start tag denotes a submenu for an item
167 SubMenu subMenu = menuState.addSubMenuItem();
Deepanshu Gupta10019612014-04-18 12:32:38 -0700168 registerMenu(subMenu, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169
170 // Parse the submenu into returned SubMenu
171 parseMenu(parser, attrs, subMenu);
172 } else {
173 lookingForEndOfUnknownTag = true;
174 unknownTagName = tagName;
175 }
176 break;
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700177
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 case XmlPullParser.END_TAG:
179 tagName = parser.getName();
180 if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) {
181 lookingForEndOfUnknownTag = false;
182 unknownTagName = null;
183 } else if (tagName.equals(XML_GROUP)) {
184 menuState.resetGroup();
185 } else if (tagName.equals(XML_ITEM)) {
186 // Add the item if it hasn't been added (if the item was
187 // a submenu, it would have been added already)
188 if (!menuState.hasAddedItem()) {
Adam Powell961dd112011-07-12 14:25:23 -0700189 if (menuState.itemActionProvider != null &&
190 menuState.itemActionProvider.hasSubMenu()) {
Deepanshu Gupta10019612014-04-18 12:32:38 -0700191 registerMenu(menuState.addSubMenuItem(), attrs);
Adam Powell961dd112011-07-12 14:25:23 -0700192 } else {
Deepanshu Gupta10019612014-04-18 12:32:38 -0700193 registerMenu(menuState.addItem(), attrs);
Adam Powell961dd112011-07-12 14:25:23 -0700194 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 }
196 } else if (tagName.equals(XML_MENU)) {
197 reachedEndOfMenu = true;
198 }
199 break;
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 case XmlPullParser.END_DOCUMENT:
202 throw new RuntimeException("Unexpected end of document");
203 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 eventType = parser.next();
206 }
207 }
Deepanshu Gupta10019612014-04-18 12:32:38 -0700208
209 /**
210 * The method is a hook for layoutlib to do its magic.
211 * Nothing is needed outside of LayoutLib. However, it should not be deleted because it
212 * appears to do nothing.
213 */
214 private void registerMenu(@SuppressWarnings("unused") MenuItem item,
215 @SuppressWarnings("unused") AttributeSet set) {
216 }
217
218 /**
219 * The method is a hook for layoutlib to do its magic.
220 * Nothing is needed outside of LayoutLib. However, it should not be deleted because it
221 * appears to do nothing.
222 */
223 private void registerMenu(@SuppressWarnings("unused") SubMenu subMenu,
224 @SuppressWarnings("unused") AttributeSet set) {
225 }
226
227 // Needed by layoutlib.
228 /*package*/ Context getContext() {
229 return mContext;
230 }
231
Adam Powell33b97432010-04-20 10:01:14 -0700232 private static class InflatedOnMenuItemClickListener
233 implements MenuItem.OnMenuItemClickListener {
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -0700234 private static final Class<?>[] PARAM_TYPES = new Class[] { MenuItem.class };
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700235
Dianne Hackborn92751972012-05-18 19:22:14 -0700236 private Object mRealOwner;
Adam Powell33b97432010-04-20 10:01:14 -0700237 private Method mMethod;
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700238
Dianne Hackborn92751972012-05-18 19:22:14 -0700239 public InflatedOnMenuItemClickListener(Object realOwner, String methodName) {
240 mRealOwner = realOwner;
241 Class<?> c = realOwner.getClass();
Adam Powell33b97432010-04-20 10:01:14 -0700242 try {
243 mMethod = c.getMethod(methodName, PARAM_TYPES);
244 } catch (Exception e) {
245 InflateException ex = new InflateException(
246 "Couldn't resolve menu item onClick handler " + methodName +
247 " in class " + c.getName());
248 ex.initCause(e);
249 throw ex;
250 }
251 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700252
Adam Powell33b97432010-04-20 10:01:14 -0700253 public boolean onMenuItemClick(MenuItem item) {
254 try {
255 if (mMethod.getReturnType() == Boolean.TYPE) {
Dianne Hackborn92751972012-05-18 19:22:14 -0700256 return (Boolean) mMethod.invoke(mRealOwner, item);
Adam Powell33b97432010-04-20 10:01:14 -0700257 } else {
Dianne Hackborn92751972012-05-18 19:22:14 -0700258 mMethod.invoke(mRealOwner, item);
Adam Powell33b97432010-04-20 10:01:14 -0700259 return true;
260 }
261 } catch (Exception e) {
262 throw new RuntimeException(e);
263 }
264 }
265 }
Yigit Boyarb8c19b12014-09-17 19:23:21 -0700266
267 private Object getRealOwner() {
268 if (mRealOwner == null) {
269 mRealOwner = findRealOwner(mContext);
270 }
271 return mRealOwner;
272 }
273
274 private Object findRealOwner(Object owner) {
275 if (owner instanceof Activity) {
276 return owner;
277 }
278 if (owner instanceof ContextWrapper) {
279 return findRealOwner(((ContextWrapper) owner).getBaseContext());
280 }
281 return owner;
282 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700283
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 /**
285 * State for the current menu.
286 * <p>
287 * Groups can not be nested unless there is another menu (which will have
288 * its state class).
289 */
290 private class MenuState {
291 private Menu menu;
292
293 /*
294 * Group state is set on items as they are added, allowing an item to
295 * override its group state. (As opposed to set on items at the group end tag.)
296 */
297 private int groupId;
298 private int groupCategory;
299 private int groupOrder;
300 private int groupCheckable;
301 private boolean groupVisible;
302 private boolean groupEnabled;
303
304 private boolean itemAdded;
305 private int itemId;
306 private int itemCategoryOrder;
Adam Powell66501852011-01-27 17:10:40 -0800307 private CharSequence itemTitle;
308 private CharSequence itemTitleCondensed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 private int itemIconResId;
Kirill Grouchnikov6eea0d22017-03-21 13:52:09 -0400310 private ColorStateList itemIconTintList = null;
Nader Jawaddadf2512019-03-06 17:29:22 -0800311 private BlendMode mItemIconBlendMode = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 private char itemAlphabeticShortcut;
Peeyush Agarwale631e322016-10-19 11:41:42 +0100313 private int itemAlphabeticModifiers;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 private char itemNumericShortcut;
Peeyush Agarwale631e322016-10-19 11:41:42 +0100315 private int itemNumericModifiers;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 /**
317 * Sync to attrs.xml enum:
318 * - 0: none
319 * - 1: all
320 * - 2: exclusive
321 */
322 private int itemCheckable;
323 private boolean itemChecked;
324 private boolean itemVisible;
325 private boolean itemEnabled;
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700326
Adam Powell96675b12010-06-10 18:58:59 -0700327 /**
Adam Powell89e06452010-06-23 20:24:52 -0700328 * Sync to attrs.xml enum, values in MenuItem:
Adam Powell96675b12010-06-10 18:58:59 -0700329 * - 0: never
330 * - 1: ifRoom
331 * - 2: always
Adam Powell4d9861e2010-08-17 11:14:40 -0700332 * - -1: Safe sentinel for "no value".
Adam Powell96675b12010-06-10 18:58:59 -0700333 */
Adam Powellfbb72fd2010-08-16 18:01:21 -0700334 private int itemShowAsAction;
Adam Powellcf78b3e2010-09-12 18:25:23 -0700335
336 private int itemActionViewLayout;
337 private String itemActionViewClassName;
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -0700338 private String itemActionProviderClassName;
339
Adam Powell33b97432010-04-20 10:01:14 -0700340 private String itemListenerMethodName;
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700341
Adam Powell961dd112011-07-12 14:25:23 -0700342 private ActionProvider itemActionProvider;
343
Vladislav Kaznacheev7039cbc2017-01-04 10:15:31 -0800344 private CharSequence itemContentDescription;
Vladislav Kaznacheev6a944ca2017-01-19 11:02:12 -0800345 private CharSequence itemTooltipText;
Vladislav Kaznacheev7039cbc2017-01-04 10:15:31 -0800346
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 private static final int defaultGroupId = NO_ID;
348 private static final int defaultItemId = NO_ID;
349 private static final int defaultItemCategory = 0;
350 private static final int defaultItemOrder = 0;
351 private static final int defaultItemCheckable = 0;
352 private static final boolean defaultItemChecked = false;
353 private static final boolean defaultItemVisible = true;
354 private static final boolean defaultItemEnabled = true;
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700355
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 public MenuState(final Menu menu) {
357 this.menu = menu;
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700358
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 resetGroup();
360 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700361
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 public void resetGroup() {
363 groupId = defaultGroupId;
364 groupCategory = defaultItemCategory;
365 groupOrder = defaultItemOrder;
366 groupCheckable = defaultItemCheckable;
367 groupVisible = defaultItemVisible;
368 groupEnabled = defaultItemEnabled;
369 }
370
371 /**
372 * Called when the parser is pointing to a group tag.
373 */
374 public void readGroup(AttributeSet attrs) {
375 TypedArray a = mContext.obtainStyledAttributes(attrs,
376 com.android.internal.R.styleable.MenuGroup);
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700377
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 groupId = a.getResourceId(com.android.internal.R.styleable.MenuGroup_id, defaultGroupId);
379 groupCategory = a.getInt(com.android.internal.R.styleable.MenuGroup_menuCategory, defaultItemCategory);
380 groupOrder = a.getInt(com.android.internal.R.styleable.MenuGroup_orderInCategory, defaultItemOrder);
381 groupCheckable = a.getInt(com.android.internal.R.styleable.MenuGroup_checkableBehavior, defaultItemCheckable);
382 groupVisible = a.getBoolean(com.android.internal.R.styleable.MenuGroup_visible, defaultItemVisible);
383 groupEnabled = a.getBoolean(com.android.internal.R.styleable.MenuGroup_enabled, defaultItemEnabled);
384
385 a.recycle();
386 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700387
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 /**
389 * Called when the parser is pointing to an item tag.
390 */
391 public void readItem(AttributeSet attrs) {
392 TypedArray a = mContext.obtainStyledAttributes(attrs,
393 com.android.internal.R.styleable.MenuItem);
394
395 // Inherit attributes from the group as default value
396 itemId = a.getResourceId(com.android.internal.R.styleable.MenuItem_id, defaultItemId);
397 final int category = a.getInt(com.android.internal.R.styleable.MenuItem_menuCategory, groupCategory);
398 final int order = a.getInt(com.android.internal.R.styleable.MenuItem_orderInCategory, groupOrder);
399 itemCategoryOrder = (category & Menu.CATEGORY_MASK) | (order & Menu.USER_MASK);
Adam Powell66501852011-01-27 17:10:40 -0800400 itemTitle = a.getText(com.android.internal.R.styleable.MenuItem_title);
401 itemTitleCondensed = a.getText(com.android.internal.R.styleable.MenuItem_titleCondensed);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 itemIconResId = a.getResourceId(com.android.internal.R.styleable.MenuItem_icon, 0);
Kirill Grouchnikov6eea0d22017-03-21 13:52:09 -0400403 if (a.hasValue(com.android.internal.R.styleable.MenuItem_iconTintMode)) {
Nader Jawaddadf2512019-03-06 17:29:22 -0800404 mItemIconBlendMode = Drawable.parseBlendMode(a.getInt(
Kirill Grouchnikov6eea0d22017-03-21 13:52:09 -0400405 com.android.internal.R.styleable.MenuItem_iconTintMode, -1),
Nader Jawaddadf2512019-03-06 17:29:22 -0800406 mItemIconBlendMode);
Kirill Grouchnikov6eea0d22017-03-21 13:52:09 -0400407 } else {
408 // Reset to null so that it's not carried over to the next item
Nader Jawaddadf2512019-03-06 17:29:22 -0800409 mItemIconBlendMode = null;
Kirill Grouchnikov6eea0d22017-03-21 13:52:09 -0400410 }
411 if (a.hasValue(com.android.internal.R.styleable.MenuItem_iconTint)) {
412 itemIconTintList = a.getColorStateList(
413 com.android.internal.R.styleable.MenuItem_iconTint);
414 } else {
415 // Reset to null so that it's not carried over to the next item
416 itemIconTintList = null;
417 }
418
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 itemAlphabeticShortcut =
420 getShortcut(a.getString(com.android.internal.R.styleable.MenuItem_alphabeticShortcut));
Peeyush Agarwale631e322016-10-19 11:41:42 +0100421 itemAlphabeticModifiers =
422 a.getInt(com.android.internal.R.styleable.MenuItem_alphabeticModifiers,
423 KeyEvent.META_CTRL_ON);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 itemNumericShortcut =
425 getShortcut(a.getString(com.android.internal.R.styleable.MenuItem_numericShortcut));
Peeyush Agarwale631e322016-10-19 11:41:42 +0100426 itemNumericModifiers =
427 a.getInt(com.android.internal.R.styleable.MenuItem_numericModifiers,
428 KeyEvent.META_CTRL_ON);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 if (a.hasValue(com.android.internal.R.styleable.MenuItem_checkable)) {
430 // Item has attribute checkable, use it
431 itemCheckable = a.getBoolean(com.android.internal.R.styleable.MenuItem_checkable, false) ? 1 : 0;
432 } else {
433 // Item does not have attribute, use the group's (group can have one more state
434 // for checkable that represents the exclusive checkable)
435 itemCheckable = groupCheckable;
436 }
437 itemChecked = a.getBoolean(com.android.internal.R.styleable.MenuItem_checked, defaultItemChecked);
438 itemVisible = a.getBoolean(com.android.internal.R.styleable.MenuItem_visible, groupVisible);
439 itemEnabled = a.getBoolean(com.android.internal.R.styleable.MenuItem_enabled, groupEnabled);
Adam Powell4d9861e2010-08-17 11:14:40 -0700440 itemShowAsAction = a.getInt(com.android.internal.R.styleable.MenuItem_showAsAction, -1);
Adam Powell33b97432010-04-20 10:01:14 -0700441 itemListenerMethodName = a.getString(com.android.internal.R.styleable.MenuItem_onClick);
Adam Powellcf78b3e2010-09-12 18:25:23 -0700442 itemActionViewLayout = a.getResourceId(com.android.internal.R.styleable.MenuItem_actionLayout, 0);
443 itemActionViewClassName = a.getString(com.android.internal.R.styleable.MenuItem_actionViewClass);
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -0700444 itemActionProviderClassName = a.getString(com.android.internal.R.styleable.MenuItem_actionProviderClass);
445
Adam Powell961dd112011-07-12 14:25:23 -0700446 final boolean hasActionProvider = itemActionProviderClassName != null;
447 if (hasActionProvider && itemActionViewLayout == 0 && itemActionViewClassName == null) {
448 itemActionProvider = newInstance(itemActionProviderClassName,
449 ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE,
450 mActionProviderConstructorArguments);
451 } else {
452 if (hasActionProvider) {
453 Log.w(LOG_TAG, "Ignoring attribute 'actionProviderClass'."
454 + " Action view already specified.");
455 }
456 itemActionProvider = null;
457 }
458
Vladislav Kaznacheev7039cbc2017-01-04 10:15:31 -0800459 itemContentDescription =
460 a.getText(com.android.internal.R.styleable.MenuItem_contentDescription);
Vladislav Kaznacheev6a944ca2017-01-19 11:02:12 -0800461 itemTooltipText = a.getText(com.android.internal.R.styleable.MenuItem_tooltipText);
Vladislav Kaznacheev7039cbc2017-01-04 10:15:31 -0800462
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 a.recycle();
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -0700464
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 itemAdded = false;
466 }
467
468 private char getShortcut(String shortcutString) {
469 if (shortcutString == null) {
470 return 0;
471 } else {
472 return shortcutString.charAt(0);
473 }
474 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700475
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 private void setItem(MenuItem item) {
477 item.setChecked(itemChecked)
478 .setVisible(itemVisible)
479 .setEnabled(itemEnabled)
480 .setCheckable(itemCheckable >= 1)
481 .setTitleCondensed(itemTitleCondensed)
482 .setIcon(itemIconResId)
Peeyush Agarwale631e322016-10-19 11:41:42 +0100483 .setAlphabeticShortcut(itemAlphabeticShortcut, itemAlphabeticModifiers)
484 .setNumericShortcut(itemNumericShortcut, itemNumericModifiers);
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700485
Adam Powell4d9861e2010-08-17 11:14:40 -0700486 if (itemShowAsAction >= 0) {
487 item.setShowAsAction(itemShowAsAction);
488 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700489
Nader Jawaddadf2512019-03-06 17:29:22 -0800490 if (mItemIconBlendMode != null) {
Nader Jawad8e31c3e2019-04-14 21:58:04 -0700491 item.setIconTintBlendMode(mItemIconBlendMode);
Kirill Grouchnikov6eea0d22017-03-21 13:52:09 -0400492 }
493
494 if (itemIconTintList != null) {
495 item.setIconTintList(itemIconTintList);
496 }
497
Adam Powell33b97432010-04-20 10:01:14 -0700498 if (itemListenerMethodName != null) {
Adam Powell5d279772010-07-27 16:34:07 -0700499 if (mContext.isRestricted()) {
500 throw new IllegalStateException("The android:onClick attribute cannot "
501 + "be used within a restricted context");
502 }
Adam Powell33b97432010-04-20 10:01:14 -0700503 item.setOnMenuItemClickListener(
Yigit Boyarb8c19b12014-09-17 19:23:21 -0700504 new InflatedOnMenuItemClickListener(getRealOwner(), itemListenerMethodName));
Adam Powell33b97432010-04-20 10:01:14 -0700505 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506
Adam Powell96675b12010-06-10 18:58:59 -0700507 if (item instanceof MenuItemImpl) {
508 MenuItemImpl impl = (MenuItemImpl) item;
509 if (itemCheckable >= 2) {
510 impl.setExclusiveCheckable(true);
511 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 }
Adam Powellcf78b3e2010-09-12 18:25:23 -0700513
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -0700514 boolean actionViewSpecified = false;
Adam Powellcf78b3e2010-09-12 18:25:23 -0700515 if (itemActionViewClassName != null) {
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -0700516 View actionView = (View) newInstance(itemActionViewClassName,
517 ACTION_VIEW_CONSTRUCTOR_SIGNATURE, mActionViewConstructorArguments);
518 item.setActionView(actionView);
519 actionViewSpecified = true;
520 }
521 if (itemActionViewLayout > 0) {
522 if (!actionViewSpecified) {
523 item.setActionView(itemActionViewLayout);
524 actionViewSpecified = true;
525 } else {
526 Log.w(LOG_TAG, "Ignoring attribute 'itemActionViewLayout'."
527 + " Action view already specified.");
Adam Powellcf78b3e2010-09-12 18:25:23 -0700528 }
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -0700529 }
Adam Powell961dd112011-07-12 14:25:23 -0700530 if (itemActionProvider != null) {
531 item.setActionProvider(itemActionProvider);
Adam Powellcf78b3e2010-09-12 18:25:23 -0700532 }
Vladislav Kaznacheev7039cbc2017-01-04 10:15:31 -0800533
534 item.setContentDescription(itemContentDescription);
Vladislav Kaznacheev6a944ca2017-01-19 11:02:12 -0800535 item.setTooltipText(itemTooltipText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 }
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -0700537
Deepanshu Gupta10019612014-04-18 12:32:38 -0700538 public MenuItem addItem() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 itemAdded = true;
Deepanshu Gupta10019612014-04-18 12:32:38 -0700540 MenuItem item = menu.add(groupId, itemId, itemCategoryOrder, itemTitle);
541 setItem(item);
542 return item;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700544
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 public SubMenu addSubMenuItem() {
546 itemAdded = true;
547 SubMenu subMenu = menu.addSubMenu(groupId, itemId, itemCategoryOrder, itemTitle);
548 setItem(subMenu.getItem());
549 return subMenu;
550 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700551
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 public boolean hasAddedItem() {
553 return itemAdded;
554 }
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -0700555
556 @SuppressWarnings("unchecked")
557 private <T> T newInstance(String className, Class<?>[] constructorSignature,
558 Object[] arguments) {
559 try {
560 Class<?> clazz = mContext.getClassLoader().loadClass(className);
561 Constructor<?> constructor = clazz.getConstructor(constructorSignature);
Alan Viverette904de2e2015-05-04 10:32:57 -0700562 constructor.setAccessible(true);
Svetoslav Ganov51ac0e92011-06-17 13:45:13 -0700563 return (T) constructor.newInstance(arguments);
564 } catch (Exception e) {
565 Log.w(LOG_TAG, "Cannot instantiate class: " + className, e);
566 }
567 return null;
568 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570}