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.
Files changed (44) hide show
  1. package/android/build.gradle +0 -6
  2. package/android/jni/Application.mk +1 -1
  3. package/android/lib/arm64-v8a/librnupdate.so +0 -0
  4. package/android/lib/armeabi-v7a/librnupdate.so +0 -0
  5. package/android/lib/x86/librnupdate.so +0 -0
  6. package/android/lib/x86_64/librnupdate.so +0 -0
  7. package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +314 -0
  8. package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +253 -586
  9. package/android/src/main/java/cn/reactnative/modules/update/NativeUpdateCore.java +1 -9
  10. package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +220 -0
  11. package/android/src/main/java/cn/reactnative/modules/update/SafeZipFile.java +9 -3
  12. package/android/src/main/java/cn/reactnative/modules/update/UiThreadRunner.java +36 -0
  13. package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +36 -26
  14. package/android/src/main/java/cn/reactnative/modules/update/UpdateEventEmitter.java +39 -0
  15. package/android/src/main/java/cn/reactnative/modules/update/UpdateFileUtils.java +74 -0
  16. package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +143 -260
  17. package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleSupport.java +63 -0
  18. package/android/src/main/java/cn/reactnative/modules/update/UpdatePackage.java +1 -5
  19. package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +26 -73
  20. package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +28 -242
  21. package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +89 -135
  22. package/harmony/pushy/src/main/cpp/PushyTurboModule.h +5 -5
  23. package/harmony/pushy/src/main/ets/DownloadTaskParams.ts +7 -7
  24. package/harmony/pushy/src/main/ets/PushyPackage.ets +3 -9
  25. package/harmony/pushy/src/main/ets/PushyPackageCompat.ts +3 -9
  26. package/harmony/pushy/src/main/ets/PushyPackageFactory.ts +14 -0
  27. package/harmony/pushy/src/main/ets/PushyTurboModule.ts +124 -24
  28. package/harmony/pushy/src/main/ets/UpdateContext.ts +92 -70
  29. package/harmony/pushy.har +0 -0
  30. package/ios/Expo/ExpoPushyReactDelegateHandler.swift +6 -26
  31. package/ios/RCTPushy/RCTPushy.mm +315 -259
  32. package/ios/RCTPushy/RCTPushyDownloader.mm +52 -29
  33. package/package.json +2 -2
  34. package/react-native-update.podspec +3 -3
  35. package/harmony/pushy/src/main/ets/UpdateModuleImpl.ts +0 -123
  36. package/ios/ImportReact.h +0 -2
  37. package/ios/RCTPushy/HDiffPatch/HDiffPatch.h +0 -16
  38. package/ios/RCTPushy/HDiffPatch/HDiffPatch.mm +0 -35
  39. package/ios/RCTPushy/RCTPushyManager.h +0 -27
  40. package/ios/RCTPushy/RCTPushyManager.mm +0 -181
  41. package/ios/RCTPushy.xcodeproj/project.pbxproj +0 -479
  42. package/package/harmony/pushy.har +0 -0
  43. package/react-native-update-10.39.0-beta.3.tgz +0 -0
  44. 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. Ensure the host app packages libc++_shared.so "
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
- Log.d("react-native-update", "Unzipping " + name);
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.util.HashMap;
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
- private Context context;
23
- private File rootDir;
24
- private Executor executor;
20
+ static final String TAG = "react-native-update";
21
+ static final boolean DEBUG = BuildConfig.DEBUG;
25
22
 
26
- public static boolean DEBUG = true;
27
- private static ReactInstanceManager mReactInstanceManager;
28
- private static boolean isUsingBundleUrl = false;
29
- private static boolean ignoreRollback = false;
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.mkdir();
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
- new DownloadTask(context).executeOnExecutor(this.executor, params);
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
- new DownloadTask(context).executeOnExecutor(this.executor, params);
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
- new DownloadTask(context).executeOnExecutor(this.executor, params);
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
- new DownloadTask(context).executeOnExecutor(this.executor, params);
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("react-native-update", "Failed to persist update state for " + reason);
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
- mReactInstanceManager = instanceManager;
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 mReactInstanceManager;
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("getBundleUrl", "Bundle version " + currentVersion + " not found.");
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
- new DownloadTask(context).executeOnExecutor(this.executor, params);
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
+ }