@appzung/react-native-code-push 10.2.4 → 11.0.0-rc10

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 (38) hide show
  1. package/CodePush.podspec +3 -5
  2. package/README.md +22 -20
  3. package/android/app/build.gradle +9 -0
  4. package/android/app/proguard-rules.pro +5 -0
  5. package/android/app/src/main/java/com/appzung/codepush/react/CodePush.java +30 -2
  6. package/android/app/src/main/java/com/appzung/codepush/react/CodePushNativeModule.java +179 -70
  7. package/android/app/src/main/java/com/appzung/codepush/react/ExpoUtils.java +22 -0
  8. package/android/app/src/main/java/com/appzung/codepush/react/ReactHostHolder.java +11 -0
  9. package/ios/CodePush/CodePush.h +8 -0
  10. package/ios/CodePush/{CodePush.m → CodePush.mm} +104 -139
  11. package/ios/CodePush.xcodeproj/project.pbxproj +6 -6
  12. package/lib/commonjs/internals/CodePushEventEmitter.js +10 -0
  13. package/lib/commonjs/internals/CodePushEventEmitter.js.map +1 -0
  14. package/lib/commonjs/internals/RemotePackageImplementation.js +2 -3
  15. package/lib/commonjs/internals/RemotePackageImplementation.js.map +1 -1
  16. package/lib/commonjs/internals/version.js +1 -1
  17. package/lib/commonjs/internals/version.js.map +1 -1
  18. package/lib/module/internals/CodePushEventEmitter.js +6 -0
  19. package/lib/module/internals/CodePushEventEmitter.js.map +1 -0
  20. package/lib/module/internals/RemotePackageImplementation.js +2 -3
  21. package/lib/module/internals/RemotePackageImplementation.js.map +1 -1
  22. package/lib/module/internals/version.js +1 -1
  23. package/lib/module/internals/version.js.map +1 -1
  24. package/lib/typescript/commonjs/src/internals/CodePushEventEmitter.d.ts +3 -0
  25. package/lib/typescript/commonjs/src/internals/CodePushEventEmitter.d.ts.map +1 -0
  26. package/lib/typescript/commonjs/src/internals/RemotePackageImplementation.d.ts.map +1 -1
  27. package/lib/typescript/commonjs/src/internals/version.d.ts +1 -1
  28. package/lib/typescript/commonjs/src/internals/version.d.ts.map +1 -1
  29. package/lib/typescript/module/src/internals/CodePushEventEmitter.d.ts +3 -0
  30. package/lib/typescript/module/src/internals/CodePushEventEmitter.d.ts.map +1 -0
  31. package/lib/typescript/module/src/internals/RemotePackageImplementation.d.ts.map +1 -1
  32. package/lib/typescript/module/src/internals/version.d.ts +1 -1
  33. package/lib/typescript/module/src/internals/version.d.ts.map +1 -1
  34. package/package.json +1 -1
  35. package/react-native.config.js +1 -1
  36. package/src/internals/CodePushEventEmitter.ts +4 -0
  37. package/src/internals/RemotePackageImplementation.ts +2 -3
  38. package/src/internals/version.ts +1 -1
package/CodePush.podspec CHANGED
@@ -14,14 +14,12 @@ Pod::Spec.new do |s|
14
14
  s.tvos.deployment_target = '15.5'
15
15
  s.preserve_paths = '*.js'
16
16
  s.library = 'z'
17
- s.source_files = 'ios/CodePush/*.{h,m}'
17
+ s.source_files = 'ios/CodePush/*.{h,m,mm}'
18
18
  s.public_header_files = ['ios/CodePush/CodePush.h']
19
19
  s.pod_target_xcconfig = { "DEFINES_MODULE" => "YES" }
20
20
 
21
- # Note: Even though there are copy/pasted versions of some of these dependencies in the repo,
22
- # we explicitly let CocoaPods pull in the versions below so all dependencies are resolved and
23
- # linked properly at a parent workspace level.
24
- s.dependency 'React-Core'
21
+ install_modules_dependencies(s)
22
+
25
23
  s.dependency 'SSZipArchive', '~> 2.5.5'
26
24
  s.dependency 'JWT', '~> 3.0.0-beta.12'
27
25
  s.dependency 'Base64', '~> 1.1'
package/README.md CHANGED
@@ -55,13 +55,13 @@ Otherwise:
55
55
  npm install --save @appzung/react-native-code-push
56
56
  ```
57
57
 
58
- _NOTE: For Expo apps a plugin will be made available soon. In the meantime, you may eject._
59
-
60
58
  Then continue with installing the native module:
61
59
 
62
- - [iOS Setup](docs/setup-ios.md)
63
- - [Android Setup](docs/setup-android.md)
64
- - [Windows Setup](docs/setup-windows.md)
60
+ - [iOS setup](docs/setup-ios.md)
61
+ - [Android setup](docs/setup-android.md)
62
+ - [Windows setup](docs/setup-windows.md)
63
+
64
+ Or if your app is managed by the Expo framework, install and configure [@appzung/expo-config-code-push](https://github.com/AppZung/expo-config-code-push).
65
65
 
66
66
  ## Migrating to AppZung CodePush
67
67
 
@@ -109,8 +109,9 @@ We try our best to maintain backwards compatibility of our plugin with previous
109
109
  | v0.60-v0.61 | 4.1+ (TLS 1.2+) | 7 | ✅ | ❌ | v6.3.1 |
110
110
  | v0.62-v0.64 | 4.1+ (TLS 1.2+) | 7 | ✅ | ❌ | v6.4.2 |
111
111
  | v0.65-v0.70 | 4.1+ (TLS 1.2+) | 9 | ✅ | ❌ | v7.1.1 |
112
- | v0.71+ | 4.1+ (TLS 1.2+) | 9 | ✅ | ❌ | v8.3.2 |
113
- | v0.71+ | 4.1+ (TLS 1.2+) | 15.5 | ✅ | ❌ | v9.0.2 or v10+ |
112
+ | v0.71-v0.79 | 4.1+ (TLS 1.2+) | 9 | ✅ | ❌ | v8.3.2 |
113
+ | v0.71-v0.79 | 4.1+ (TLS 1.2+) | 15.5 | ✅ | ❌ | v9.0.2 |
114
+ | v0.71+ | 4.1+ (TLS 1.2+) | 15.5 | ✅ | ❌ | v10+ |
114
115
  | v0.74+ | 4.1+ (TLS 1.2+) | 15.5 | ✅ | ✅ | v11+ |
115
116
 
116
117
  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.
@@ -145,11 +146,11 @@ withCodePush({ checkFrequency: CheckFrequency.ON_APP_RESUME })(MyApp);
145
146
  Alternatively, if you want fine-grained control over when the check happens (like a button press or timer interval), eg. in a staging environment, you can call [`CodePush.sync()`](docs/api-js/functions/sync.md) at any time with your desired `SyncOptions`, and turn off CodePush's automatic checking by specifying a manual `checkFrequency`:
146
147
 
147
148
  ```javascript
148
- import withCodePush, { CheckFrequency, InstallMode } from '@appzung/react-native-code-push';
149
+ import withCodePush, { CheckFrequency, InstallMode, sync } from '@appzung/react-native-code-push';
149
150
 
150
151
  class MyApp extends Component {
151
152
  onButtonPress() {
152
- CodePush.sync({
153
+ sync({
153
154
  updateDialog: true,
154
155
  installMode: InstallMode.IMMEDIATE,
155
156
  });
@@ -173,7 +174,7 @@ If you would like to display an update confirmation dialog (an "active install")
173
174
 
174
175
  ## Releasing updates
175
176
 
176
- Once your app is configured and distributed to your users, and you have made some JS or asset changes, it's time to release them. The recommended way to release them is using the `appzung releases deploy-react-native` command in the AppZung CLI, which will bundle your JavaScript files, asset files, and release the update to the CodePush server.
177
+ Once your app is configured and distributed to your users, and you have made some JS or asset changes, it's time to release them. The recommended way to release them is running the `appzung releases deploy-react-native` command (or `appzung releases deploy-expo` if using Expo) in the AppZung CLI, which will bundle your JavaScript files, asset files, and release the update to the CodePush server.
177
178
 
178
179
  ### Locally
179
180
 
@@ -258,7 +259,7 @@ This is not necessarily the case for `updateDialog`, since it won't force the us
258
259
 
259
260
  ## Debugging / Troubleshooting
260
261
 
261
- The `sync` method includes a lot of diagnostic logging out-of-the-box, so if you're encountering an issue when using it, the best thing to try first is examining the output logs of your app. This will tell you whether the app is configured correctly (like can the plugin find your release channel public ID?), if the app is able to reach the server, if an available update is being discovered, if the update is being successfully downloaded/installed, etc. We want to continue improving the logging to be as intuitive/comprehensive as possible, so please [let us know](mailto:support@appzung.com) if you find it to be confusing or missing anything.
262
+ The `withCodePush`/`sync` methods include a lot of diagnostic logging out-of-the-box, so if you're encountering an issue when using it, the best thing to try first is examining the output logs of your app. This will tell you whether the app is configured correctly (like can the plugin find your release channel public ID?), if the app is able to reach the server, if an available update is being discovered, if the update is being successfully downloaded/installed, etc. We want to continue improving the logging to be as intuitive/comprehensive as possible, so please [let us know](mailto:support@appzung.com) if you find it to be confusing or missing anything.
262
263
 
263
264
  Start up the Chrome DevTools Console, the Xcode Console (iOS) and/or ADB logcat (Android), and look for messages which are prefixed with `[CodePush]`.
264
265
 
@@ -274,12 +275,13 @@ Note that by default, React Native logs are disabled on iOS in release builds, s
274
275
 
275
276
  Now you'll be able to see CodePush logs in either debug or release mode, on both iOS or Android. If examining the logs don't provide an indication of the issue, please refer to the following common issues for additional resolution ideas:
276
277
 
277
- | Issue / Symptom | Possible Solution |
278
- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
279
- | Compilation Error | Clean your build folders and double-check that your version of React Native is [compatible](#compatibility-table) with the CodePush version you are using. |
280
- | Network timeout / hang when calling `sync` or `checkForUpdate` in the iOS Simulator | Try resetting the simulator by selecting the `Simulator -> Reset Content and Settings..` menu item, and then re-running your app. |
281
- | Server responds with a `404` when calling `sync` or `checkForUpdate` | Double-check that the release channel public ID you added to your `Info.plist` (iOS), `strings.xml` or `app/build.gradle` (Android) or that you're passing to `sync`/`checkForUpdate`, is in fact correct. You can run `appzung release-channels list` to view the correct public ID for your app release channel. |
282
- | Update not being discovered | Double-check that the version of your running app (like `1.0.0`) matches the version you specified when releasing the update to CodePush. Additionally, make sure that you are releasing to the same release channel that your app is configured to sync with. |
283
- | Update not being displayed after restart | If you're not using the withCodePush HOC or calling `sync` on app start (like within `componentDidMount` of your root component), then you need to explicitly call `notifyAppReady` on app start, otherwise, the plugin will think your update failed and roll it back. |
284
- | I've released an update for iOS but my Android app also shows an update and it breaks it | Be sure you have different release channels for each platform in order to receive updates correctly |
285
- | I've released new update but changes are not reflected | Be sure that you are running app in modes other than Debug. In Debug mode, React Native app always downloads JS bundle generated by packager, so JS bundle downloaded by CodePush does not apply. |
278
+ | Issue / Symptom | Possible Solution |
279
+ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
280
+ | Compilation Error | Clean your build folders and double-check that your version of React Native is [compatible](#compatibility-table) with the CodePush version you are using. |
281
+ | Network timeout / hang when calling `sync` or `checkForUpdate` in the iOS Simulator | Try resetting the simulator by selecting the `Simulator -> Reset Content and Settings..` menu item, and then re-running your app. |
282
+ | Server responds with a `404` when calling `sync` or `checkForUpdate` | Double-check that the release channel public ID you added to your `Info.plist` (iOS), `strings.xml` or `app/build.gradle` (Android) or that you're passing to `sync`/`checkForUpdate`, is in fact correct. You can run `appzung release-channels list` to view the correct public ID for your app release channel. |
283
+ | Update not being discovered | Double-check that the version of your running app (like `1.0.0`) matches the version you specified when releasing the update to CodePush. Additionally, make sure that you are releasing to the same release channel that your app is configured to sync with. |
284
+ | Update not being displayed after restart | If you're not using the withCodePush HOC or calling `sync` on app start (like within `componentDidMount` of your root component), then you need to explicitly call `notifyAppReady` on app start, otherwise, the plugin will think your update failed and roll it back. |
285
+ | I've released an update for iOS but my Android app also shows an update and it breaks it | Be sure you have different release channels for each platform in order to receive updates correctly |
286
+ | I've released new update but changes are not reflected | Be sure that you are running app in modes other than Debug. In Debug mode, React Native app always downloads JS bundle generated by packager, so JS bundle downloaded by CodePush does not apply. |
287
+ | Android compilation fails after migrating to AppZung with error "Task :app:checkReleaseAarMetadata FAILED A problem was found with the configuration of task ':app:checkReleaseAarMetadata' (type 'CheckAarMetadataTask')". | In `android/settings.gradle` remove the lines about CodePush. Be sure to read the other [migration steps](./docs/migrating-to-v10.md) carefully to review if you missed any others. |
@@ -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
  }
@@ -20,6 +20,11 @@
20
20
  -keepclassmembers class com.facebook.react.ReactInstanceManager {
21
21
  private final ** mBundleLoader;
22
22
  }
23
+ -keepclassmembers class com.facebook.react.runtime.ReactHostImpl {
24
+ private final ** mReactHostDelegate;
25
+ }
26
+ -keep interface com.facebook.react.runtime.ReactHostDelegate { *; }
27
+ -keep class * implements com.facebook.react.runtime.ReactHostDelegate { *; }
23
28
 
24
29
  # Can't find referenced class org.bouncycastle.**
25
30
  -dontwarn com.nimbusds.jose.**
@@ -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");
@@ -188,6 +203,7 @@ public class CodePush implements ReactPackage {
188
203
 
189
204
  public static String getJSBundleFile(String assetsBundleFileName) {
190
205
  if (mCurrentInstance == null) {
206
+ CodePushUtils.log("A CodePush instance has not been created yet. Have you added it to your app's list of ReactPackages?");
191
207
  throw new CodePushNotInitializedException("A CodePush instance has not been created yet. Have you added it to your app's list of ReactPackages?");
192
208
  }
193
209
 
@@ -366,6 +382,18 @@ public class CodePush implements ReactPackage {
366
382
  return mReactInstanceHolder.getReactInstanceManager();
367
383
  }
368
384
 
385
+ public static void setReactHost(ReactHostHolder reactHostHolder) {
386
+ mReactHostHolder = reactHostHolder;
387
+ }
388
+
389
+ static ReactHost getReactHost() {
390
+ if (mReactHostHolder == null) {
391
+ return null;
392
+ }
393
+
394
+ return mReactHostHolder.getReactHost();
395
+ }
396
+
369
397
  @Override
370
398
  public List<NativeModule> createNativeModules(ReactApplicationContext reactApplicationContext) {
371
399
  CodePushNativeModule codePushModule = new CodePushNativeModule(reactApplicationContext, this, mUpdateManager, mTelemetryManager, mSettingsManager);
@@ -8,22 +8,28 @@ import android.os.Looper;
8
8
  import android.view.Choreographer;
9
9
  import android.view.View;
10
10
 
11
+ import androidx.annotation.OptIn;
12
+
11
13
  import com.facebook.react.ReactApplication;
14
+ import com.facebook.react.ReactHost;
12
15
  import com.facebook.react.ReactInstanceManager;
13
16
  import com.facebook.react.ReactRootView;
14
17
  import com.facebook.react.bridge.Arguments;
18
+ import com.facebook.react.bridge.BaseJavaModule;
15
19
  import com.facebook.react.bridge.JSBundleLoader;
16
20
  import com.facebook.react.bridge.LifecycleEventListener;
17
21
  import com.facebook.react.bridge.Promise;
18
22
  import com.facebook.react.bridge.ReactApplicationContext;
19
- import com.facebook.react.bridge.ReactContextBaseJavaModule;
20
23
  import com.facebook.react.bridge.ReactMethod;
21
24
  import com.facebook.react.bridge.ReadableMap;
22
25
  import com.facebook.react.bridge.WritableMap;
26
+ import com.facebook.react.common.annotations.UnstableReactNativeAPI;
23
27
  import com.facebook.react.devsupport.interfaces.DevSupportManager;
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,11 +45,13 @@ 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 boolean mTelemetryEnabled = true;
46
53
  private boolean mDataTransmissionEnabled = true;
54
+ private boolean mIsExpoApp = false;
47
55
  private LifecycleEventListener mLifecycleEventListener = null;
48
56
  private int mMinimumBackgroundDuration = 0;
49
57
 
@@ -52,9 +60,9 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
52
60
  private CodePushTelemetryManager mTelemetryManager;
53
61
  private CodePushUpdateManager mUpdateManager;
54
62
 
55
- private boolean _allowed = true;
56
- private boolean _restartInProgress = false;
57
- private ArrayList<Boolean> _restartQueue = new ArrayList<>();
63
+ private boolean _allowed = true;
64
+ private boolean _restartInProgress = false;
65
+ private ArrayList<Boolean> _restartQueue = new ArrayList<>();
58
66
 
59
67
  public CodePushNativeModule(ReactApplicationContext reactContext, CodePush codePush, CodePushUpdateManager codePushUpdateManager, CodePushTelemetryManager codePushTelemetryManager, SettingsManager settingsManager) {
60
68
  super(reactContext);
@@ -64,6 +72,8 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
64
72
  mTelemetryManager = codePushTelemetryManager;
65
73
  mUpdateManager = codePushUpdateManager;
66
74
 
75
+ mIsExpoApp = ExpoUtils.detectExpoEnvironment(reactContext);
76
+
67
77
  // Initialize module state while we have a reference to the current context.
68
78
  mBinaryContentsHash = CodePushUpdateUtils.getHashForBinaryContents(reactContext, mCodePush.isDebugMode());
69
79
 
@@ -119,7 +129,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
119
129
  }
120
130
 
121
131
  private void loadBundleLegacy() {
122
- final Activity currentActivity = getCurrentActivity();
132
+ final Activity currentActivity = getReactApplicationContext().getCurrentActivity();
123
133
  if (currentActivity == null) {
124
134
  // The currentActivity can be null if it is backgrounded / destroyed, so we simply
125
135
  // no-op to prevent any null pointer exceptions.
@@ -150,65 +160,135 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
150
160
  bundleLoaderField.setAccessible(true);
151
161
  bundleLoaderField.set(instanceManager, latestJSBundleLoader);
152
162
  } catch (Exception e) {
153
- CodePushUtils.log("Unable to set JSBundle - CodePush may not support this version of React Native");
163
+ CodePushUtils.log("Unable to set JSBundle of ReactInstanceManager - CodePush may not support this version of React Native");
154
164
  throw new IllegalAccessException("Could not setJSBundle");
155
165
  }
156
166
  }
157
167
 
158
- private void loadBundle() {
159
- clearLifecycleEventListener();
168
+ // Use reflection to find and set the appropriate fields on ReactHostDelegate. See #556 for a proposal for a less brittle way
169
+ // to approach this.
170
+ private void setJSBundle(ReactHostDelegate reactHostDelegate, String latestJSBundleFile) throws IllegalAccessException {
160
171
  try {
161
- DevSupportManager devSupportManager = null;
162
- ReactInstanceManager reactInstanceManager = resolveInstanceManager();
163
- if (reactInstanceManager != null) {
164
- devSupportManager = reactInstanceManager.getDevSupportManager();
172
+ JSBundleLoader latestJSBundleLoader;
173
+ if (latestJSBundleFile.toLowerCase().startsWith("assets://")) {
174
+ latestJSBundleLoader = JSBundleLoader.createAssetLoader(getReactApplicationContext(), latestJSBundleFile, false);
175
+ } else {
176
+ latestJSBundleLoader = JSBundleLoader.createFileLoader(latestJSBundleFile);
165
177
  }
166
- boolean isLiveReloadEnabled = isLiveReloadEnabled(devSupportManager);
167
178
 
168
- mCodePush.clearDebugCacheIfNeeded(isLiveReloadEnabled);
169
- } catch(Exception e) {
170
- // If we got error in out reflection we should clear debug cache anyway.
171
- mCodePush.clearDebugCacheIfNeeded(false);
179
+ Field bundleLoaderField = reactHostDelegate.getClass().getDeclaredField("jsBundleLoader");
180
+ bundleLoaderField.setAccessible(true);
181
+ bundleLoaderField.set(reactHostDelegate, latestJSBundleLoader);
182
+ } catch (Exception e) {
183
+ CodePushUtils.log("Unable to set JSBundle of ReactHostDelegate - CodePush may not support this version of React Native");
184
+ throw new IllegalAccessException("Could not setJSBundle");
172
185
  }
186
+ }
173
187
 
174
- try {
175
- // #1) Get the ReactInstanceManager instance, which is what includes the
176
- // logic to reload the current React context.
177
- final ReactInstanceManager instanceManager = resolveInstanceManager();
178
- if (instanceManager == null) {
179
- return;
188
+ private void loadBundle() {
189
+ clearLifecycleEventListener();
190
+
191
+ if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
192
+ try {
193
+ DevSupportManager devSupportManager = null;
194
+ ReactHost reactHost = resolveReactHost();
195
+ if (reactHost != null) {
196
+ devSupportManager = reactHost.getDevSupportManager();
197
+ }
198
+ boolean isLiveReloadEnabled = isLiveReloadEnabled(devSupportManager);
199
+
200
+ mCodePush.clearDebugCacheIfNeeded(isLiveReloadEnabled);
201
+ } catch (Exception e) {
202
+ // If we got error in out reflection we should clear debug cache anyway.
203
+ mCodePush.clearDebugCacheIfNeeded(false);
180
204
  }
181
205
 
182
- String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFileName());
183
-
184
- // #2) Update the locally stored JS bundle file path
185
- setJSBundle(instanceManager, latestJSBundleFile);
186
-
187
- // #3) Get the context creation method and fire it on the UI thread (which RN enforces)
188
- new Handler(Looper.getMainLooper()).post(new Runnable() {
189
- @Override
190
- public void run() {
191
- try {
192
- // We don't need to resetReactRootViews anymore
193
- // due the issue https://github.com/facebook/react-native/issues/14533
194
- // has been fixed in RN 0.46.0
195
- //resetReactRootViews(instanceManager);
196
-
197
- instanceManager.recreateReactContextInBackground();
198
- mCodePush.initializeUpdateAfterRestart();
199
- } catch (Exception e) {
200
- // The recreation method threw an unknown exception
201
- // so just simply fallback to restarting the Activity (if it exists)
202
- loadBundleLegacy();
203
- }
206
+ try {
207
+ // #1) Get the ReactHost instance, which is what includes the
208
+ // logic to reload the current React context.
209
+ final ReactHost reactHost = resolveReactHost();
210
+ if (reactHost == null) {
211
+ return;
204
212
  }
205
- });
206
213
 
207
- } catch (Exception e) {
208
- // Our reflection logic failed somewhere
209
- // so fall back to restarting the Activity (if it exists)
210
- CodePushUtils.log("Failed to load the bundle, falling back to restarting the Activity (if it exists). " + e.getMessage());
211
- loadBundleLegacy();
214
+ String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFileName());
215
+
216
+ // if expo and new arch this is unnecessary, see https://github.com/expo/expo/blob/8113ce44edaef0311e2daff3ab1a63b9731d2d6c/packages/expo-updates/android/src/main/java/expo/modules/updates/procedures/RelaunchProcedure.kt#L148
217
+ if (!mIsExpoApp) {
218
+ // #2) Update the locally stored JS bundle file path
219
+ setJSBundle(getReactHostDelegate((ReactHostImpl) reactHost), latestJSBundleFile);
220
+ }
221
+
222
+ // #3) Get the context creation method
223
+ try {
224
+ reactHost.reload("CodePush triggers reload");
225
+ mCodePush.initializeUpdateAfterRestart();
226
+ } catch (Exception e) {
227
+ // The recreation method threw an unknown exception
228
+ // so just simply fallback to restarting the Activity (if it exists)
229
+ loadBundleLegacy();
230
+ }
231
+
232
+ } catch (Exception e) {
233
+ // Our reflection logic failed somewhere
234
+ // so fall back to restarting the Activity (if it exists)
235
+ CodePushUtils.log("Failed to load the bundle, falling back to restarting the Activity (if it exists). " + e.getMessage());
236
+ loadBundleLegacy();
237
+ }
238
+ } else {
239
+ try {
240
+ DevSupportManager devSupportManager = null;
241
+ ReactInstanceManager reactInstanceManager = resolveInstanceManager();
242
+ if (reactInstanceManager != null) {
243
+ devSupportManager = reactInstanceManager.getDevSupportManager();
244
+ }
245
+ boolean isLiveReloadEnabled = isLiveReloadEnabled(devSupportManager);
246
+
247
+ mCodePush.clearDebugCacheIfNeeded(isLiveReloadEnabled);
248
+ } catch (Exception e) {
249
+ // If we got error in out reflection we should clear debug cache anyway.
250
+ mCodePush.clearDebugCacheIfNeeded(false);
251
+ }
252
+
253
+ try {
254
+ // #1) Get the ReactInstanceManager instance, which is what includes the
255
+ // logic to reload the current React context.
256
+ final ReactInstanceManager instanceManager = resolveInstanceManager();
257
+ if (instanceManager == null) {
258
+ return;
259
+ }
260
+
261
+ String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFileName());
262
+
263
+ // #2) Update the locally stored JS bundle file path
264
+ setJSBundle(instanceManager, latestJSBundleFile);
265
+
266
+ // #3) Get the context creation method and fire it on the UI thread (which RN enforces)
267
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
268
+ @Override
269
+ public void run() {
270
+ try {
271
+ // We don't need to resetReactRootViews anymore
272
+ // due the issue https://github.com/facebook/react-native/issues/14533
273
+ // has been fixed in RN 0.46.0
274
+ //resetReactRootViews(instanceManager);
275
+
276
+ instanceManager.recreateReactContextInBackground();
277
+ mCodePush.initializeUpdateAfterRestart();
278
+ } catch (Exception e) {
279
+ // The recreation method threw an unknown exception
280
+ // so just simply fallback to restarting the Activity (if it exists)
281
+ loadBundleLegacy();
282
+ }
283
+ }
284
+ });
285
+
286
+ } catch (Exception e) {
287
+ // Our reflection logic failed somewhere
288
+ // so fall back to restarting the Activity (if it exists)
289
+ CodePushUtils.log("Failed to load the bundle, falling back to restarting the Activity (if it exists). " + e.getMessage());
290
+ loadBundleLegacy();
291
+ }
212
292
  }
213
293
  }
214
294
 
@@ -238,7 +318,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
238
318
  private void resetReactRootViews(ReactInstanceManager instanceManager) throws NoSuchFieldException, IllegalAccessException {
239
319
  Field mAttachedRootViewsField = instanceManager.getClass().getDeclaredField("mAttachedRootViews");
240
320
  mAttachedRootViewsField.setAccessible(true);
241
- List<ReactRootView> mAttachedRootViews = (List<ReactRootView>)mAttachedRootViewsField.get(instanceManager);
321
+ List<ReactRootView> mAttachedRootViews = (List<ReactRootView>) mAttachedRootViewsField.get(instanceManager);
242
322
  for (ReactRootView reactRootView : mAttachedRootViews) {
243
323
  reactRootView.removeAllViews();
244
324
  reactRootView.setId(View.NO_ID);
@@ -261,7 +341,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
261
341
  return instanceManager;
262
342
  }
263
343
 
264
- final Activity currentActivity = getCurrentActivity();
344
+ final Activity currentActivity = getReactApplicationContext().getCurrentActivity();
265
345
  if (currentActivity == null) {
266
346
  return null;
267
347
  }
@@ -272,6 +352,21 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
272
352
  return instanceManager;
273
353
  }
274
354
 
355
+ private ReactHost resolveReactHost() throws NoSuchFieldException, IllegalAccessException {
356
+ ReactHost reactHost = CodePush.getReactHost();
357
+ if (reactHost != null) {
358
+ return reactHost;
359
+ }
360
+
361
+ final Activity currentActivity = getReactApplicationContext().getCurrentActivity();
362
+ if (currentActivity == null) {
363
+ return null;
364
+ }
365
+
366
+ ReactApplication reactApplication = (ReactApplication) currentActivity.getApplication();
367
+ return reactApplication.getReactHost();
368
+ }
369
+
275
370
  private void restartAppInternal(boolean onlyIfUpdateIsPending) {
276
371
  if (this._restartInProgress) {
277
372
  CodePushUtils.log("Restart request queued until the current restart is completed");
@@ -334,7 +429,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
334
429
  try {
335
430
  restartAppInternal(onlyIfUpdateIsPending);
336
431
  promise.resolve(null);
337
- } catch(CodePushUnknownException e) {
432
+ } catch (CodePushUnknownException e) {
338
433
  CodePushUtils.log(e);
339
434
  promise.reject(e);
340
435
  }
@@ -415,7 +510,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
415
510
  @ReactMethod
416
511
  public void getConfiguration(Promise promise) {
417
512
  try {
418
- WritableMap configMap = Arguments.createMap();
513
+ WritableMap configMap = Arguments.createMap();
419
514
  configMap.putString("appVersion", mCodePush.getAppVersion());
420
515
  configMap.putString("clientUniqueId", mClientUniqueId);
421
516
  configMap.putString("releaseChannelPublicId", mCodePush.getReleaseChannelPublicId());
@@ -429,7 +524,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
429
524
  }
430
525
 
431
526
  promise.resolve(configMap);
432
- } catch(CodePushUnknownException e) {
527
+ } catch (CodePushUnknownException e) {
433
528
  CodePushUtils.log(e);
434
529
  promise.reject(e);
435
530
  }
@@ -491,7 +586,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
491
586
  CodePushUtils.log(e.getMessage());
492
587
  clearUpdates();
493
588
  promise.resolve(null);
494
- } catch(CodePushUnknownException e) {
589
+ } catch (CodePushUnknownException e) {
495
590
  CodePushUtils.log(e);
496
591
  promise.reject(e);
497
592
  }
@@ -549,7 +644,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
549
644
  }
550
645
 
551
646
  promise.resolve("");
552
- } catch(CodePushUnknownException e) {
647
+ } catch (CodePushUnknownException e) {
553
648
  CodePushUtils.log(e);
554
649
  promise.reject(e);
555
650
  }
@@ -576,11 +671,11 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
576
671
  }
577
672
 
578
673
  if (installMode == CodePushInstallMode.ON_NEXT_RESUME.getValue() ||
579
- // We also add the resume listener if the installMode is IMMEDIATE, because
580
- // if the current activity is backgrounded, we want to reload the bundle when
581
- // it comes back into the foreground.
582
- installMode == CodePushInstallMode.IMMEDIATE.getValue() ||
583
- installMode == CodePushInstallMode.ON_NEXT_SUSPEND.getValue()) {
674
+ // We also add the resume listener if the installMode is IMMEDIATE, because
675
+ // if the current activity is backgrounded, we want to reload the bundle when
676
+ // it comes back into the foreground.
677
+ installMode == CodePushInstallMode.IMMEDIATE.getValue() ||
678
+ installMode == CodePushInstallMode.ON_NEXT_SUSPEND.getValue()) {
584
679
 
585
680
  // Store the minimum duration on the native module as an instance
586
681
  // variable instead of relying on a closure below, so that any
@@ -636,7 +731,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
636
731
  }
637
732
 
638
733
  promise.resolve("");
639
- } catch(CodePushUnknownException e) {
734
+ } catch (CodePushUnknownException e) {
640
735
  CodePushUtils.log(e);
641
736
  promise.reject(e);
642
737
  }
@@ -692,7 +787,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
692
787
  && packageHash.length() > 0
693
788
  && packageHash.equals(mUpdateManager.getCurrentPackageHash());
694
789
  promise.resolve(isFirstRun);
695
- } catch(CodePushUnknownException e) {
790
+ } catch (CodePushUnknownException e) {
696
791
  CodePushUtils.log(e);
697
792
  promise.reject(e);
698
793
  }
@@ -703,7 +798,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
703
798
  try {
704
799
  mSettingsManager.removePendingUpdate();
705
800
  promise.resolve("");
706
- } catch(CodePushUnknownException e) {
801
+ } catch (CodePushUnknownException e) {
707
802
  CodePushUtils.log(e);
708
803
  promise.reject(e);
709
804
  }
@@ -713,7 +808,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
713
808
  public void recordStatusReported(ReadableMap statusReport) {
714
809
  try {
715
810
  mTelemetryManager.recordStatusReported(statusReport);
716
- } catch(CodePushUnknownException e) {
811
+ } catch (CodePushUnknownException e) {
717
812
  CodePushUtils.log(e);
718
813
  }
719
814
  }
@@ -722,7 +817,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
722
817
  public void saveStatusReportForRetry(ReadableMap statusReport) {
723
818
  try {
724
819
  mTelemetryManager.saveStatusReportForRetry(statusReport);
725
- } catch(CodePushUnknownException e) {
820
+ } catch (CodePushUnknownException e) {
726
821
  CodePushUtils.log(e);
727
822
  }
728
823
  }
@@ -739,7 +834,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
739
834
  throw new CodePushUnknownException("Unable to replace current bundle", e);
740
835
  }
741
836
  }
742
- } catch(CodePushUnknownException | CodePushMalformedDataException e) {
837
+ } catch (CodePushUnknownException | CodePushMalformedDataException e) {
743
838
  CodePushUtils.log(e);
744
839
  }
745
840
  }
@@ -816,4 +911,18 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
816
911
  public void removeListeners(Integer count) {
817
912
  // Remove upstream listeners, stop unnecessary background tasks
818
913
  }
914
+
915
+ public ReactHostDelegate getReactHostDelegate(ReactHostImpl reactHostImpl) {
916
+ try {
917
+ Class<?> clazz = reactHostImpl.getClass();
918
+ Field field = clazz.getDeclaredField("mReactHostDelegate");
919
+ field.setAccessible(true);
920
+
921
+ // Get the value of the field for the provided instance
922
+ return (ReactHostDelegate) field.get(reactHostImpl);
923
+ } catch (NoSuchFieldException | IllegalAccessException e) {
924
+ e.printStackTrace();
925
+ return null;
926
+ }
927
+ }
819
928
  }