Source code
package android.support.v7.internal.app;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.TypedArray;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.util.ArrayMap;
import android.support.v4.view.ViewCompat;
import android.support.v7.appcompat.R;
import android.support.v7.internal.view.ContextThemeWrapper;
import android.support.v7.widget.AppCompatAutoCompleteTextView;
import android.support.v7.widget.AppCompatButton;
import android.support.v7.widget.AppCompatCheckBox;
import android.support.v7.widget.AppCompatCheckedTextView;
import android.support.v7.widget.AppCompatEditText;
import android.support.v7.widget.AppCompatImageButton;
import android.support.v7.widget.AppCompatImageView;
import android.support.v7.widget.AppCompatMultiAutoCompleteTextView;
import android.support.v7.widget.AppCompatRadioButton;
import android.support.v7.widget.AppCompatRatingBar;
import android.support.v7.widget.AppCompatSeekBar;
import android.support.v7.widget.AppCompatSpinner;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.InflateException;
import android.view.View;
import android.view.View.OnClickListener;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
public class AppCompatViewInflater {
private static final String LOG_TAG = "AppCompatViewInflater";
private static final Map<String, Constructor<? extends View>> sConstructorMap = new ArrayMap();
private static final Class<?>[] sConstructorSignature = new Class[]{Context.class, AttributeSet.class};
private static final int[] sOnClickAttrs = new int[]{16843375};
private final Object[] mConstructorArgs = new Object[2];
private static class DeclaredOnClickListener implements OnClickListener {
private final View mHostView;
private final String mMethodName;
private Context mResolvedContext;
private Method mResolvedMethod;
public DeclaredOnClickListener(@NonNull View hostView, @NonNull String methodName) {
this.mHostView = hostView;
this.mMethodName = methodName;
}
public void onClick(@NonNull View v) {
if (this.mResolvedMethod == null) {
resolveMethod(this.mHostView.getContext(), this.mMethodName);
}
try {
this.mResolvedMethod.invoke(this.mResolvedContext, new Object[]{v});
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not execute non-public method for android:onClick", e);
} catch (InvocationTargetException e2) {
throw new IllegalStateException("Could not execute method for android:onClick", e2);
}
}
@NonNull
private void resolveMethod(@Nullable Context context, @NonNull String name) {
while (context != null) {
try {
if (!context.isRestricted()) {
Method method = context.getClass().getMethod(this.mMethodName, new Class[]{View.class});
if (method != null) {
this.mResolvedMethod = method;
this.mResolvedContext = context;
return;
}
}
} catch (NoSuchMethodException e) {
}
if (context instanceof ContextWrapper) {
context = ((ContextWrapper) context).getBaseContext();
} else {
context = null;
}
}
int id = this.mHostView.getId();
throw new IllegalStateException("Could not find method " + this.mMethodName + "(View) in a parent or ancestor Context for android:onClick " + "attribute defined on view " + this.mHostView.getClass() + (id == -1 ? "" : " with id '" + this.mHostView.getContext().getResources().getResourceEntryName(id) + "'"));
}
}
public final View createView(View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs, boolean inheritContext, boolean readAndroidTheme, boolean readAppTheme) {
Context originalContext = context;
if (inheritContext && parent != null) {
context = parent.getContext();
}
if (readAndroidTheme || readAppTheme) {
context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
}
View view = null;
Object obj = -1;
switch (name.hashCode()) {
case -1946472170:
if (name.equals("RatingBar")) {
obj = 11;
break;
}
break;
case -1455429095:
if (name.equals("CheckedTextView")) {
obj = 8;
break;
}
break;
case -1346021293:
if (name.equals("MultiAutoCompleteTextView")) {
obj = 10;
break;
}
break;
case -938935918:
if (name.equals("TextView")) {
obj = null;
break;
}
break;
case -937446323:
if (name.equals("ImageButton")) {
obj = 5;
break;
}
break;
case -658531749:
if (name.equals("SeekBar")) {
obj = 12;
break;
}
break;
case -339785223:
if (name.equals("Spinner")) {
obj = 4;
break;
}
break;
case 776382189:
if (name.equals("RadioButton")) {
obj = 7;
break;
}
break;
case 1125864064:
if (name.equals("ImageView")) {
obj = 1;
break;
}
break;
case 1413872058:
if (name.equals("AutoCompleteTextView")) {
obj = 9;
break;
}
break;
case 1601505219:
if (name.equals("CheckBox")) {
obj = 6;
break;
}
break;
case 1666676343:
if (name.equals("EditText")) {
obj = 3;
break;
}
break;
case 2001146706:
if (name.equals("Button")) {
obj = 2;
break;
}
break;
}
switch (obj) {
case null:
view = new AppCompatTextView(context, attrs);
break;
case 1:
view = new AppCompatImageView(context, attrs);
break;
case 2:
view = new AppCompatButton(context, attrs);
break;
case 3:
view = new AppCompatEditText(context, attrs);
break;
case 4:
view = new AppCompatSpinner(context, attrs);
break;
case 5:
view = new AppCompatImageButton(context, attrs);
break;
case 6:
view = new AppCompatCheckBox(context, attrs);
break;
case 7:
view = new AppCompatRadioButton(context, attrs);
break;
case 8:
view = new AppCompatCheckedTextView(context, attrs);
break;
case 9:
view = new AppCompatAutoCompleteTextView(context, attrs);
break;
case 10:
view = new AppCompatMultiAutoCompleteTextView(context, attrs);
break;
case 11:
view = new AppCompatRatingBar(context, attrs);
break;
case 12:
view = new AppCompatSeekBar(context, attrs);
break;
}
if (view == null && originalContext != context) {
view = createViewFromTag(context, name, attrs);
}
if (view != null) {
checkOnClickListener(view, attrs);
}
return view;
}
private View createViewFromTag(Context context, String name, AttributeSet attrs) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
try {
this.mConstructorArgs[0] = context;
this.mConstructorArgs[1] = attrs;
View createView;
if (-1 == name.indexOf(46)) {
createView = createView(context, name, "android.widget.");
return createView;
}
createView = createView(context, name, null);
this.mConstructorArgs[0] = null;
this.mConstructorArgs[1] = null;
return createView;
} catch (Exception e) {
return null;
} finally {
this.mConstructorArgs[0] = null;
this.mConstructorArgs[1] = null;
}
}
private void checkOnClickListener(View view, AttributeSet attrs) {
Context context = view.getContext();
if (ViewCompat.hasOnClickListeners(view) && (context instanceof ContextWrapper)) {
TypedArray a = context.obtainStyledAttributes(attrs, sOnClickAttrs);
String handlerName = a.getString(0);
if (handlerName != null) {
view.setOnClickListener(new DeclaredOnClickListener(view, handlerName));
}
a.recycle();
}
}
private View createView(Context context, String name, String prefix) throws ClassNotFoundException, InflateException {
Constructor<? extends View> constructor = (Constructor) sConstructorMap.get(name);
if (constructor == null) {
try {
constructor = context.getClassLoader().loadClass(prefix != null ? prefix + name : name).asSubclass(View.class).getConstructor(sConstructorSignature);
sConstructorMap.put(name, constructor);
} catch (Exception e) {
return null;
}
}
constructor.setAccessible(true);
return (View) constructor.newInstance(this.mConstructorArgs);
}
private static Context themifyContext(Context context, AttributeSet attrs, boolean useAndroidTheme, boolean useAppTheme) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.View, 0, 0);
int themeId = 0;
if (useAndroidTheme) {
themeId = a.getResourceId(R.styleable.View_android_theme, 0);
}
if (useAppTheme && themeId == 0) {
themeId = a.getResourceId(R.styleable.View_theme, 0);
if (themeId != 0) {
Log.i(LOG_TAG, "app:theme is now deprecated. Please move to using android:theme instead.");
}
}
a.recycle();
if (themeId == 0) {
return context;
}
if ((context instanceof ContextThemeWrapper) && ((ContextThemeWrapper) context).getThemeResId() == themeId) {
return context;
}
return new ContextThemeWrapper(context, themeId);
}
}