@appzung/react-native-code-push 10.0.0 → 11.0.0-rc1

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.
@@ -1,5 +1,13 @@
1
+ def isNewArchitectureEnabled() {
2
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
3
+ }
4
+
1
5
  apply plugin: "com.android.library"
2
6
 
7
+ if (isNewArchitectureEnabled()) {
8
+ apply plugin: "com.facebook.react"
9
+ }
10
+
3
11
  def getExtOrDefault(name) {
4
12
  return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["CodePush_" + name]
5
13
  }
@@ -33,6 +41,7 @@ android {
33
41
  defaultConfig {
34
42
  minSdkVersion getExtOrIntegerDefault("minSdkVersion")
35
43
  targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
44
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
36
45
 
37
46
  consumerProguardFiles 'proguard-rules.pro'
38
47
  }
@@ -4,6 +4,7 @@ import android.content.Context;
4
4
  import android.content.pm.PackageInfo;
5
5
  import android.content.pm.PackageManager;
6
6
 
7
+ import com.facebook.react.ReactHost;
7
8
  import com.facebook.react.ReactInstanceManager;
8
9
  import com.facebook.react.ReactPackage;
9
10
  import com.facebook.react.bridge.NativeModule;
@@ -18,6 +19,19 @@ import java.util.ArrayList;
18
19
  import java.util.List;
19
20
 
20
21
  public class CodePush implements ReactPackage {
22
+ private static final Object LOCK = new Object();
23
+ private static volatile CodePush mCurrentInstance;
24
+
25
+ public static CodePush getInstance(Context context) {
26
+ if (mCurrentInstance == null) {
27
+ synchronized (LOCK) {
28
+ if (mCurrentInstance == null) {
29
+ mCurrentInstance = new CodePush(context);
30
+ }
31
+ }
32
+ }
33
+ return mCurrentInstance;
34
+ }
21
35
 
22
36
  private static boolean sIsRunningBinaryVersion = false;
23
37
  private static boolean sNeedToReportRollback = false;
@@ -43,13 +57,14 @@ public class CodePush implements ReactPackage {
43
57
  private static String mPublicKey;
44
58
 
45
59
  private static ReactInstanceHolder mReactInstanceHolder;
46
- private static CodePush mCurrentInstance;
60
+
61
+ private static ReactHostHolder mReactHostHolder;
47
62
 
48
63
  public static String getServiceUrl() {
49
64
  return mServerUrl;
50
65
  }
51
66
 
52
- public CodePush(Context context) {
67
+ private CodePush(Context context) {
53
68
  mContext = context.getApplicationContext();
54
69
 
55
70
  String releaseChannelPublicIdFromStrings = getCustomPropertyFromStringsIfExist("ReleaseChannelPublicId");
@@ -363,6 +378,18 @@ public class CodePush implements ReactPackage {
363
378
  return mReactInstanceHolder.getReactInstanceManager();
364
379
  }
365
380
 
381
+ public static void setReactHost(ReactHostHolder reactHostHolder) {
382
+ mReactHostHolder = reactHostHolder;
383
+ }
384
+
385
+ static ReactHost getReactHost() {
386
+ if (mReactHostHolder == null) {
387
+ return null;
388
+ }
389
+
390
+ return mReactHostHolder.getReactHost();
391
+ }
392
+
366
393
  @Override
367
394
  public List<NativeModule> createNativeModules(ReactApplicationContext reactApplicationContext) {
368
395
  CodePushNativeModule codePushModule = new CodePushNativeModule(reactApplicationContext, this, mUpdateManager, mTelemetryManager, mSettingsManager);
@@ -7,23 +7,29 @@ import android.os.Handler;
7
7
  import android.os.Looper;
8
8
  import android.view.View;
9
9
 
10
+ import androidx.annotation.OptIn;
11
+
10
12
  import com.facebook.react.ReactApplication;
13
+ import com.facebook.react.ReactHost;
11
14
  import com.facebook.react.ReactInstanceManager;
12
15
  import com.facebook.react.ReactRootView;
13
16
  import com.facebook.react.bridge.Arguments;
17
+ import com.facebook.react.bridge.BaseJavaModule;
14
18
  import com.facebook.react.bridge.JSBundleLoader;
15
19
  import com.facebook.react.bridge.LifecycleEventListener;
16
20
  import com.facebook.react.bridge.Promise;
17
21
  import com.facebook.react.bridge.ReactApplicationContext;
18
- import com.facebook.react.bridge.ReactContextBaseJavaModule;
19
22
  import com.facebook.react.bridge.ReactMethod;
20
23
  import com.facebook.react.bridge.ReadableMap;
21
24
  import com.facebook.react.bridge.WritableMap;
25
+ import com.facebook.react.common.annotations.UnstableReactNativeAPI;
22
26
  import com.facebook.react.devsupport.interfaces.DevSupportManager;
23
27
  import com.facebook.react.modules.core.ChoreographerCompat;
24
28
  import com.facebook.react.modules.core.DeviceEventManagerModule;
25
29
  import com.facebook.react.modules.core.ReactChoreographer;
26
30
  import com.facebook.react.modules.debug.interfaces.DeveloperSettings;
31
+ import com.facebook.react.runtime.ReactHostDelegate;
32
+ import com.facebook.react.runtime.ReactHostImpl;
27
33
 
28
34
  import org.json.JSONArray;
29
35
  import org.json.JSONException;
@@ -39,7 +45,8 @@ import java.util.List;
39
45
  import java.util.Map;
40
46
  import java.util.UUID;
41
47
 
42
- public class CodePushNativeModule extends ReactContextBaseJavaModule {
48
+ @OptIn(markerClass = UnstableReactNativeAPI.class)
49
+ public class CodePushNativeModule extends BaseJavaModule {
43
50
  private String mBinaryContentsHash = null;
44
51
  private String mClientUniqueId = null;
45
52
  private LifecycleEventListener mLifecycleEventListener = null;
@@ -50,9 +57,9 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
50
57
  private CodePushTelemetryManager mTelemetryManager;
51
58
  private CodePushUpdateManager mUpdateManager;
52
59
 
53
- private boolean _allowed = true;
54
- private boolean _restartInProgress = false;
55
- private ArrayList<Boolean> _restartQueue = new ArrayList<>();
60
+ private boolean _allowed = true;
61
+ private boolean _restartInProgress = false;
62
+ private ArrayList<Boolean> _restartQueue = new ArrayList<>();
56
63
 
57
64
  public CodePushNativeModule(ReactApplicationContext reactContext, CodePush codePush, CodePushUpdateManager codePushUpdateManager, CodePushTelemetryManager codePushTelemetryManager, SettingsManager settingsManager) {
58
65
  super(reactContext);
@@ -95,7 +102,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
95
102
  }
96
103
 
97
104
  private void loadBundleLegacy() {
98
- final Activity currentActivity = getCurrentActivity();
105
+ final Activity currentActivity = getReactApplicationContext().getCurrentActivity();
99
106
  if (currentActivity == null) {
100
107
  // The currentActivity can be null if it is backgrounded / destroyed, so we simply
101
108
  // no-op to prevent any null pointer exceptions.
@@ -126,65 +133,132 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
126
133
  bundleLoaderField.setAccessible(true);
127
134
  bundleLoaderField.set(instanceManager, latestJSBundleLoader);
128
135
  } catch (Exception e) {
129
- CodePushUtils.log("Unable to set JSBundle - CodePush may not support this version of React Native");
136
+ CodePushUtils.log("Unable to set JSBundle of ReactInstanceManager - CodePush may not support this version of React Native");
130
137
  throw new IllegalAccessException("Could not setJSBundle");
131
138
  }
132
139
  }
133
140
 
134
- private void loadBundle() {
135
- clearLifecycleEventListener();
141
+ // Use reflection to find and set the appropriate fields on ReactHostDelegate. See #556 for a proposal for a less brittle way
142
+ // to approach this.
143
+ private void setJSBundle(ReactHostDelegate reactHostDelegate, String latestJSBundleFile) throws IllegalAccessException {
136
144
  try {
137
- DevSupportManager devSupportManager = null;
138
- ReactInstanceManager reactInstanceManager = resolveInstanceManager();
139
- if (reactInstanceManager != null) {
140
- devSupportManager = reactInstanceManager.getDevSupportManager();
145
+ JSBundleLoader latestJSBundleLoader;
146
+ if (latestJSBundleFile.toLowerCase().startsWith("assets://")) {
147
+ latestJSBundleLoader = JSBundleLoader.createAssetLoader(getReactApplicationContext(), latestJSBundleFile, false);
148
+ } else {
149
+ latestJSBundleLoader = JSBundleLoader.createFileLoader(latestJSBundleFile);
141
150
  }
142
- boolean isLiveReloadEnabled = isLiveReloadEnabled(devSupportManager);
143
151
 
144
- mCodePush.clearDebugCacheIfNeeded(isLiveReloadEnabled);
145
- } catch(Exception e) {
146
- // If we got error in out reflection we should clear debug cache anyway.
147
- mCodePush.clearDebugCacheIfNeeded(false);
152
+ Field bundleLoaderField = reactHostDelegate.getClass().getDeclaredField("jsBundleLoader");
153
+ bundleLoaderField.setAccessible(true);
154
+ bundleLoaderField.set(reactHostDelegate, latestJSBundleLoader);
155
+ } catch (Exception e) {
156
+ CodePushUtils.log("Unable to set JSBundle of ReactHostDelegate - CodePush may not support this version of React Native");
157
+ throw new IllegalAccessException("Could not setJSBundle");
148
158
  }
159
+ }
149
160
 
150
- try {
151
- // #1) Get the ReactInstanceManager instance, which is what includes the
152
- // logic to reload the current React context.
153
- final ReactInstanceManager instanceManager = resolveInstanceManager();
154
- if (instanceManager == null) {
155
- return;
161
+ private void loadBundle() {
162
+ clearLifecycleEventListener();
163
+
164
+ if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
165
+ try {
166
+ DevSupportManager devSupportManager = null;
167
+ ReactHost reactHost = resolveReactHost();
168
+ if (reactHost != null) {
169
+ devSupportManager = reactHost.getDevSupportManager();
170
+ }
171
+ boolean isLiveReloadEnabled = isLiveReloadEnabled(devSupportManager);
172
+
173
+ mCodePush.clearDebugCacheIfNeeded(isLiveReloadEnabled);
174
+ } catch (Exception e) {
175
+ // If we got error in out reflection we should clear debug cache anyway.
176
+ mCodePush.clearDebugCacheIfNeeded(false);
156
177
  }
157
178
 
158
- String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFileName());
159
-
160
- // #2) Update the locally stored JS bundle file path
161
- setJSBundle(instanceManager, latestJSBundleFile);
162
-
163
- // #3) Get the context creation method and fire it on the UI thread (which RN enforces)
164
- new Handler(Looper.getMainLooper()).post(new Runnable() {
165
- @Override
166
- public void run() {
167
- try {
168
- // We don't need to resetReactRootViews anymore
169
- // due the issue https://github.com/facebook/react-native/issues/14533
170
- // has been fixed in RN 0.46.0
171
- //resetReactRootViews(instanceManager);
172
-
173
- instanceManager.recreateReactContextInBackground();
174
- mCodePush.initializeUpdateAfterRestart();
175
- } catch (Exception e) {
176
- // The recreation method threw an unknown exception
177
- // so just simply fallback to restarting the Activity (if it exists)
178
- loadBundleLegacy();
179
- }
179
+ try {
180
+ // #1) Get the ReactHost instance, which is what includes the
181
+ // logic to reload the current React context.
182
+ final ReactHost reactHost = resolveReactHost();
183
+ if (reactHost == null) {
184
+ return;
180
185
  }
181
- });
182
186
 
183
- } catch (Exception e) {
184
- // Our reflection logic failed somewhere
185
- // so fall back to restarting the Activity (if it exists)
186
- CodePushUtils.log("Failed to load the bundle, falling back to restarting the Activity (if it exists). " + e.getMessage());
187
- loadBundleLegacy();
187
+ String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFileName());
188
+
189
+ // #2) Update the locally stored JS bundle file path
190
+ setJSBundle(getReactHostDelegate((ReactHostImpl) reactHost), latestJSBundleFile);
191
+
192
+ // #3) Get the context creation method
193
+ try {
194
+ reactHost.reload("CodePush triggers reload");
195
+ mCodePush.initializeUpdateAfterRestart();
196
+ } catch (Exception e) {
197
+ // The recreation method threw an unknown exception
198
+ // so just simply fallback to restarting the Activity (if it exists)
199
+ loadBundleLegacy();
200
+ }
201
+
202
+ } catch (Exception e) {
203
+ // Our reflection logic failed somewhere
204
+ // so fall back to restarting the Activity (if it exists)
205
+ CodePushUtils.log("Failed to load the bundle, falling back to restarting the Activity (if it exists). " + e.getMessage());
206
+ loadBundleLegacy();
207
+ }
208
+ } else {
209
+ try {
210
+ DevSupportManager devSupportManager = null;
211
+ ReactInstanceManager reactInstanceManager = resolveInstanceManager();
212
+ if (reactInstanceManager != null) {
213
+ devSupportManager = reactInstanceManager.getDevSupportManager();
214
+ }
215
+ boolean isLiveReloadEnabled = isLiveReloadEnabled(devSupportManager);
216
+
217
+ mCodePush.clearDebugCacheIfNeeded(isLiveReloadEnabled);
218
+ } catch (Exception e) {
219
+ // If we got error in out reflection we should clear debug cache anyway.
220
+ mCodePush.clearDebugCacheIfNeeded(false);
221
+ }
222
+
223
+ try {
224
+ // #1) Get the ReactInstanceManager instance, which is what includes the
225
+ // logic to reload the current React context.
226
+ final ReactInstanceManager instanceManager = resolveInstanceManager();
227
+ if (instanceManager == null) {
228
+ return;
229
+ }
230
+
231
+ String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFileName());
232
+
233
+ // #2) Update the locally stored JS bundle file path
234
+ setJSBundle(instanceManager, latestJSBundleFile);
235
+
236
+ // #3) Get the context creation method and fire it on the UI thread (which RN enforces)
237
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
238
+ @Override
239
+ public void run() {
240
+ try {
241
+ // We don't need to resetReactRootViews anymore
242
+ // due the issue https://github.com/facebook/react-native/issues/14533
243
+ // has been fixed in RN 0.46.0
244
+ //resetReactRootViews(instanceManager);
245
+
246
+ instanceManager.recreateReactContextInBackground();
247
+ mCodePush.initializeUpdateAfterRestart();
248
+ } catch (Exception e) {
249
+ // The recreation method threw an unknown exception
250
+ // so just simply fallback to restarting the Activity (if it exists)
251
+ loadBundleLegacy();
252
+ }
253
+ }
254
+ });
255
+
256
+ } catch (Exception e) {
257
+ // Our reflection logic failed somewhere
258
+ // so fall back to restarting the Activity (if it exists)
259
+ CodePushUtils.log("Failed to load the bundle, falling back to restarting the Activity (if it exists). " + e.getMessage());
260
+ loadBundleLegacy();
261
+ }
188
262
  }
189
263
  }
190
264
 
@@ -214,7 +288,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
214
288
  private void resetReactRootViews(ReactInstanceManager instanceManager) throws NoSuchFieldException, IllegalAccessException {
215
289
  Field mAttachedRootViewsField = instanceManager.getClass().getDeclaredField("mAttachedRootViews");
216
290
  mAttachedRootViewsField.setAccessible(true);
217
- List<ReactRootView> mAttachedRootViews = (List<ReactRootView>)mAttachedRootViewsField.get(instanceManager);
291
+ List<ReactRootView> mAttachedRootViews = (List<ReactRootView>) mAttachedRootViewsField.get(instanceManager);
218
292
  for (ReactRootView reactRootView : mAttachedRootViews) {
219
293
  reactRootView.removeAllViews();
220
294
  reactRootView.setId(View.NO_ID);
@@ -237,7 +311,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
237
311
  return instanceManager;
238
312
  }
239
313
 
240
- final Activity currentActivity = getCurrentActivity();
314
+ final Activity currentActivity = getReactApplicationContext().getCurrentActivity();
241
315
  if (currentActivity == null) {
242
316
  return null;
243
317
  }
@@ -248,6 +322,21 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
248
322
  return instanceManager;
249
323
  }
250
324
 
325
+ private ReactHost resolveReactHost() throws NoSuchFieldException, IllegalAccessException {
326
+ ReactHost reactHost = CodePush.getReactHost();
327
+ if (reactHost != null) {
328
+ return reactHost;
329
+ }
330
+
331
+ final Activity currentActivity = getReactApplicationContext().getCurrentActivity();
332
+ if (currentActivity == null) {
333
+ return null;
334
+ }
335
+
336
+ ReactApplication reactApplication = (ReactApplication) currentActivity.getApplication();
337
+ return reactApplication.getReactHost();
338
+ }
339
+
251
340
  private void restartAppInternal(boolean onlyIfUpdateIsPending) {
252
341
  if (this._restartInProgress) {
253
342
  CodePushUtils.log("Restart request queued until the current restart is completed");
@@ -310,7 +399,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
310
399
  try {
311
400
  restartAppInternal(onlyIfUpdateIsPending);
312
401
  promise.resolve(null);
313
- } catch(CodePushUnknownException e) {
402
+ } catch (CodePushUnknownException e) {
314
403
  CodePushUtils.log(e);
315
404
  promise.reject(e);
316
405
  }
@@ -391,7 +480,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
391
480
  @ReactMethod
392
481
  public void getConfiguration(Promise promise) {
393
482
  try {
394
- WritableMap configMap = Arguments.createMap();
483
+ WritableMap configMap = Arguments.createMap();
395
484
  configMap.putString("appVersion", mCodePush.getAppVersion());
396
485
  configMap.putString("clientUniqueId", mClientUniqueId);
397
486
  configMap.putString("releaseChannelPublicId", mCodePush.getReleaseChannelPublicId());
@@ -403,7 +492,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
403
492
  }
404
493
 
405
494
  promise.resolve(configMap);
406
- } catch(CodePushUnknownException e) {
495
+ } catch (CodePushUnknownException e) {
407
496
  CodePushUtils.log(e);
408
497
  promise.reject(e);
409
498
  }
@@ -465,7 +554,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
465
554
  CodePushUtils.log(e.getMessage());
466
555
  clearUpdates();
467
556
  promise.resolve(null);
468
- } catch(CodePushUnknownException e) {
557
+ } catch (CodePushUnknownException e) {
469
558
  CodePushUtils.log(e);
470
559
  promise.reject(e);
471
560
  }
@@ -523,7 +612,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
523
612
  }
524
613
 
525
614
  promise.resolve("");
526
- } catch(CodePushUnknownException e) {
615
+ } catch (CodePushUnknownException e) {
527
616
  CodePushUtils.log(e);
528
617
  promise.reject(e);
529
618
  }
@@ -550,11 +639,11 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
550
639
  }
551
640
 
552
641
  if (installMode == CodePushInstallMode.ON_NEXT_RESUME.getValue() ||
553
- // We also add the resume listener if the installMode is IMMEDIATE, because
554
- // if the current activity is backgrounded, we want to reload the bundle when
555
- // it comes back into the foreground.
556
- installMode == CodePushInstallMode.IMMEDIATE.getValue() ||
557
- installMode == CodePushInstallMode.ON_NEXT_SUSPEND.getValue()) {
642
+ // We also add the resume listener if the installMode is IMMEDIATE, because
643
+ // if the current activity is backgrounded, we want to reload the bundle when
644
+ // it comes back into the foreground.
645
+ installMode == CodePushInstallMode.IMMEDIATE.getValue() ||
646
+ installMode == CodePushInstallMode.ON_NEXT_SUSPEND.getValue()) {
558
647
 
559
648
  // Store the minimum duration on the native module as an instance
560
649
  // variable instead of relying on a closure below, so that any
@@ -610,7 +699,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
610
699
  }
611
700
 
612
701
  promise.resolve("");
613
- } catch(CodePushUnknownException e) {
702
+ } catch (CodePushUnknownException e) {
614
703
  CodePushUtils.log(e);
615
704
  promise.reject(e);
616
705
  }
@@ -666,7 +755,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
666
755
  && packageHash.length() > 0
667
756
  && packageHash.equals(mUpdateManager.getCurrentPackageHash());
668
757
  promise.resolve(isFirstRun);
669
- } catch(CodePushUnknownException e) {
758
+ } catch (CodePushUnknownException e) {
670
759
  CodePushUtils.log(e);
671
760
  promise.reject(e);
672
761
  }
@@ -677,7 +766,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
677
766
  try {
678
767
  mSettingsManager.removePendingUpdate();
679
768
  promise.resolve("");
680
- } catch(CodePushUnknownException e) {
769
+ } catch (CodePushUnknownException e) {
681
770
  CodePushUtils.log(e);
682
771
  promise.reject(e);
683
772
  }
@@ -687,7 +776,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
687
776
  public void recordStatusReported(ReadableMap statusReport) {
688
777
  try {
689
778
  mTelemetryManager.recordStatusReported(statusReport);
690
- } catch(CodePushUnknownException e) {
779
+ } catch (CodePushUnknownException e) {
691
780
  CodePushUtils.log(e);
692
781
  }
693
782
  }
@@ -696,7 +785,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
696
785
  public void saveStatusReportForRetry(ReadableMap statusReport) {
697
786
  try {
698
787
  mTelemetryManager.saveStatusReportForRetry(statusReport);
699
- } catch(CodePushUnknownException e) {
788
+ } catch (CodePushUnknownException e) {
700
789
  CodePushUtils.log(e);
701
790
  }
702
791
  }
@@ -713,7 +802,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
713
802
  throw new CodePushUnknownException("Unable to replace current bundle", e);
714
803
  }
715
804
  }
716
- } catch(CodePushUnknownException | CodePushMalformedDataException e) {
805
+ } catch (CodePushUnknownException | CodePushMalformedDataException e) {
717
806
  CodePushUtils.log(e);
718
807
  }
719
808
  }
@@ -740,4 +829,18 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
740
829
  public void removeListeners(Integer count) {
741
830
  // Remove upstream listeners, stop unnecessary background tasks
742
831
  }
832
+
833
+ public ReactHostDelegate getReactHostDelegate(ReactHostImpl reactHostImpl) {
834
+ try {
835
+ Class<?> clazz = reactHostImpl.getClass();
836
+ Field field = clazz.getDeclaredField("mReactHostDelegate");
837
+ field.setAccessible(true);
838
+
839
+ // Get the value of the field for the provided instance
840
+ return (ReactHostDelegate) field.get(reactHostImpl);
841
+ } catch (NoSuchFieldException | IllegalAccessException e) {
842
+ e.printStackTrace();
843
+ return null;
844
+ }
845
+ }
743
846
  }
@@ -0,0 +1,11 @@
1
+ package com.appzung.codepush.react;
2
+
3
+ import com.facebook.react.ReactHost;
4
+ import com.facebook.react.runtime.ReactHostDelegate;
5
+
6
+ /**
7
+ * Provides access to a {@link ReactHostDelegate}
8
+ */
9
+ public interface ReactHostHolder {
10
+ ReactHost getReactHost();
11
+ }
@@ -1,3 +1,4 @@
1
+ #if __has_include(<React/RCTAssert.h>)
1
2
  #import <React/RCTAssert.h>
2
3
  #import <React/RCTBridgeModule.h>
3
4
  #import <React/RCTConvert.h>
@@ -5,10 +6,18 @@
5
6
  #import <React/RCTRootView.h>
6
7
  #import <React/RCTUtils.h>
7
8
  #import <React/RCTReloadCommand.h>
9
+ #else // back compatibility for RN version < 0.40
10
+ #import "RCTAssert.h"
11
+ #import "RCTBridgeModule.h"
12
+ #import "RCTConvert.h"
13
+ #import "RCTEventDispatcher.h"
14
+ #import "RCTRootView.h"
15
+ #import "RCTUtils.h"
16
+ #endif
8
17
 
9
18
  #import "CodePush.h"
10
19
 
11
- @interface CodePush () <RCTBridgeModule>
20
+ @interface CodePush () <RCTBridgeModule, RCTFrameUpdateObserver>
12
21
  @end
13
22
 
14
23
  @implementation CodePush {
@@ -21,8 +30,8 @@
21
30
 
22
31
  // Used to coordinate the dispatching of download progress events to JS.
23
32
  long long _latestExpectedContentLength;
24
- long long _latestReceivedContentLength;
25
- NSTimeInterval _lastProgressEmitTimestamp;
33
+ long long _latestReceivedConentLength;
34
+ BOOL _didUpdateProgress;
26
35
 
27
36
  BOOL _allowed;
28
37
  BOOL _restartInProgress;
@@ -246,6 +255,19 @@ static NSString *const LatestRollbackCountKey = @"count";
246
255
  #pragma mark - Private API methods
247
256
 
248
257
  @synthesize methodQueue = _methodQueue;
258
+ @synthesize pauseCallback = _pauseCallback;
259
+ @synthesize paused = _paused;
260
+
261
+ - (void)setPaused:(BOOL)paused
262
+ {
263
+ if (_paused != paused) {
264
+ _paused = paused;
265
+ if (_pauseCallback) {
266
+ _pauseCallback();
267
+ }
268
+ }
269
+ }
270
+
249
271
  /*
250
272
  * This method is used to clear updates that are installed
251
273
  * under a different app version and hence don't apply anymore,
@@ -310,7 +332,7 @@ static NSString *const LatestRollbackCountKey = @"count";
310
332
  @"totalBytes" : [NSNumber
311
333
  numberWithLongLong:_latestExpectedContentLength],
312
334
  @"receivedBytes" : [NSNumber
313
- numberWithLongLong:_latestReceivedContentLength]
335
+ numberWithLongLong:_latestReceivedConentLength]
314
336
  }];
315
337
  }
316
338
 
@@ -374,6 +396,7 @@ static NSString *const LatestRollbackCountKey = @"count";
374
396
  #ifdef DEBUG
375
397
  [self clearDebugUpdates];
376
398
  #endif
399
+ self.paused = YES;
377
400
  NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
378
401
  NSDictionary *pendingUpdate = [preferences objectForKey:PendingUpdateKey];
379
402
  if (pendingUpdate) {
@@ -695,6 +718,13 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
695
718
  forKey:BinaryBundleDateKey];
696
719
  }
697
720
 
721
+ if (notifyProgress) {
722
+ // Set up and unpause the frame observer so that it can emit
723
+ // progress events every frame if the progress is updated.
724
+ _didUpdateProgress = NO;
725
+ self.paused = NO;
726
+ }
727
+
698
728
  NSString * publicKey = [[CodePushConfig current] publicKey];
699
729
 
700
730
  [CodePushPackage
@@ -704,18 +734,17 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
704
734
  operationQueue:_methodQueue
705
735
  // The download is progressing forward
706
736
  progressCallback:^(long long expectedContentLength, long long receivedContentLength) {
707
- // Update the download progress
708
- self->_latestExpectedContentLength = expectedContentLength;
709
- self->_latestReceivedContentLength = receivedContentLength;
737
+ // Update the download progress so that the frame observer can notify the JS side
738
+ _latestExpectedContentLength = expectedContentLength;
739
+ _latestReceivedConentLength = receivedContentLength;
740
+ _didUpdateProgress = YES;
710
741
 
742
+ // If the download is completed, stop observing frame
743
+ // updates and synchronously send the last event.
711
744
  if (expectedContentLength == receivedContentLength) {
745
+ _didUpdateProgress = NO;
746
+ self.paused = YES;
712
747
  [self dispatchDownloadProgressEvent];
713
- } else if (notifyProgress) {
714
- NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
715
- if (timestamp - self->_lastProgressEmitTimestamp > 30.0 / 1000.0) {
716
- self->_lastProgressEmitTimestamp = timestamp;
717
- [self dispatchDownloadProgressEvent];
718
- }
719
748
  }
720
749
  }
721
750
  // The download completed
@@ -734,6 +763,9 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
734
763
  [self saveFailedUpdate:mutableUpdatePackage];
735
764
  }
736
765
 
766
+ // Stop observing frame updates if the download fails.
767
+ _didUpdateProgress = NO;
768
+ self.paused = YES;
737
769
  reject([NSString stringWithFormat: @"%lu", (long)err.code], err.localizedDescription, err);
738
770
  }];
739
771
  }
@@ -1075,4 +1107,16 @@ RCT_EXPORT_METHOD(saveStatusReportForRetry:(NSDictionary *)statusReport)
1075
1107
  [CodePushTelemetryManager saveStatusReportForRetry:statusReport];
1076
1108
  }
1077
1109
 
1110
+ #pragma mark - RCTFrameUpdateObserver Methods
1111
+
1112
+ - (void)didUpdateFrame:(RCTFrameUpdate *)update
1113
+ {
1114
+ if (!_didUpdateProgress) {
1115
+ return;
1116
+ }
1117
+
1118
+ [self dispatchDownloadProgressEvent];
1119
+ _didUpdateProgress = NO;
1120
+ }
1121
+
1078
1122
  @end
@@ -5,5 +5,5 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.version = void 0;
7
7
  // Generated by genversion.
8
- const version = exports.version = '10.0.0';
8
+ const version = exports.version = '11.0.0-rc1';
9
9
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["version","exports"],"sourceRoot":"../../../src","sources":["internals/version.ts"],"mappings":";;;;;;AAAA;AACO,MAAMA,OAAO,GAAAC,OAAA,CAAAD,OAAA,GAAG,QAAQ","ignoreList":[]}
1
+ {"version":3,"names":["version","exports"],"sourceRoot":"../../../src","sources":["internals/version.ts"],"mappings":";;;;;;AAAA;AACO,MAAMA,OAAO,GAAAC,OAAA,CAAAD,OAAA,GAAG,YAAY","ignoreList":[]}
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
 
3
3
  // Generated by genversion.
4
- export const version = '10.0.0';
4
+ export const version = '11.0.0-rc1';
5
5
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["version"],"sourceRoot":"../../../src","sources":["internals/version.ts"],"mappings":";;AAAA;AACA,OAAO,MAAMA,OAAO,GAAG,QAAQ","ignoreList":[]}
1
+ {"version":3,"names":["version"],"sourceRoot":"../../../src","sources":["internals/version.ts"],"mappings":";;AAAA;AACA,OAAO,MAAMA,OAAO,GAAG,YAAY","ignoreList":[]}
@@ -1,2 +1,2 @@
1
- export declare const version = "10.0.0";
1
+ export declare const version = "11.0.0-rc1";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../../../src/internals/version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,OAAO,WAAW,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../../../src/internals/version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,OAAO,eAAe,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare const version = "10.0.0";
1
+ export declare const version = "11.0.0-rc1";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../../../src/internals/version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,OAAO,WAAW,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../../../src/internals/version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,OAAO,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appzung/react-native-code-push",
3
- "version": "10.0.0",
3
+ "version": "11.0.0-rc1",
4
4
  "description": "React Native plugin for the CodePush service",
5
5
  "author": "Louis Lagrange <lagrange.louis@gmail.com> (https://github.com/Minishlink)",
6
6
  "license": "MIT",
@@ -2,7 +2,7 @@ module.exports = {
2
2
  dependency: {
3
3
  platforms: {
4
4
  android: {
5
- packageInstance: 'new CodePush(getApplicationContext())',
5
+ packageInstance: 'CodePush.getInstance(getApplicationContext())',
6
6
  sourceDir: './android/app',
7
7
  },
8
8
  },
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '10.0.0';
2
+ export const version = '11.0.0-rc1';