react-native-update 10.39.0 → 10.40.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +0 -6
- package/android/jni/Application.mk +1 -1
- package/android/lib/arm64-v8a/librnupdate.so +0 -0
- package/android/lib/armeabi-v7a/librnupdate.so +0 -0
- package/android/lib/x86/librnupdate.so +0 -0
- package/android/lib/x86_64/librnupdate.so +0 -0
- package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +314 -0
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +253 -586
- package/android/src/main/java/cn/reactnative/modules/update/NativeUpdateCore.java +1 -9
- package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +220 -0
- package/android/src/main/java/cn/reactnative/modules/update/SafeZipFile.java +9 -3
- package/android/src/main/java/cn/reactnative/modules/update/UiThreadRunner.java +36 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +36 -26
- package/android/src/main/java/cn/reactnative/modules/update/UpdateEventEmitter.java +39 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateFileUtils.java +74 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +143 -260
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleSupport.java +63 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdatePackage.java +1 -5
- package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +26 -73
- package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +28 -242
- package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +89 -135
- package/harmony/pushy/src/main/cpp/PushyTurboModule.h +5 -5
- package/harmony/pushy/src/main/ets/DownloadTaskParams.ts +7 -7
- package/harmony/pushy/src/main/ets/PushyPackage.ets +3 -9
- package/harmony/pushy/src/main/ets/PushyPackageCompat.ts +3 -9
- package/harmony/pushy/src/main/ets/PushyPackageFactory.ts +14 -0
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +124 -24
- package/harmony/pushy/src/main/ets/UpdateContext.ts +92 -70
- package/harmony/pushy.har +0 -0
- package/ios/Expo/ExpoPushyReactDelegateHandler.swift +6 -26
- package/ios/RCTPushy/RCTPushy.mm +315 -259
- package/ios/RCTPushy/RCTPushyDownloader.mm +52 -29
- package/package.json +2 -2
- package/react-native-update.podspec +3 -3
- package/harmony/pushy/src/main/ets/UpdateModuleImpl.ts +0 -123
- package/ios/ImportReact.h +0 -2
- package/ios/RCTPushy/HDiffPatch/HDiffPatch.h +0 -16
- package/ios/RCTPushy/HDiffPatch/HDiffPatch.mm +0 -35
- package/ios/RCTPushy/RCTPushyManager.h +0 -27
- package/ios/RCTPushy/RCTPushyManager.mm +0 -181
- package/ios/RCTPushy.xcodeproj/project.pbxproj +0 -479
- package/package/harmony/pushy.har +0 -0
- package/react-native-update-10.39.0-beta.3.tgz +0 -0
- package/scripts/prune-host-stl.sh +0 -6
|
@@ -11,19 +11,11 @@ final class NativeUpdateCore {
|
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
try {
|
|
15
|
-
System.loadLibrary("c++_shared");
|
|
16
|
-
} catch (UnsatisfiedLinkError ignored) {
|
|
17
|
-
// Fall back to the transitive dependency load path when the host app already
|
|
18
|
-
// packages libc++_shared.so but the linker has not loaded it yet.
|
|
19
|
-
}
|
|
20
|
-
|
|
21
14
|
try {
|
|
22
15
|
System.loadLibrary("rnupdate");
|
|
23
16
|
} catch (UnsatisfiedLinkError error) {
|
|
24
17
|
UnsatisfiedLinkError wrapped = new UnsatisfiedLinkError(
|
|
25
|
-
"Failed to load rnupdate
|
|
26
|
-
+ "when using the shared C++ runtime. Original error: "
|
|
18
|
+
"Failed to load rnupdate native library. Original error: "
|
|
27
19
|
+ error.getMessage());
|
|
28
20
|
wrapped.initCause(error);
|
|
29
21
|
throw wrapped;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
package cn.reactnative.modules.update;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.content.Context;
|
|
5
|
+
import android.util.Log;
|
|
6
|
+
import androidx.annotation.Nullable;
|
|
7
|
+
import com.facebook.react.ReactActivity;
|
|
8
|
+
import com.facebook.react.ReactApplication;
|
|
9
|
+
import com.facebook.react.ReactDelegate;
|
|
10
|
+
import com.facebook.react.ReactInstanceManager;
|
|
11
|
+
import com.facebook.react.ReactNativeHost;
|
|
12
|
+
import com.facebook.react.bridge.JSBundleLoader;
|
|
13
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
14
|
+
import java.lang.reflect.Field;
|
|
15
|
+
import java.lang.reflect.Method;
|
|
16
|
+
|
|
17
|
+
final class ReactReloadManager {
|
|
18
|
+
private ReactReloadManager() {
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static void restartApp(
|
|
22
|
+
UpdateContext updateContext,
|
|
23
|
+
ReactApplicationContext reactContext,
|
|
24
|
+
@Nullable String hash
|
|
25
|
+
) throws Throwable {
|
|
26
|
+
if (hash != null) {
|
|
27
|
+
updateContext.switchVersion(hash);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Context application = reactContext.getApplicationContext();
|
|
31
|
+
Activity currentActivity = reactContext.getCurrentActivity();
|
|
32
|
+
String updateBundlePath = updateContext.getBundleUrl();
|
|
33
|
+
|
|
34
|
+
Object reactHost = getReactHost(currentActivity, application);
|
|
35
|
+
if (reactHost != null) {
|
|
36
|
+
try {
|
|
37
|
+
reloadReactHost(reactHost, createBundleLoader(application, updateBundlePath, true));
|
|
38
|
+
return;
|
|
39
|
+
} catch (Throwable err) {
|
|
40
|
+
Log.e(UpdateContext.TAG, "Failed to reload via ReactHost", err);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
JSBundleLoader loader = createBundleLoader(application, updateBundlePath, false);
|
|
45
|
+
try {
|
|
46
|
+
ReactInstanceManager instanceManager =
|
|
47
|
+
resolveReactInstanceManager(updateContext, application);
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
Field loadField = instanceManager.getClass().getDeclaredField("mBundleLoader");
|
|
51
|
+
loadField.setAccessible(true);
|
|
52
|
+
loadField.set(instanceManager, loader);
|
|
53
|
+
} catch (Throwable err) {
|
|
54
|
+
Field jsBundleField = instanceManager.getClass().getDeclaredField("mJSBundleFile");
|
|
55
|
+
jsBundleField.setAccessible(true);
|
|
56
|
+
jsBundleField.set(instanceManager, updateContext.getBundleUrl());
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
instanceManager.recreateReactContextInBackground();
|
|
60
|
+
} catch (Throwable err) {
|
|
61
|
+
if (currentActivity == null) {
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
Object currentReactHost = getReactHost(currentActivity, application);
|
|
67
|
+
if (currentReactHost == null) {
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
reloadReactHost(
|
|
71
|
+
currentReactHost,
|
|
72
|
+
createBundleLoader(application, updateBundlePath, true)
|
|
73
|
+
);
|
|
74
|
+
} catch (Throwable ignored) {
|
|
75
|
+
currentActivity.recreate();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private static Field getCompatibleField(Class<?> clazz, String fieldName)
|
|
81
|
+
throws NoSuchFieldException {
|
|
82
|
+
try {
|
|
83
|
+
return clazz.getDeclaredField("m" + capitalize(fieldName));
|
|
84
|
+
} catch (NoSuchFieldException e) {
|
|
85
|
+
try {
|
|
86
|
+
return clazz.getDeclaredField(fieldName);
|
|
87
|
+
} catch (NoSuchFieldException e2) {
|
|
88
|
+
throw new NoSuchFieldException(
|
|
89
|
+
"Field not found with either name: m"
|
|
90
|
+
+ capitalize(fieldName)
|
|
91
|
+
+ " or "
|
|
92
|
+
+ fieldName
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private static String capitalize(String str) {
|
|
99
|
+
if (str == null || str.length() == 0) {
|
|
100
|
+
return str;
|
|
101
|
+
}
|
|
102
|
+
return str.substring(0, 1).toUpperCase() + str.substring(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private static String getDefaultBundleAssetName(Context application) {
|
|
106
|
+
String bundleAssetName = "index.android.bundle";
|
|
107
|
+
if (!(application instanceof ReactApplication)) {
|
|
108
|
+
return bundleAssetName;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
ReactNativeHost reactNativeHost = ((ReactApplication) application).getReactNativeHost();
|
|
113
|
+
if (reactNativeHost == null) {
|
|
114
|
+
return bundleAssetName;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
Method getBundleAssetNameMethod = ReactNativeHost.class.getDeclaredMethod("getBundleAssetName");
|
|
118
|
+
getBundleAssetNameMethod.setAccessible(true);
|
|
119
|
+
Object resolvedBundleAssetName = getBundleAssetNameMethod.invoke(reactNativeHost);
|
|
120
|
+
if (resolvedBundleAssetName instanceof String && !((String) resolvedBundleAssetName).isEmpty()) {
|
|
121
|
+
return (String) resolvedBundleAssetName;
|
|
122
|
+
}
|
|
123
|
+
} catch (Exception e) {
|
|
124
|
+
Log.e(UpdateContext.TAG, "Failed to get default asset name from ReactNativeHost", e);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return bundleAssetName;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private static String toAssetUrl(String bundleAssetName) {
|
|
131
|
+
if (bundleAssetName == null || bundleAssetName.isEmpty()) {
|
|
132
|
+
return "assets://index.android.bundle";
|
|
133
|
+
}
|
|
134
|
+
if (bundleAssetName.startsWith("assets://")) {
|
|
135
|
+
return bundleAssetName;
|
|
136
|
+
}
|
|
137
|
+
return "assets://" + bundleAssetName;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private static JSBundleLoader createBundleLoader(
|
|
141
|
+
Context application,
|
|
142
|
+
@Nullable String updateBundlePath,
|
|
143
|
+
boolean loadAssetSynchronously
|
|
144
|
+
) {
|
|
145
|
+
if (updateBundlePath != null) {
|
|
146
|
+
return JSBundleLoader.createFileLoader(updateBundlePath);
|
|
147
|
+
}
|
|
148
|
+
return JSBundleLoader.createAssetLoader(
|
|
149
|
+
application,
|
|
150
|
+
toAssetUrl(getDefaultBundleAssetName(application)),
|
|
151
|
+
loadAssetSynchronously
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
@Nullable
|
|
156
|
+
private static Object getReactHost(@Nullable Activity currentActivity, Context application) {
|
|
157
|
+
if (currentActivity instanceof ReactActivity) {
|
|
158
|
+
try {
|
|
159
|
+
Method getReactDelegateMethod = ReactActivity.class.getMethod("getReactDelegate");
|
|
160
|
+
ReactDelegate reactDelegate =
|
|
161
|
+
(ReactDelegate) getReactDelegateMethod.invoke(currentActivity);
|
|
162
|
+
if (reactDelegate != null) {
|
|
163
|
+
Field reactHostField = getCompatibleField(reactDelegate.getClass(), "reactHost");
|
|
164
|
+
reactHostField.setAccessible(true);
|
|
165
|
+
Object reactHost = reactHostField.get(reactDelegate);
|
|
166
|
+
if (reactHost != null) {
|
|
167
|
+
return reactHost;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} catch (Throwable ignored) {
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
Method getReactHostMethod = application.getClass().getMethod("getReactHost");
|
|
176
|
+
return getReactHostMethod.invoke(application);
|
|
177
|
+
} catch (Throwable ignored) {
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private static void reloadReactHost(Object reactHost, JSBundleLoader loader) throws Throwable {
|
|
184
|
+
try {
|
|
185
|
+
Field devSupportField = getCompatibleField(reactHost.getClass(), "useDevSupport");
|
|
186
|
+
devSupportField.setAccessible(true);
|
|
187
|
+
devSupportField.set(reactHost, false);
|
|
188
|
+
} catch (Throwable ignored) {
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
Field reactHostDelegateField = getCompatibleField(reactHost.getClass(), "reactHostDelegate");
|
|
192
|
+
reactHostDelegateField.setAccessible(true);
|
|
193
|
+
Object reactHostDelegate = reactHostDelegateField.get(reactHost);
|
|
194
|
+
|
|
195
|
+
String bundleFieldName = "jsBundleLoader";
|
|
196
|
+
if ("expo.modules.ExpoReactHostFactory.ExpoReactHostDelegate".equals(
|
|
197
|
+
reactHostDelegate.getClass().getCanonicalName()
|
|
198
|
+
)) {
|
|
199
|
+
bundleFieldName = "_jsBundleLoader";
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
Field jsBundleLoaderField = reactHostDelegate.getClass().getDeclaredField(bundleFieldName);
|
|
203
|
+
jsBundleLoaderField.setAccessible(true);
|
|
204
|
+
jsBundleLoaderField.set(reactHostDelegate, loader);
|
|
205
|
+
|
|
206
|
+
Method reloadMethod = reactHost.getClass().getMethod("reload", String.class);
|
|
207
|
+
reloadMethod.invoke(reactHost, "react-native-update");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private static ReactInstanceManager resolveReactInstanceManager(
|
|
211
|
+
UpdateContext updateContext,
|
|
212
|
+
Context application
|
|
213
|
+
) {
|
|
214
|
+
ReactInstanceManager instanceManager = updateContext.getCustomReactInstanceManager();
|
|
215
|
+
if (instanceManager != null) {
|
|
216
|
+
return instanceManager;
|
|
217
|
+
}
|
|
218
|
+
return ((ReactApplication) application).getReactNativeHost().getReactInstanceManager();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -66,8 +66,9 @@ public class SafeZipFile extends ZipFile {
|
|
|
66
66
|
throw new SecurityException("Illegal name: " + name);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
if (UpdateContext.DEBUG) {
|
|
70
|
+
Log.d(UpdateContext.TAG, "Unzipping " + name);
|
|
71
|
+
}
|
|
71
72
|
|
|
72
73
|
if (ze.isDirectory()) {
|
|
73
74
|
target.mkdirs();
|
|
@@ -77,6 +78,11 @@ public class SafeZipFile extends ZipFile {
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
public void unzipToFile(ZipEntry ze, File target) throws IOException {
|
|
81
|
+
File parent = target.getParentFile();
|
|
82
|
+
if (parent != null && !parent.exists() && !parent.mkdirs() && !parent.exists()) {
|
|
83
|
+
throw new IOException("Failed to create parent dir for " + target);
|
|
84
|
+
}
|
|
85
|
+
|
|
80
86
|
try (InputStream inputStream = getInputStream(ze)) {
|
|
81
87
|
try (BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(target));
|
|
82
88
|
BufferedInputStream input = new BufferedInputStream(inputStream)) {
|
|
@@ -89,4 +95,4 @@ public class SafeZipFile extends ZipFile {
|
|
|
89
95
|
}
|
|
90
96
|
}
|
|
91
97
|
|
|
92
|
-
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
package cn.reactnative.modules.update;
|
|
2
|
+
|
|
3
|
+
import android.util.Log;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
import com.facebook.react.bridge.Promise;
|
|
6
|
+
import com.facebook.react.bridge.UiThreadUtil;
|
|
7
|
+
|
|
8
|
+
final class UiThreadRunner {
|
|
9
|
+
interface Operation {
|
|
10
|
+
void run() throws Throwable;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
private UiThreadRunner() {
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static void run(
|
|
17
|
+
@Nullable final Promise promise,
|
|
18
|
+
final String operationName,
|
|
19
|
+
final Operation operation
|
|
20
|
+
) {
|
|
21
|
+
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
22
|
+
@Override
|
|
23
|
+
public void run() {
|
|
24
|
+
try {
|
|
25
|
+
operation.run();
|
|
26
|
+
} catch (Throwable error) {
|
|
27
|
+
if (promise != null) {
|
|
28
|
+
promise.reject(operationName + " failed", error);
|
|
29
|
+
} else {
|
|
30
|
+
Log.e(UpdateContext.TAG, operationName + " failed", error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -8,25 +8,26 @@ import android.os.Build;
|
|
|
8
8
|
import android.os.Environment;
|
|
9
9
|
import android.util.Log;
|
|
10
10
|
import com.facebook.react.ReactInstanceManager;
|
|
11
|
-
import java.
|
|
12
|
-
import java.util.Map;
|
|
11
|
+
import java.io.File;
|
|
13
12
|
import java.util.concurrent.Executor;
|
|
14
13
|
import java.util.concurrent.Executors;
|
|
15
|
-
import java.io.File;
|
|
16
14
|
|
|
17
15
|
public class UpdateContext {
|
|
18
16
|
static {
|
|
19
17
|
NativeUpdateCore.ensureLoaded();
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
private Executor executor;
|
|
20
|
+
static final String TAG = "react-native-update";
|
|
21
|
+
static final boolean DEBUG = BuildConfig.DEBUG;
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
private
|
|
28
|
-
private
|
|
29
|
-
private
|
|
23
|
+
private final Context context;
|
|
24
|
+
private final File rootDir;
|
|
25
|
+
private final Executor executor;
|
|
26
|
+
private final SharedPreferences sp;
|
|
27
|
+
|
|
28
|
+
private ReactInstanceManager reactInstanceManager;
|
|
29
|
+
private boolean isUsingBundleUrl;
|
|
30
|
+
private boolean ignoreRollback;
|
|
30
31
|
private static final int STATE_OP_SWITCH_VERSION = 1;
|
|
31
32
|
private static final int STATE_OP_MARK_SUCCESS = 2;
|
|
32
33
|
private static final int STATE_OP_ROLLBACK = 3;
|
|
@@ -38,6 +39,7 @@ public class UpdateContext {
|
|
|
38
39
|
// Singleton instance
|
|
39
40
|
private static UpdateContext sInstance;
|
|
40
41
|
private static final Object sLock = new Object();
|
|
42
|
+
private static ReactInstanceManager pendingReactInstanceManager;
|
|
41
43
|
|
|
42
44
|
private static native StateCoreResult syncStateWithBinaryVersion(
|
|
43
45
|
String packageVersion,
|
|
@@ -54,16 +56,17 @@ public class UpdateContext {
|
|
|
54
56
|
);
|
|
55
57
|
|
|
56
58
|
public UpdateContext(Context context) {
|
|
57
|
-
this.context = context;
|
|
59
|
+
this.context = context.getApplicationContext();
|
|
58
60
|
this.executor = Executors.newSingleThreadExecutor();
|
|
59
61
|
|
|
60
|
-
this.rootDir = new File(context.getFilesDir(), "_update");
|
|
62
|
+
this.rootDir = new File(this.context.getFilesDir(), "_update");
|
|
61
63
|
|
|
62
|
-
if (!rootDir.exists()) {
|
|
63
|
-
rootDir
|
|
64
|
+
if (!rootDir.exists() && !rootDir.mkdirs() && !rootDir.exists()) {
|
|
65
|
+
throw new IllegalStateException("Failed to create update root dir: " + rootDir);
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
this.sp = context.getSharedPreferences("update", Context.MODE_PRIVATE);
|
|
68
|
+
this.sp = this.context.getSharedPreferences("update", Context.MODE_PRIVATE);
|
|
69
|
+
this.reactInstanceManager = pendingReactInstanceManager;
|
|
67
70
|
|
|
68
71
|
String packageVersion = getPackageVersion();
|
|
69
72
|
String buildTime = getBuildTime();
|
|
@@ -107,6 +110,10 @@ public class UpdateContext {
|
|
|
107
110
|
return isUsingBundleUrl;
|
|
108
111
|
}
|
|
109
112
|
|
|
113
|
+
private void enqueue(DownloadTaskParams params) {
|
|
114
|
+
executor.execute(new DownloadTask(context, params));
|
|
115
|
+
}
|
|
116
|
+
|
|
110
117
|
public interface DownloadFileListener {
|
|
111
118
|
void onDownloadCompleted(DownloadTaskParams params);
|
|
112
119
|
void onDownloadFailed(Throwable error);
|
|
@@ -120,7 +127,7 @@ public class UpdateContext {
|
|
|
120
127
|
params.listener = listener;
|
|
121
128
|
params.targetFile = new File(rootDir, hash + ".ppk");
|
|
122
129
|
params.unzipDirectory = new File(rootDir, hash);
|
|
123
|
-
|
|
130
|
+
enqueue(params);
|
|
124
131
|
}
|
|
125
132
|
|
|
126
133
|
public void downloadFile(String url, String hash, String fileName, DownloadFileListener listener) {
|
|
@@ -138,7 +145,7 @@ public class UpdateContext {
|
|
|
138
145
|
|
|
139
146
|
}
|
|
140
147
|
// params.unzipDirectory = new File(rootDir, hash);
|
|
141
|
-
|
|
148
|
+
enqueue(params);
|
|
142
149
|
}
|
|
143
150
|
|
|
144
151
|
public void downloadPatchFromApk(String url, String hash, DownloadFileListener listener) {
|
|
@@ -149,7 +156,7 @@ public class UpdateContext {
|
|
|
149
156
|
params.listener = listener;
|
|
150
157
|
params.targetFile = new File(rootDir, hash + ".apk.patch");
|
|
151
158
|
params.unzipDirectory = new File(rootDir, hash);
|
|
152
|
-
|
|
159
|
+
enqueue(params);
|
|
153
160
|
}
|
|
154
161
|
|
|
155
162
|
public void downloadPatchFromPpk(String url, String hash, String originHash, DownloadFileListener listener) {
|
|
@@ -162,11 +169,9 @@ public class UpdateContext {
|
|
|
162
169
|
params.targetFile = new File(rootDir, originHash + "-" + hash + ".ppk.patch");
|
|
163
170
|
params.unzipDirectory = new File(rootDir, hash);
|
|
164
171
|
params.originDirectory = new File(rootDir, originHash);
|
|
165
|
-
|
|
172
|
+
enqueue(params);
|
|
166
173
|
}
|
|
167
174
|
|
|
168
|
-
private SharedPreferences sp;
|
|
169
|
-
|
|
170
175
|
private StateCoreResult getStateSnapshot() {
|
|
171
176
|
StateCoreResult state = new StateCoreResult();
|
|
172
177
|
state.packageVersion = sp.getString("packageVersion", null);
|
|
@@ -203,7 +208,7 @@ public class UpdateContext {
|
|
|
203
208
|
|
|
204
209
|
private void persistEditor(SharedPreferences.Editor editor, String reason) {
|
|
205
210
|
if (!editor.commit() && DEBUG) {
|
|
206
|
-
Log.w(
|
|
211
|
+
Log.w(TAG, "Failed to persist update state for " + reason);
|
|
207
212
|
}
|
|
208
213
|
}
|
|
209
214
|
|
|
@@ -314,11 +319,16 @@ public class UpdateContext {
|
|
|
314
319
|
|
|
315
320
|
|
|
316
321
|
public static void setCustomInstanceManager(ReactInstanceManager instanceManager) {
|
|
317
|
-
|
|
322
|
+
synchronized (sLock) {
|
|
323
|
+
pendingReactInstanceManager = instanceManager;
|
|
324
|
+
if (sInstance != null) {
|
|
325
|
+
sInstance.reactInstanceManager = instanceManager;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
318
328
|
}
|
|
319
329
|
|
|
320
330
|
public ReactInstanceManager getCustomReactInstanceManager() {
|
|
321
|
-
return
|
|
331
|
+
return reactInstanceManager;
|
|
322
332
|
}
|
|
323
333
|
|
|
324
334
|
/**
|
|
@@ -378,7 +388,7 @@ public class UpdateContext {
|
|
|
378
388
|
while (currentVersion != null) {
|
|
379
389
|
File bundleFile = new File(rootDir, currentVersion+"/index.bundlejs");
|
|
380
390
|
if (!bundleFile.exists()) {
|
|
381
|
-
Log.e(
|
|
391
|
+
Log.e(TAG, "Bundle version " + currentVersion + " not found.");
|
|
382
392
|
currentVersion = this.rollBack();
|
|
383
393
|
continue;
|
|
384
394
|
}
|
|
@@ -409,6 +419,6 @@ public class UpdateContext {
|
|
|
409
419
|
params.hash = sp.getString("currentVersion", null);
|
|
410
420
|
params.originHash = sp.getString("lastVersion", null);
|
|
411
421
|
params.unzipDirectory = rootDir;
|
|
412
|
-
|
|
422
|
+
enqueue(params);
|
|
413
423
|
}
|
|
414
424
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
package cn.reactnative.modules.update;
|
|
2
|
+
|
|
3
|
+
import android.util.Log;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
6
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
7
|
+
import com.facebook.react.bridge.WritableMap;
|
|
8
|
+
import java.lang.ref.WeakReference;
|
|
9
|
+
|
|
10
|
+
final class UpdateEventEmitter {
|
|
11
|
+
private static WeakReference<ReactApplicationContext> reactContextRef =
|
|
12
|
+
new WeakReference<ReactApplicationContext>(null);
|
|
13
|
+
|
|
14
|
+
private UpdateEventEmitter() {
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static synchronized void register(ReactApplicationContext reactContext) {
|
|
18
|
+
reactContextRef = new WeakReference<ReactApplicationContext>(reactContext);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Nullable
|
|
22
|
+
private static synchronized ReactApplicationContext getReactContext() {
|
|
23
|
+
return reactContextRef.get();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static void sendEvent(String eventName, WritableMap params) {
|
|
27
|
+
ReactApplicationContext reactContext = getReactContext();
|
|
28
|
+
if (reactContext == null || !reactContext.hasActiveCatalystInstance()) {
|
|
29
|
+
if (UpdateContext.DEBUG) {
|
|
30
|
+
Log.d(UpdateContext.TAG, "Skipping event " + eventName + " because React context is unavailable");
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
reactContext
|
|
36
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
37
|
+
.emit(eventName, params);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
package cn.reactnative.modules.update;
|
|
2
|
+
|
|
3
|
+
import android.util.Log;
|
|
4
|
+
import java.io.File;
|
|
5
|
+
import java.io.FileInputStream;
|
|
6
|
+
import java.io.FileOutputStream;
|
|
7
|
+
import java.io.IOException;
|
|
8
|
+
import java.io.InputStream;
|
|
9
|
+
|
|
10
|
+
final class UpdateFileUtils {
|
|
11
|
+
private static final int BUFFER_SIZE = 8192;
|
|
12
|
+
|
|
13
|
+
private UpdateFileUtils() {
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static void ensureDirectory(File directory) throws IOException {
|
|
17
|
+
if (!directory.exists() && !directory.mkdirs() && !directory.exists()) {
|
|
18
|
+
throw new IOException("Failed to create directory: " + directory);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static void ensureParentDirectory(File file) throws IOException {
|
|
23
|
+
File parent = file.getParentFile();
|
|
24
|
+
if (parent != null) {
|
|
25
|
+
ensureDirectory(parent);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static void removeDirectory(File file) throws IOException {
|
|
30
|
+
if (UpdateContext.DEBUG) {
|
|
31
|
+
Log.d(UpdateContext.TAG, "Removing " + file);
|
|
32
|
+
}
|
|
33
|
+
if (file.isDirectory()) {
|
|
34
|
+
File[] files = file.listFiles();
|
|
35
|
+
if (files != null) {
|
|
36
|
+
for (File child : files) {
|
|
37
|
+
String name = child.getName();
|
|
38
|
+
if (name.equals(".") || name.equals("..")) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
removeDirectory(child);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (file.exists() && !file.delete()) {
|
|
46
|
+
throw new IOException("Failed to delete " + file);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static void copyFile(File from, File to) throws IOException {
|
|
51
|
+
ensureParentDirectory(to);
|
|
52
|
+
try (
|
|
53
|
+
InputStream in = new FileInputStream(from);
|
|
54
|
+
FileOutputStream out = new FileOutputStream(to)
|
|
55
|
+
) {
|
|
56
|
+
copy(in, out);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static void copyInputStreamToFile(InputStream input, File destination) throws IOException {
|
|
61
|
+
ensureParentDirectory(destination);
|
|
62
|
+
try (InputStream in = input; FileOutputStream out = new FileOutputStream(destination)) {
|
|
63
|
+
copy(in, out);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private static void copy(InputStream in, FileOutputStream out) throws IOException {
|
|
68
|
+
byte[] buffer = new byte[BUFFER_SIZE];
|
|
69
|
+
int count;
|
|
70
|
+
while ((count = in.read(buffer)) != -1) {
|
|
71
|
+
out.write(buffer, 0, count);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|