package me.ele.amigo;
import android.app.Activity;
import android.app.Fragment;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import me.ele.amigo.reflect.FieldUtils;
import me.ele.amigo.stub.ActivityStub;
import me.ele.amigo.utils.component.ActivityFinder;
import static me.ele.amigo.utils.component.ActivityFinder.getActivityInfoInNewApp;
public class AmigoInstrumentation extends Instrumentation implements IInstrumentation {
public static final String EXTRA_TARGET_INTENT = "me.ele.amigo.OldIntent";
public static final String EXTRA_TARGET_INFO = "me.ele.amigo.OldInfo";
public static final String EXTRA_STUB_NAME = "me.ele.amigo.stub";
private static final String TAG = AmigoInstrumentation.class.getSimpleName();
private Instrumentation oldInstrumentation;
private Method methodExecStart1;
private Method methodExecStart2;
private Method methodExecStart3;
private Method methodExecStart4;
private Method methodExecStart5;
private Method methodExecStart6;
public AmigoInstrumentation(Instrumentation oldInstrumentation) {
this.oldInstrumentation = oldInstrumentation;
}
private Intent wrapIntent(Context who, Intent intent) {
Amigo.rollAmigoBack(who);
Pair<Boolean, Intent> result = getRealIntent(who, intent);
if (result == null) {
return null;
}
if (result.first) {
return intent;
}
ComponentName componentName = intent.getComponent();
ActivityStub.recycleActivityStub(getActivityInfo(who, componentName.getClassName()));
Class stubClazz = getDelegateActivityName(who, componentName.getClassName());
if (stubClazz == null) {
Log.e(TAG, "wrapIntent: weird, no stubs available for now.");
return intent;
}
Intent stubIntent = new Intent();
stubIntent.setComponent(new ComponentName(componentName.getPackageName(), stubClazz.getName()));
stubIntent.putExtra(EXTRA_TARGET_INTENT, intent);
stubIntent.setFlags(intent.getFlags());
intent.putExtra(EXTRA_STUB_NAME, stubClazz);
ActivityStub.onActivityCreated(stubClazz, null, componentName.getClassName());
return stubIntent;
}
private Pair<Boolean, Intent> getRealIntent(Context who, Intent intent) {
if (intent == null) {
return null;
}
ComponentName componentName = intent.getComponent();
if (componentName == null) {
componentName = intent.resolveActivity(who.getPackageManager());
intent.setComponent(componentName);
}
if (componentName != null) {
//search from host
ActivityInfo[] activityInfos = ActivityFinder.getAppActivities(who);
for (ActivityInfo activityInfo : activityInfos) {
if (activityInfo.name.equals(componentName.getClassName())) {
boolean isAlias = !TextUtils.isEmpty(activityInfo.targetActivity);
if (!isAlias) {
return new Pair<>(true, intent);
} else {
intent.setComponent(new ComponentName(componentName.getPackageName(), activityInfo.targetActivity));
return getRealIntent(who, intent);
}
}
}
}
//search from patch
ActivityInfo info = getActivityInfoInNewApp(who, intent);
if (info == null) {
return new Pair<>(true, intent);
}
intent.setComponent(new ComponentName(who.getPackageName(), info.name));
return new Pair<>(false, intent);
}
@Override
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token,
Activity target,
Intent intent, int requestCode, Bundle options) {
try {
intent = wrapIntent(who, intent);
if (methodExecStart1 == null) {
methodExecStart1 = oldInstrumentation.getClass().getDeclaredMethod
("execStartActivity",
Context.class, IBinder.class, IBinder.class, Activity.class,
Intent.class,
int.class, Bundle.class);
}
return (ActivityResult) methodExecStart1.invoke(oldInstrumentation, who,
contextThread, token,
target, intent, requestCode, options);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
@Override
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token,
Activity target,
Intent intent, int requestCode) {
try {
intent = wrapIntent(who, intent);
if (methodExecStart2 == null) {
methodExecStart2 = oldInstrumentation.getClass().getDeclaredMethod
("execStartActivity",
Context.class, IBinder.class, IBinder.class, Activity.class,
Intent.class,
int.class);
}
return (ActivityResult) methodExecStart2.invoke(oldInstrumentation, who,
contextThread, token,
target, intent, requestCode);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
@Override
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token,
Fragment target, Intent intent, int requestCode) {
try {
intent = wrapIntent(who, intent);
if (methodExecStart3 == null) {
methodExecStart3 = oldInstrumentation.getClass().getDeclaredMethod
("execStartActivity",
Context.class, IBinder.class, IBinder.class, Fragment.class,
Intent.class,
int.class, Bundle.class);
}
return (ActivityResult) methodExecStart3.invoke(oldInstrumentation, who,
contextThread, token,
target, intent, requestCode);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
@Override
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token,
Fragment target, Intent intent, int requestCode,
Bundle options) {
try {
intent = wrapIntent(who, intent);
if (methodExecStart4 == null) {
methodExecStart4 = oldInstrumentation.getClass().getDeclaredMethod
("execStartActivity",
Context.class, IBinder.class, IBinder.class, Fragment.class,
Intent.class,
int.class, Bundle.class);
}
return (ActivityResult) methodExecStart4.invoke(oldInstrumentation, who,
contextThread, token,
target, intent, requestCode, options);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
@Override
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token,
Activity target, Intent intent, int requestCode,
Bundle options, UserHandle user) {
try {
intent = wrapIntent(who, intent);
if (methodExecStart5 == null)
methodExecStart5 = oldInstrumentation.getClass().getDeclaredMethod
("execStartActivity",
Context.class, IBinder.class, IBinder.class, Activity.class,
Intent.class,
int.class, Bundle.class, UserHandle.class);
return (ActivityResult) methodExecStart5.invoke(oldInstrumentation, who,
contextThread, token,
target, intent, requestCode, options, user);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
@Override
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token,
String target, Intent intent, int requestCode, Bundle
options) {
try {
intent = wrapIntent(who, intent);
if (methodExecStart6 == null)
methodExecStart6 = oldInstrumentation.getClass().getDeclaredMethod
("execStartActivity",
Context.class, IBinder.class, IBinder.class, String.class, Intent
.class, int
.class, Bundle.class);
return (ActivityResult) methodExecStart6.invoke(oldInstrumentation, who,
contextThread, token,
target, intent, requestCode, options);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
private Class getDelegateActivityName(Context context, String targetClassName) {
return ActivityStub.selectActivityStubClazz(getActivityInfo(context, targetClassName));
}
private ActivityInfo getActivityInfo(Context context, String targetClassName) {
if (targetClassName.startsWith(".")) {
targetClassName = context.getPackageName() + targetClassName;
}
ActivityInfo info = getActivityInfoInNewApp(context, targetClassName);
if (info == null) {
throw new RuntimeException(String.format("cannot find %s in apk", targetClassName));
}
return info;
}
@Override
public void callActivityOnNewIntent(Activity activity, Intent intent) {
super.callActivityOnNewIntent(activity, intent);
}
@Override
public void callActivityOnCreate(Activity activity, Bundle icicle) {
try {
boolean result = Amigo.rollAmigoBack(activity);
Intent targetIntent = activity.getIntent();
if (targetIntent != null) {
if (result) {
targetIntent.setExtrasClassLoader(activity.getClassLoader());
}
ActivityInfo targetInfo = targetIntent.getParcelableExtra(EXTRA_TARGET_INFO);
if (targetInfo != null) {
activity.setRequestedOrientation(targetInfo.screenOrientation);
ComponentName componentName = new ComponentName(activity,
getDelegateActivityName(activity, activity.getClass().getName()));
FieldUtils.writeField(activity, "mComponent", componentName);
Class stubClazz = (Class) targetIntent.getSerializableExtra(EXTRA_STUB_NAME);
if (stubClazz != null)
ActivityStub.onActivityCreated(stubClazz, activity, "");
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (oldInstrumentation != null) {
oldInstrumentation.callActivityOnCreate(activity, icicle);
} else {
super.callActivityOnCreate(activity, icicle);
}
}
@Override
public void callActivityOnDestroy(Activity activity) {
if (oldInstrumentation != null) {
oldInstrumentation.callActivityOnDestroy(activity);
} else {
super.callActivityOnDestroy(activity);
}
Intent intent = activity.getIntent();
Class stubClazz;
if (intent != null && (stubClazz = (Class) intent.getSerializableExtra(EXTRA_STUB_NAME))
!= null)
ActivityStub.onActivityDestroyed(stubClazz, activity);
}
}