@revopush/react-native-code-push 2.5.1 → 2.6.0-rc.4

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/README.md CHANGED
@@ -53,7 +53,8 @@ We try our best to maintain backwards compatibility of our plugin with previous
53
53
  | 0.76, 0.77, 0.78, 0.79 | v1.0+ *(Support both New and Old Architectures)* |
54
54
  | v0.80 | v1.2 |
55
55
  | Expo sdk 52 | v1.3 |
56
- | v0.81 | v1.5 |
56
+ | v0.81, v0.82 | v1.5 |
57
+ | v0.83, expo 55 | v1.6 |
57
58
 
58
59
 
59
60
  We work hard to respond to new RN releases, but they do occasionally break us. We will update this chart with each RN release, so that users can check to see what our "official" support is.
@@ -18,16 +18,32 @@
18
18
 
19
19
  # Invoked via reflection, when setting js bundle.
20
20
  -keepclassmembers class com.facebook.react.ReactInstanceManager {
21
- private final ** mBundleLoader;
21
+ private ** mBundleLoader;
22
+ private ** mAttachedReactRoots;
23
+ private ** mAttachedRootViews;
22
24
  }
23
25
 
24
26
  -keepclassmembers class com.facebook.react.runtime.ReactHostImpl {
25
- private final ** reactHostDelegate;
27
+ private ** mReactHostDelegate;
28
+ private ** reactHostDelegate;
26
29
  }
27
30
 
28
31
  -keep interface com.facebook.react.runtime.ReactHostDelegate { *; }
29
32
 
30
33
  -keep class * implements com.facebook.react.runtime.ReactHostDelegate { *; }
31
34
 
35
+ -keepclassmembers class * implements com.facebook.react.runtime.ReactHostDelegate {
36
+ private ** jsBundleLoader;
37
+ private ** _jsBundleLoader;
38
+ }
39
+
40
+ -keepclassmembers interface com.facebook.react.ReactRoot {
41
+ public ** getRootViewGroup();
42
+ }
43
+
44
+ -keepclassmembers class * implements com.facebook.react.ReactRoot {
45
+ public ** getRootViewGroup();
46
+ }
47
+
32
48
  # Can't find referenced class org.bouncycastle.**
33
49
  -dontwarn com.nimbusds.jose.**
@@ -44,7 +44,7 @@ public class CodePush implements ReactPackage {
44
44
 
45
45
  private boolean mDidUpdate = false;
46
46
 
47
- private String mAssetsBundleFileName;
47
+ private String mAssetsBundleFileName = CodePushConstants.DEFAULT_JS_BUNDLE_NAME;
48
48
 
49
49
  // Helper classes.
50
50
  private CodePushUpdateManager mUpdateManager;
@@ -5,15 +5,15 @@ import android.content.SharedPreferences;
5
5
  import android.os.AsyncTask;
6
6
  import android.os.Handler;
7
7
  import android.os.Looper;
8
- import android.view.View;
9
8
  import android.view.Choreographer;
9
+ import android.view.View;
10
+ import android.view.ViewGroup;
10
11
 
11
12
  import androidx.annotation.OptIn;
12
13
 
13
14
  import com.facebook.react.ReactApplication;
14
15
  import com.facebook.react.ReactHost;
15
16
  import com.facebook.react.ReactInstanceManager;
16
- import com.facebook.react.ReactRootView;
17
17
  import com.facebook.react.bridge.Arguments;
18
18
  import com.facebook.react.bridge.BaseJavaModule;
19
19
  import com.facebook.react.bridge.JSBundleLoader;
@@ -43,7 +43,6 @@ import java.lang.reflect.Method;
43
43
  import java.util.ArrayList;
44
44
  import java.util.Date;
45
45
  import java.util.HashMap;
46
- import java.util.List;
47
46
  import java.util.Map;
48
47
  import java.util.UUID;
49
48
 
@@ -58,6 +57,7 @@ public class CodePushNativeModule extends BaseJavaModule {
58
57
  private SettingsManager mSettingsManager;
59
58
  private CodePushTelemetryManager mTelemetryManager;
60
59
  private CodePushUpdateManager mUpdateManager;
60
+ private boolean mIsExpoApp = false;
61
61
 
62
62
  private boolean _allowed = true;
63
63
  private boolean _restartInProgress = false;
@@ -70,6 +70,8 @@ public class CodePushNativeModule extends BaseJavaModule {
70
70
  mSettingsManager = settingsManager;
71
71
  mTelemetryManager = codePushTelemetryManager;
72
72
  mUpdateManager = codePushUpdateManager;
73
+ mIsExpoApp = detectExpoEnvironment();
74
+
73
75
  // Initialize module state while we have a reference to the current context.
74
76
 
75
77
  mNativeBundleHash = bundleMetadataManager.getNativeBundleHash();
@@ -119,6 +121,31 @@ public class CodePushNativeModule extends BaseJavaModule {
119
121
  });
120
122
  }
121
123
 
124
+ private boolean detectExpoEnvironment() {
125
+ try {
126
+ Class.forName("expo.modules.ReactNativeHostWrapper");
127
+ return true;
128
+ } catch (ClassNotFoundException e) {
129
+ return false;
130
+ }
131
+ }
132
+
133
+ private Field findField(Class<?> clazz, String... fieldNames) throws NoSuchFieldException {
134
+ Class<?> currentClass = clazz;
135
+ while (currentClass != null) {
136
+ for (String fieldName : fieldNames) {
137
+ try {
138
+ return currentClass.getDeclaredField(fieldName);
139
+ } catch (NoSuchFieldException ignored) {
140
+ }
141
+ }
142
+
143
+ currentClass = currentClass.getSuperclass();
144
+ }
145
+
146
+ throw new NoSuchFieldException(fieldNames.length > 0 ? fieldNames[0] : "");
147
+ }
148
+
122
149
  // Use reflection to find and set the appropriate fields on ReactInstanceManager. See #556 for a proposal for a less brittle way
123
150
  // to approach this.
124
151
  private void setJSBundle(ReactInstanceManager instanceManager, String latestJSBundleFile) throws IllegalAccessException {
@@ -130,7 +157,7 @@ public class CodePushNativeModule extends BaseJavaModule {
130
157
  latestJSBundleLoader = JSBundleLoader.createFileLoader(latestJSBundleFile);
131
158
  }
132
159
 
133
- Field bundleLoaderField = instanceManager.getClass().getDeclaredField("mBundleLoader");
160
+ Field bundleLoaderField = findField(instanceManager.getClass(), "mBundleLoader");
134
161
  bundleLoaderField.setAccessible(true);
135
162
  bundleLoaderField.set(instanceManager, latestJSBundleLoader);
136
163
  } catch (Exception e) {
@@ -150,12 +177,10 @@ public class CodePushNativeModule extends BaseJavaModule {
150
177
  latestJSBundleLoader = JSBundleLoader.createFileLoader(latestJSBundleFile);
151
178
  }
152
179
 
153
- Field bundleLoaderField = reactHostDelegate.getClass().getDeclaredField("jsBundleLoader");
180
+ Field bundleLoaderField = findField(reactHostDelegate.getClass(), "jsBundleLoader", "_jsBundleLoader");
154
181
  bundleLoaderField.setAccessible(true);
155
182
  bundleLoaderField.set(reactHostDelegate, latestJSBundleLoader);
156
- } catch (NoSuchFieldException noSuchFileFound) {
157
- // Ignore this error for Expo
158
- } catch (Exception e) {
183
+ } catch (Exception e) {
159
184
  CodePushUtils.log("Unable to set JSBundle of ReactHostDelegate - CodePush may not support this version of React Native");
160
185
  throw new IllegalAccessException("Could not setJSBundle");
161
186
  }
@@ -192,7 +217,7 @@ public class CodePushNativeModule extends BaseJavaModule {
192
217
  String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFileName());
193
218
 
194
219
  try {
195
- if (reactHost instanceof ReactHostImpl) {
220
+ if (!mIsExpoApp && reactHost instanceof ReactHostImpl) {
196
221
  ReactHostDelegate delegate = getReactHostDelegate((ReactHostImpl) reactHost);
197
222
  if (delegate != null) {
198
223
  // #2) Update the locally stored JS bundle file path
@@ -200,7 +225,9 @@ public class CodePushNativeModule extends BaseJavaModule {
200
225
  }
201
226
  }
202
227
  } catch (Exception e) {
203
- CodePushUtils.log("Exception setJSBundle: " + e.getMessage());
228
+ if (!mIsExpoApp) {
229
+ CodePushUtils.log("Exception setJSBundle: " + e.getMessage());
230
+ }
204
231
  }
205
232
 
206
233
  // #3) Get the context creation method
@@ -301,14 +328,38 @@ public class CodePushNativeModule extends BaseJavaModule {
301
328
  // resetReactRootViews allows to call recreateReactContextInBackground without any exceptions
302
329
  // This fix also relates to https://github.com/microsoft/react-native-code-push/issues/878
303
330
  private void resetReactRootViews(ReactInstanceManager instanceManager) throws NoSuchFieldException, IllegalAccessException {
304
- Field mAttachedRootViewsField = instanceManager.getClass().getDeclaredField("mAttachedRootViews");
305
- mAttachedRootViewsField.setAccessible(true);
306
- List<ReactRootView> mAttachedRootViews = (List<ReactRootView>) mAttachedRootViewsField.get(instanceManager);
307
- for (ReactRootView reactRootView : mAttachedRootViews) {
308
- reactRootView.removeAllViews();
309
- reactRootView.setId(View.NO_ID);
331
+ Field attachedRootsField = findField(instanceManager.getClass(), "mAttachedReactRoots", "mAttachedRootViews");
332
+ attachedRootsField.setAccessible(true);
333
+ Object attachedRoots = attachedRootsField.get(instanceManager);
334
+ if (attachedRoots instanceof Iterable) {
335
+ for (Object reactRoot : (Iterable<?>) attachedRoots) {
336
+ resetReactRootView(reactRoot);
337
+ }
338
+ }
339
+ }
340
+
341
+ private void resetReactRootView(Object reactRoot) {
342
+ try {
343
+ View rootView = null;
344
+ if (reactRoot instanceof View) {
345
+ rootView = (View) reactRoot;
346
+ } else {
347
+ Method getRootViewGroupMethod = reactRoot.getClass().getMethod("getRootViewGroup");
348
+ Object rootViewGroup = getRootViewGroupMethod.invoke(reactRoot);
349
+ if (rootViewGroup instanceof View) {
350
+ rootView = (View) rootViewGroup;
351
+ }
352
+ }
353
+
354
+ if (rootView instanceof ViewGroup) {
355
+ ((ViewGroup) rootView).removeAllViews();
356
+ }
357
+ if (rootView != null) {
358
+ rootView.setId(View.NO_ID);
359
+ }
360
+ } catch (Exception e) {
361
+ CodePushUtils.log("Failed to reset root view: " + e.getMessage());
310
362
  }
311
- mAttachedRootViewsField.set(instanceManager, mAttachedRootViews);
312
363
  }
313
364
 
314
365
  private void clearLifecycleEventListener() {
@@ -690,7 +741,12 @@ public class CodePushNativeModule extends BaseJavaModule {
690
741
  if (installMode == CodePushInstallMode.IMMEDIATE.getValue()
691
742
  || durationInBackground >= CodePushNativeModule.this.mMinimumBackgroundDuration) {
692
743
  CodePushUtils.log("Loading bundle on resume");
693
- restartAppInternal(false);
744
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
745
+ @Override
746
+ public void run() {
747
+ restartAppInternal(false);
748
+ }
749
+ });
694
750
  }
695
751
  }
696
752
  }
@@ -856,7 +912,7 @@ public class CodePushNativeModule extends BaseJavaModule {
856
912
  field = clazz.getDeclaredField("mReactHostDelegate");
857
913
  } catch (NoSuchFieldException e) {
858
914
  // RN >= 0.81
859
- field = clazz.getDeclaredField("reactHostDelegate");
915
+ field = findField(reactHostImpl.getClass(), "mReactHostDelegate", "reactHostDelegate");
860
916
  }
861
917
  field.setAccessible(true);
862
918
 
@@ -18,7 +18,7 @@ In order to integrate CodePush into your Android project, please perform the fol
18
18
 
19
19
  2. Update the `MainApplication` file to use CodePush via the following changes:
20
20
 
21
- For React Native 0.76 and above: update the `MainApplication.kt`
21
+ For React Native 0.76 - 0.82: update the `MainApplication.kt`
22
22
 
23
23
  **Important! : PackageList must be instantiated only one in application lifetime.**
24
24
 
@@ -45,6 +45,32 @@ In order to integrate CodePush into your Android project, please perform the fol
45
45
  }
46
46
  ```
47
47
 
48
+ For React Native 0.83 and above: update the `MainApplication.kt`
49
+
50
+ ```kotlin
51
+ ...
52
+ // 1. Import the plugin class.
53
+ import com.microsoft.codepush.react.CodePush
54
+
55
+ class MainApplication : Application(), ReactApplication {
56
+
57
+ override val reactHost: ReactHost by lazy {
58
+ getDefaultReactHost(
59
+ context = applicationContext,
60
+ packageList =
61
+ PackageList(this).packages.apply {
62
+ // Packages that cannot be autolinked yet can be added manually here, for example:
63
+ // add(MyReactNativePackage())
64
+ },
65
+ // 2. Override the jsBundleFilePath in order to let
66
+ // the CodePush runtime determine where to get the JS
67
+ // bundle location from on each app start
68
+ jsBundleFilePath = CodePush.getJSBundleFile(),
69
+ )
70
+ }
71
+ }
72
+ ```
73
+
48
74
 
49
75
  3. Add the Deployment key to `strings.xml`:
50
76
 
@@ -19,7 +19,7 @@
19
19
 
20
20
  #import "CodePush.h"
21
21
 
22
- @interface CodePush () <RCTBridgeModule, RCTFrameUpdateObserver>
22
+ @interface CodePush () <RCTBridgeModule>
23
23
  @end
24
24
 
25
25
  @implementation CodePush {
@@ -32,8 +32,8 @@
32
32
 
33
33
  // Used to coordinate the dispatching of download progress events to JS.
34
34
  long long _latestExpectedContentLength;
35
- long long _latestReceivedConentLength;
36
- BOOL _didUpdateProgress;
35
+ long long _latestReceivedContentLength;
36
+ NSTimeInterval _lastProgressEmitTimestamp;
37
37
 
38
38
  BOOL _allowed;
39
39
  BOOL _restartInProgress;
@@ -263,18 +263,6 @@ static NSString *const LatestRollbackCountKey = @"count";
263
263
  #pragma mark - Private API methods
264
264
 
265
265
  @synthesize methodQueue = _methodQueue;
266
- @synthesize pauseCallback = _pauseCallback;
267
- @synthesize paused = _paused;
268
-
269
- - (void)setPaused:(BOOL)paused
270
- {
271
- if (_paused != paused) {
272
- _paused = paused;
273
- if (_pauseCallback) {
274
- _pauseCallback();
275
- }
276
- }
277
- }
278
266
 
279
267
  /*
280
268
  * This method is used to clear updates that are installed
@@ -340,7 +328,7 @@ static NSString *const LatestRollbackCountKey = @"count";
340
328
  @"totalBytes" : [NSNumber
341
329
  numberWithLongLong:_latestExpectedContentLength],
342
330
  @"receivedBytes" : [NSNumber
343
- numberWithLongLong:_latestReceivedConentLength]
331
+ numberWithLongLong:_latestReceivedContentLength]
344
332
  }];
345
333
  }
346
334
 
@@ -404,7 +392,6 @@ static NSString *const LatestRollbackCountKey = @"count";
404
392
  #ifdef DEBUG
405
393
  [self clearDebugUpdates];
406
394
  #endif
407
- self.paused = YES;
408
395
  NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
409
396
  NSDictionary *pendingUpdate = [preferences objectForKey:PendingUpdateKey];
410
397
  if (pendingUpdate) {
@@ -546,7 +533,7 @@ static NSString *const LatestRollbackCountKey = @"count";
546
533
  // file (since Chrome wouldn't support it). Otherwise, update
547
534
  // the current bundle URL to point at the latest update
548
535
  if ([CodePush isUsingTestConfiguration] || ![super.bridge.bundleURL.scheme hasPrefix:@"http"]) {
549
- [super.bridge setValue:[CodePush bundleURL] forKey:@"bundleURL"];
536
+ RCTReloadCommandSetBundleURL([CodePush bundleURL]);
550
537
  }
551
538
 
552
539
  RCTTriggerReloadCommandListeners(@"react-native-code-push: Restart");
@@ -727,10 +714,7 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
727
714
  }
728
715
 
729
716
  if (notifyProgress) {
730
- // Set up and unpause the frame observer so that it can emit
731
- // progress events every frame if the progress is updated.
732
- _didUpdateProgress = NO;
733
- self.paused = NO;
717
+ _lastProgressEmitTimestamp = 0;
734
718
  }
735
719
 
736
720
  NSString * publicKey = [[CodePushConfig current] publicKey];
@@ -742,17 +726,21 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
742
726
  operationQueue:_methodQueue
743
727
  // The download is progressing forward
744
728
  progressCallback:^(long long expectedContentLength, long long receivedContentLength) {
745
- // Update the download progress so that the frame observer can notify the JS side
729
+ if (!notifyProgress) {
730
+ return;
731
+ }
732
+
746
733
  _latestExpectedContentLength = expectedContentLength;
747
- _latestReceivedConentLength = receivedContentLength;
748
- _didUpdateProgress = YES;
734
+ _latestReceivedContentLength = receivedContentLength;
749
735
 
750
- // If the download is completed, stop observing frame
751
- // updates and synchronously send the last event.
752
736
  if (expectedContentLength == receivedContentLength) {
753
- _didUpdateProgress = NO;
754
- self.paused = YES;
755
737
  [self dispatchDownloadProgressEvent];
738
+ } else {
739
+ NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
740
+ if (timestamp - _lastProgressEmitTimestamp > 0.3) {
741
+ _lastProgressEmitTimestamp = timestamp;
742
+ [self dispatchDownloadProgressEvent];
743
+ }
756
744
  }
757
745
  }
758
746
  // The download completed
@@ -773,9 +761,6 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
773
761
  [self saveFailedUpdate:mutableUpdatePackage];
774
762
  }
775
763
 
776
- // Stop observing frame updates if the download fails.
777
- _didUpdateProgress = NO;
778
- self.paused = YES;
779
764
  reject([NSString stringWithFormat: @"%lu", (long)err.code], err.localizedDescription, err);
780
765
  }];
781
766
  }
@@ -1128,16 +1113,4 @@ RCT_EXPORT_METHOD(saveStatusReportForRetry:(NSDictionary *)statusReport)
1128
1113
  [CodePushTelemetryManager saveStatusReportForRetry:statusReport];
1129
1114
  }
1130
1115
 
1131
- #pragma mark - RCTFrameUpdateObserver Methods
1132
-
1133
- - (void)didUpdateFrame:(RCTFrameUpdate *)update
1134
- {
1135
- if (!_didUpdateProgress) {
1136
- return;
1137
- }
1138
-
1139
- [self dispatchDownloadProgressEvent];
1140
- _didUpdateProgress = NO;
1141
- }
1142
-
1143
1116
  @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revopush/react-native-code-push",
3
- "version": "2.5.1",
3
+ "version": "2.6.0-rc.4",
4
4
  "description": "React Native plugin for the CodePush service",
5
5
  "main": "CodePush.js",
6
6
  "typings": "typings/react-native-code-push.d.ts",
@@ -65,6 +65,9 @@
65
65
  "tslint": "^6.1.3",
66
66
  "typescript": "^4.4.3"
67
67
  },
68
+ "peerDependencies": {
69
+ "react-native": ">=0.83.0"
70
+ },
68
71
  "rnpm": {
69
72
  "android": {
70
73
  "packageInstance": "new CodePush(getResources().getString(R.string.CodePushDeploymentKey), getApplicationContext(), BuildConfig.DEBUG)"