am e8518e7e: am f8fdf87c: Merge "Fix handling of android:onClick for ContextWrapper" into mnc-dev

* commit 'e8518e7ee729597d2b2141c0c4aba695eb529028':
  Fix handling of android:onClick for ContextWrapper
This commit is contained in:
Alan Viverette
2015-04-28 18:19:58 +00:00
committed by Android Git Automerger

View File

@@ -30,6 +30,7 @@ import android.annotation.Nullable;
import android.annotation.Size;
import android.content.ClipData;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -4016,37 +4017,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
final String handlerName = a.getString(attr);
if (handlerName != null) {
setOnClickListener(new OnClickListener() {
private Method mHandler;
public void onClick(View v) {
if (mHandler == null) {
try {
mHandler = getContext().getClass().getMethod(handlerName,
View.class);
} catch (NoSuchMethodException e) {
int id = getId();
String idText = id == NO_ID ? "" : " with id '"
+ getContext().getResources().getResourceEntryName(
id) + "'";
throw new IllegalStateException("Could not find a method " +
handlerName + "(View) in the activity "
+ getContext().getClass() + " for onClick handler"
+ " on view " + View.this.getClass() + idText, e);
}
}
try {
mHandler.invoke(getContext(), View.this);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not execute non "
+ "public method of the activity", e);
} catch (InvocationTargetException e) {
throw new IllegalStateException("Could not execute "
+ "method of the activity", e);
}
}
});
setOnClickListener(new DeclaredOnClickListener(this, handlerName));
}
break;
case R.styleable.View_overScrollMode:
@@ -4237,6 +4208,66 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
computeOpaqueFlags();
}
/**
* An implementation of OnClickListener that attempts to lazily load a
* named click handling method from a parent or ancestor context.
*/
private static class DeclaredOnClickListener implements OnClickListener {
private final View mHostView;
private final String mMethodName;
private Method mMethod;
public DeclaredOnClickListener(@NonNull View hostView, @NonNull String methodName) {
mHostView = hostView;
mMethodName = methodName;
}
@Override
public void onClick(@NonNull View v) {
if (mMethod == null) {
mMethod = resolveMethod(mHostView.getContext(), mMethodName);
}
try {
mMethod.invoke(mHostView.getContext(), v);
} catch (IllegalAccessException e) {
throw new IllegalStateException(
"Could not execute non-public method for android:onClick", e);
} catch (InvocationTargetException e) {
throw new IllegalStateException(
"Could not execute method for android:onClick", e);
}
}
@NonNull
private Method resolveMethod(@Nullable Context context, @NonNull String name) {
while (context != null) {
try {
if (!context.isRestricted()) {
return context.getClass().getMethod(mMethodName, View.class);
}
} catch (NoSuchMethodException e) {
// Failed to find method, keep searching up the hierarchy.
}
if (context instanceof ContextWrapper) {
context = ((ContextWrapper) context).getBaseContext();
} else {
// Can't search up the hierarchy, null out and fail.
context = null;
}
}
final int id = mHostView.getId();
final String idText = id == NO_ID ? "" : " with id '"
+ mHostView.getContext().getResources().getResourceEntryName(id) + "'";
throw new IllegalStateException("Could not find method " + mMethodName
+ "(View) in a parent or ancestor Context for android:onClick "
+ "attribute defined on view " + mHostView.getClass() + idText);
}
}
/**
* Non-public constructor for use in testing
*/