Source code
package android.support.v7.internal.view;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.support.v4.internal.view.SupportMenu;
import android.support.v4.view.ActionProvider;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.appcompat.R;
import android.support.v7.internal.view.menu.MenuItemImpl;
import android.support.v7.internal.view.menu.MenuItemWrapperICS;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
import android.view.InflateException;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.SubMenu;
import android.view.View;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
public class SupportMenuInflater extends MenuInflater {
private static final Class<?>[] ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE = ACTION_VIEW_CONSTRUCTOR_SIGNATURE;
private static final Class<?>[] ACTION_VIEW_CONSTRUCTOR_SIGNATURE = new Class[]{Context.class};
private static final String LOG_TAG = "SupportMenuInflater";
private static final int NO_ID = 0;
private static final String XML_GROUP = "group";
private static final String XML_ITEM = "item";
private static final String XML_MENU = "menu";
private final Object[] mActionProviderConstructorArguments = this.mActionViewConstructorArguments;
private final Object[] mActionViewConstructorArguments;
private Context mContext;
private Object mRealOwner;
private static class InflatedOnMenuItemClickListener implements OnMenuItemClickListener {
private static final Class<?>[] PARAM_TYPES = new Class[]{MenuItem.class};
private Method mMethod;
private Object mRealOwner;
public InflatedOnMenuItemClickListener(Object realOwner, String methodName) {
this.mRealOwner = realOwner;
Class<?> c = realOwner.getClass();
try {
this.mMethod = c.getMethod(methodName, PARAM_TYPES);
} catch (Exception e) {
InflateException ex = new InflateException("Couldn't resolve menu item onClick handler " + methodName + " in class " + c.getName());
ex.initCause(e);
throw ex;
}
}
public boolean onMenuItemClick(MenuItem item) {
try {
if (this.mMethod.getReturnType() == Boolean.TYPE) {
return ((Boolean) this.mMethod.invoke(this.mRealOwner, new Object[]{item})).booleanValue();
}
this.mMethod.invoke(this.mRealOwner, new Object[]{item});
return true;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
private class MenuState {
private static final int defaultGroupId = 0;
private static final int defaultItemCategory = 0;
private static final int defaultItemCheckable = 0;
private static final boolean defaultItemChecked = false;
private static final boolean defaultItemEnabled = true;
private static final int defaultItemId = 0;
private static final int defaultItemOrder = 0;
private static final boolean defaultItemVisible = true;
private int groupCategory;
private int groupCheckable;
private boolean groupEnabled;
private int groupId;
private int groupOrder;
private boolean groupVisible;
private ActionProvider itemActionProvider;
private String itemActionProviderClassName;
private String itemActionViewClassName;
private int itemActionViewLayout;
private boolean itemAdded;
private char itemAlphabeticShortcut;
private int itemCategoryOrder;
private int itemCheckable;
private boolean itemChecked;
private boolean itemEnabled;
private int itemIconResId;
private int itemId;
private String itemListenerMethodName;
private char itemNumericShortcut;
private int itemShowAsAction;
private CharSequence itemTitle;
private CharSequence itemTitleCondensed;
private boolean itemVisible;
private Menu menu;
public MenuState(Menu menu) {
this.menu = menu;
resetGroup();
}
public void resetGroup() {
this.groupId = 0;
this.groupCategory = 0;
this.groupOrder = 0;
this.groupCheckable = 0;
this.groupVisible = true;
this.groupEnabled = true;
}
public void readGroup(AttributeSet attrs) {
TypedArray a = SupportMenuInflater.this.mContext.obtainStyledAttributes(attrs, R.styleable.MenuGroup);
this.groupId = a.getResourceId(R.styleable.MenuGroup_android_id, 0);
this.groupCategory = a.getInt(R.styleable.MenuGroup_android_menuCategory, 0);
this.groupOrder = a.getInt(R.styleable.MenuGroup_android_orderInCategory, 0);
this.groupCheckable = a.getInt(R.styleable.MenuGroup_android_checkableBehavior, 0);
this.groupVisible = a.getBoolean(R.styleable.MenuGroup_android_visible, true);
this.groupEnabled = a.getBoolean(R.styleable.MenuGroup_android_enabled, true);
a.recycle();
}
public void readItem(AttributeSet attrs) {
TypedArray a = SupportMenuInflater.this.mContext.obtainStyledAttributes(attrs, R.styleable.MenuItem);
this.itemId = a.getResourceId(R.styleable.MenuItem_android_id, 0);
this.itemCategoryOrder = (SupportMenu.CATEGORY_MASK & a.getInt(R.styleable.MenuItem_android_menuCategory, this.groupCategory)) | (SupportMenu.USER_MASK & a.getInt(R.styleable.MenuItem_android_orderInCategory, this.groupOrder));
this.itemTitle = a.getText(R.styleable.MenuItem_android_title);
this.itemTitleCondensed = a.getText(R.styleable.MenuItem_android_titleCondensed);
this.itemIconResId = a.getResourceId(R.styleable.MenuItem_android_icon, 0);
this.itemAlphabeticShortcut = getShortcut(a.getString(R.styleable.MenuItem_android_alphabeticShortcut));
this.itemNumericShortcut = getShortcut(a.getString(R.styleable.MenuItem_android_numericShortcut));
if (a.hasValue(R.styleable.MenuItem_android_checkable)) {
int i;
if (a.getBoolean(R.styleable.MenuItem_android_checkable, false)) {
i = 1;
} else {
i = 0;
}
this.itemCheckable = i;
} else {
this.itemCheckable = this.groupCheckable;
}
this.itemChecked = a.getBoolean(R.styleable.MenuItem_android_checked, false);
this.itemVisible = a.getBoolean(R.styleable.MenuItem_android_visible, this.groupVisible);
this.itemEnabled = a.getBoolean(R.styleable.MenuItem_android_enabled, this.groupEnabled);
this.itemShowAsAction = a.getInt(R.styleable.MenuItem_showAsAction, -1);
this.itemListenerMethodName = a.getString(R.styleable.MenuItem_android_onClick);
this.itemActionViewLayout = a.getResourceId(R.styleable.MenuItem_actionLayout, 0);
this.itemActionViewClassName = a.getString(R.styleable.MenuItem_actionViewClass);
this.itemActionProviderClassName = a.getString(R.styleable.MenuItem_actionProviderClass);
boolean hasActionProvider = this.itemActionProviderClassName != null;
if (hasActionProvider && this.itemActionViewLayout == 0 && this.itemActionViewClassName == null) {
this.itemActionProvider = (ActionProvider) newInstance(this.itemActionProviderClassName, SupportMenuInflater.ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE, SupportMenuInflater.this.mActionProviderConstructorArguments);
} else {
if (hasActionProvider) {
Log.w(SupportMenuInflater.LOG_TAG, "Ignoring attribute 'actionProviderClass'. Action view already specified.");
}
this.itemActionProvider = null;
}
a.recycle();
this.itemAdded = false;
}
private char getShortcut(String shortcutString) {
if (shortcutString == null) {
return '\u0000';
}
return shortcutString.charAt(0);
}
private void setItem(MenuItem item) {
item.setChecked(this.itemChecked).setVisible(this.itemVisible).setEnabled(this.itemEnabled).setCheckable(this.itemCheckable >= 1).setTitleCondensed(this.itemTitleCondensed).setIcon(this.itemIconResId).setAlphabeticShortcut(this.itemAlphabeticShortcut).setNumericShortcut(this.itemNumericShortcut);
if (this.itemShowAsAction >= 0) {
MenuItemCompat.setShowAsAction(item, this.itemShowAsAction);
}
if (this.itemListenerMethodName != null) {
if (SupportMenuInflater.this.mContext.isRestricted()) {
throw new IllegalStateException("The android:onClick attribute cannot be used within a restricted context");
}
item.setOnMenuItemClickListener(new InflatedOnMenuItemClickListener(SupportMenuInflater.this.getRealOwner(), this.itemListenerMethodName));
}
if (item instanceof MenuItemImpl) {
MenuItemImpl impl = (MenuItemImpl) item;
}
if (this.itemCheckable >= 2) {
if (item instanceof MenuItemImpl) {
((MenuItemImpl) item).setExclusiveCheckable(true);
} else if (item instanceof MenuItemWrapperICS) {
((MenuItemWrapperICS) item).setExclusiveCheckable(true);
}
}
boolean actionViewSpecified = false;
if (this.itemActionViewClassName != null) {
MenuItemCompat.setActionView(item, (View) newInstance(this.itemActionViewClassName, SupportMenuInflater.ACTION_VIEW_CONSTRUCTOR_SIGNATURE, SupportMenuInflater.this.mActionViewConstructorArguments));
actionViewSpecified = true;
}
if (this.itemActionViewLayout > 0) {
if (actionViewSpecified) {
Log.w(SupportMenuInflater.LOG_TAG, "Ignoring attribute 'itemActionViewLayout'. Action view already specified.");
} else {
MenuItemCompat.setActionView(item, this.itemActionViewLayout);
}
}
if (this.itemActionProvider != null) {
MenuItemCompat.setActionProvider(item, this.itemActionProvider);
}
}
public void addItem() {
this.itemAdded = true;
setItem(this.menu.add(this.groupId, this.itemId, this.itemCategoryOrder, this.itemTitle));
}
public SubMenu addSubMenuItem() {
this.itemAdded = true;
SubMenu subMenu = this.menu.addSubMenu(this.groupId, this.itemId, this.itemCategoryOrder, this.itemTitle);
setItem(subMenu.getItem());
return subMenu;
}
public boolean hasAddedItem() {
return this.itemAdded;
}
private <T> T newInstance(String className, Class<?>[] constructorSignature, Object[] arguments) {
try {
Constructor<?> constructor = SupportMenuInflater.this.mContext.getClassLoader().loadClass(className).getConstructor(constructorSignature);
constructor.setAccessible(true);
return constructor.newInstance(arguments);
} catch (Exception e) {
Log.w(SupportMenuInflater.LOG_TAG, "Cannot instantiate class: " + className, e);
return null;
}
}
}
public SupportMenuInflater(Context context) {
super(context);
this.mContext = context;
this.mActionViewConstructorArguments = new Object[]{context};
}
public void inflate(int menuRes, Menu menu) {
if (menu instanceof SupportMenu) {
XmlResourceParser parser = null;
try {
parser = this.mContext.getResources().getLayout(menuRes);
parseMenu(parser, Xml.asAttributeSet(parser), menu);
if (parser != null) {
parser.close();
}
} catch (XmlPullParserException e) {
throw new InflateException("Error inflating menu XML", e);
} catch (IOException e2) {
throw new InflateException("Error inflating menu XML", e2);
} catch (Throwable th) {
if (parser != null) {
parser.close();
}
}
} else {
super.inflate(menuRes, menu);
}
}
private void parseMenu(XmlPullParser parser, AttributeSet attrs, Menu menu) throws XmlPullParserException, IOException {
MenuState menuState = new MenuState(menu);
int eventType = parser.getEventType();
boolean lookingForEndOfUnknownTag = false;
String unknownTagName = null;
while (eventType != 2) {
eventType = parser.next();
if (eventType == 1) {
break;
}
}
String tagName = parser.getName();
if (tagName.equals(XML_MENU)) {
eventType = parser.next();
boolean reachedEndOfMenu = false;
while (!reachedEndOfMenu) {
switch (eventType) {
case 1:
throw new RuntimeException("Unexpected end of document");
case 2:
if (!lookingForEndOfUnknownTag) {
tagName = parser.getName();
if (!tagName.equals(XML_GROUP)) {
if (!tagName.equals(XML_ITEM)) {
if (!tagName.equals(XML_MENU)) {
lookingForEndOfUnknownTag = true;
unknownTagName = tagName;
break;
}
parseMenu(parser, attrs, menuState.addSubMenuItem());
break;
}
menuState.readItem(attrs);
break;
}
menuState.readGroup(attrs);
break;
}
break;
case 3:
tagName = parser.getName();
if (!lookingForEndOfUnknownTag || !tagName.equals(unknownTagName)) {
if (!tagName.equals(XML_GROUP)) {
if (!tagName.equals(XML_ITEM)) {
if (!tagName.equals(XML_MENU)) {
break;
}
reachedEndOfMenu = true;
break;
} else if (!menuState.hasAddedItem()) {
if (menuState.itemActionProvider != null && menuState.itemActionProvider.hasSubMenu()) {
menuState.addSubMenuItem();
break;
} else {
menuState.addItem();
break;
}
} else {
break;
}
}
menuState.resetGroup();
break;
}
lookingForEndOfUnknownTag = false;
unknownTagName = null;
break;
default:
break;
}
eventType = parser.next();
}
return;
}
throw new RuntimeException("Expecting menu, got " + tagName);
}
private Object getRealOwner() {
if (this.mRealOwner == null) {
this.mRealOwner = findRealOwner(this.mContext);
}
return this.mRealOwner;
}
private Object findRealOwner(Object owner) {
if (!(owner instanceof Activity) && (owner instanceof ContextWrapper)) {
return findRealOwner(((ContextWrapper) owner).getBaseContext());
}
return owner;
}
}