Skip to content

Commit 664994f

Browse files
committed
Support Android 9.0, API 28
1 parent d83e9d1 commit 664994f

2 files changed

Lines changed: 78 additions & 10 deletions

File tree

Android/DevSample/small/src/main/java/net/wequick/small/ApkBundleLauncher.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ public boolean handleMessage(Message msg) {
163163
}
164164

165165
private void redirectActivityForP(Message msg) {
166+
if (Build.VERSION.SDK_INT >= 28) {
167+
// Following APIs cannot be called again since android 9.0.
168+
return;
169+
}
170+
166171
Object/*android.app.servertransaction.ClientTransaction*/ t = msg.obj;
167172
List callbacks = ReflectAccelerator.getLaunchActivityItems(t);
168173
if (callbacks == null) return;
@@ -189,7 +194,7 @@ public void replace(ActivityInfo targetInfo) {
189194
});
190195
}
191196

192-
private void tryReplaceActivityInfo(Intent intent, ActivityInfoReplacer replacer) {
197+
static void tryReplaceActivityInfo(Intent intent, ActivityInfoReplacer replacer) {
193198
if (intent == null) return;
194199

195200
String targetClass = unwrapIntent(intent);
@@ -338,6 +343,20 @@ public ActivityResult execStartActivity(
338343
who, contextThread, token, target, intent, requestCode);
339344
}
340345

346+
@Override
347+
public Activity newActivity(ClassLoader cl, final String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
348+
final String[] targetClassName = {className};
349+
if (Build.VERSION.SDK_INT >= 28) {
350+
ActivityThreadHandlerCallback.tryReplaceActivityInfo(intent, new ActivityThreadHandlerCallback.ActivityInfoReplacer() {
351+
@Override
352+
public void replace(ActivityInfo info) {
353+
targetClassName[0] = info.targetActivity; // Redirect to the plugin activity
354+
}
355+
});
356+
}
357+
return mBase.newActivity(cl, targetClassName[0], intent);
358+
}
359+
341360
@Override
342361
/** Prepare resources for REAL */
343362
public void callActivityOnCreate(Activity activity, android.os.Bundle icicle) {
@@ -1046,6 +1065,11 @@ public <T> T createObject(Bundle bundle, Context context, String type) {
10461065
* @param ai
10471066
*/
10481067
private static void applyActivityInfo(Activity activity, ActivityInfo ai) {
1068+
// Apply theme (9.0 only)
1069+
if (Build.VERSION.SDK_INT >= 28) {
1070+
ReflectAccelerator.resetResourcesAndTheme(activity, ai.getThemeResource());
1071+
}
1072+
10491073
// Apply window attributes
10501074
Window window = activity.getWindow();
10511075
window.setSoftInputMode(ai.softInputMode);

Android/DevSample/small/src/main/java/net/wequick/small/util/ReflectAccelerator.java

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import android.os.Bundle;
3131
import android.os.IBinder;
3232
import android.util.ArrayMap;
33+
import android.view.ContextThemeWrapper;
3334

3435
import java.io.File;
3536
import java.io.IOException;
@@ -187,7 +188,7 @@ private static Object makeDexElement(File pkg, boolean isDirectory, DexFile dexF
187188
}
188189
if (sDexElementConstructor == null) {
189190
if (Build.VERSION.SDK_INT >= 26) {
190-
sDexElementConstructor = sDexElementClass.getConstructors()[1]; // (DexFile, File)
191+
sDexElementConstructor = sDexElementClass.getConstructor(new Class[]{DexFile.class, File.class});
191192
} else {
192193
sDexElementConstructor = sDexElementClass.getConstructors()[0];
193194
}
@@ -465,12 +466,23 @@ public static int addAssetPath(AssetManager assets, String path) {
465466
}
466467

467468
public static int[] addAssetPaths(AssetManager assets, String[] paths) {
468-
if (sAssetManager_addAssetPaths_method == null) {
469-
sAssetManager_addAssetPaths_method = getMethod(AssetManager.class,
470-
"addAssetPaths", new Class[]{String[].class});
469+
if (Build.VERSION.SDK_INT < 28) {
470+
if (sAssetManager_addAssetPaths_method == null) {
471+
sAssetManager_addAssetPaths_method = getMethod(AssetManager.class,
472+
"addAssetPaths", new Class[]{String[].class});
473+
}
474+
if (sAssetManager_addAssetPaths_method == null) return null;
475+
return invoke(sAssetManager_addAssetPaths_method, assets, new Object[]{paths});
476+
} else {
477+
// `AssetManager#addAssetPaths` becomes unavailable since android 9.0,
478+
// use recursively `addAssetPath` instead.
479+
int N = paths.length;
480+
int[] ids = new int[N];
481+
for (int i = 0; i < N; i++) {
482+
ids[i] = addAssetPath(assets, paths[i]);
483+
}
484+
return ids;
471485
}
472-
if (sAssetManager_addAssetPaths_method == null) return null;
473-
return invoke(sAssetManager_addAssetPaths_method, assets, new Object[]{paths});
474486
}
475487

476488
public static void mergeResources(Application app, Object activityThread, String[] assetPaths) {
@@ -484,9 +496,13 @@ public static void mergeResources(Application app, Object activityThread, String
484496
addAssetPaths(newAssetManager, assetPaths);
485497

486498
try {
487-
Method mEnsureStringBlocks = AssetManager.class.getDeclaredMethod("ensureStringBlocks", new Class[0]);
488-
mEnsureStringBlocks.setAccessible(true);
489-
mEnsureStringBlocks.invoke(newAssetManager, new Object[0]);
499+
if (Build.VERSION.SDK_INT < 28) {
500+
Method mEnsureStringBlocks = AssetManager.class.getDeclaredMethod("ensureStringBlocks", new Class[0]);
501+
mEnsureStringBlocks.setAccessible(true);
502+
mEnsureStringBlocks.invoke(newAssetManager, new Object[0]);
503+
} else {
504+
// `AssetManager#ensureStringBlocks` becomes unavailable since android 9.0
505+
}
490506

491507
Collection<WeakReference<Resources>> references;
492508

@@ -737,6 +753,34 @@ public static Intent getIntentOfLaunchActivityItem(Object item) {
737753
return getValue(f, item);
738754
}
739755

756+
public static void resetResourcesAndTheme(Activity activity, int themeId) {
757+
AssetManager newAssetManager = activity.getApplication().getAssets();
758+
Resources resources = activity.getResources();
759+
760+
// Set the activity resources assets to the application one
761+
try {
762+
Field mResourcesImpl = Resources.class.getDeclaredField("mResourcesImpl");
763+
mResourcesImpl.setAccessible(true);
764+
Object resourceImpl = mResourcesImpl.get(resources);
765+
Field implAssets = resourceImpl.getClass().getDeclaredField("mAssets");
766+
implAssets.setAccessible(true);
767+
implAssets.set(resourceImpl, newAssetManager);
768+
} catch (Throwable e) {
769+
android.util.Log.e("Small", "Failed to update resources for activity " + activity, e);
770+
}
771+
772+
// Reset the theme
773+
try {
774+
Field mt = ContextThemeWrapper.class.getDeclaredField("mTheme");
775+
mt.setAccessible(true);
776+
mt.set(activity, null);
777+
} catch (Throwable e) {
778+
android.util.Log.e("Small", "Failed to update existing theme for activity " + activity, e);
779+
}
780+
781+
activity.setTheme(themeId);
782+
}
783+
740784
//______________________________________________________________________________________________
741785
// Private
742786

0 commit comments

Comments
 (0)