Skip to content

Commit 94daa3a

Browse files
committed
feat: add support for New Architecture and TurboModule
Implement TurboModule support for CodePush to enable compatibility with the React Native New Architecture. - Add TurboModule specification in `src/NativeCodePush.ts` - Configure `codegenConfig` in `package.json` - Implement Android bridge for both old and new architectures - Update `CodePush.js` to prefer `TurboModuleRegistry` over `NativeModules`
1 parent 25ab10e commit 94daa3a

File tree

7 files changed

+129
-16
lines changed

7 files changed

+129
-16
lines changed

CodePush.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { AcquisitionManager as Sdk } from "code-push/script/acquisition-sdk";
22
import { Alert } from "./AlertAdapter";
33
import requestFetchAdapter from "./request-fetch-adapter";
4-
import { AppState, Platform } from "react-native";
4+
import { AppState, Platform, TurboModuleRegistry } from "react-native";
55
import log from "./logging";
66
import hoistStatics from 'hoist-non-react-statics';
77

8-
let NativeCodePush = require("react-native").NativeModules.CodePush;
8+
let NativeCodePush = (TurboModuleRegistry?.get('CodePush')) || require("react-native").NativeModules.CodePush;
99
const PackageMixins = require("./package-mixins")(NativeCodePush);
1010

1111
async function checkForUpdate(deploymentKey = null, handleBinaryVersionMismatchCallback = null) {

android/app/build.gradle

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ android {
4040
defaultConfig {
4141
consumerProguardFiles 'proguard-rules.pro'
4242
}
43+
44+
sourceSets {
45+
main {
46+
if (IS_NEW_ARCHITECTURE_ENABLED) {
47+
java.srcDirs += ['src/newarch/java']
48+
} else {
49+
java.srcDirs += ['src/oldarch/java']
50+
}
51+
}
52+
}
4353
}
4454

4555
dependencies {

android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
import java.util.UUID;
4747

4848
@OptIn(markerClass = UnstableReactNativeAPI.class)
49-
public class CodePushNativeModule extends BaseJavaModule {
49+
public class CodePushNativeModule extends CodePushNativeModuleSpec {
5050
private String mBinaryContentsHash = null;
5151
private String mClientUniqueId = null;
5252
private LifecycleEventListener mLifecycleEventListener = null;
@@ -82,6 +82,11 @@ public CodePushNativeModule(ReactApplicationContext reactContext, CodePush codeP
8282

8383
@Override
8484
public Map<String, Object> getConstants() {
85+
return getTypedExportedConstants();
86+
}
87+
88+
@Override
89+
public Map<String, Object> getTypedExportedConstants() {
8590
final Map<String, Object> constants = new HashMap<>();
8691

8792
constants.put("codePushInstallModeImmediate", CodePushInstallMode.IMMEDIATE.getValue());
@@ -500,8 +505,9 @@ public void getConfiguration(Promise promise) {
500505
}
501506
}
502507

508+
@Override
503509
@ReactMethod
504-
public void getUpdateMetadata(final int updateState, final Promise promise) {
510+
public void getUpdateMetadata(final double updateState, final Promise promise) {
505511
AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
506512
@Override
507513
protected Void doInBackground(Void... params) {
@@ -520,11 +526,11 @@ protected Void doInBackground(Void... params) {
520526
currentUpdateIsPending = mSettingsManager.isPendingUpdate(currentHash);
521527
}
522528

523-
if (updateState == CodePushUpdateState.PENDING.getValue() && !currentUpdateIsPending) {
529+
if ((int)updateState == CodePushUpdateState.PENDING.getValue() && !currentUpdateIsPending) {
524530
// The caller wanted a pending update
525531
// but there isn't currently one.
526532
promise.resolve(null);
527-
} else if (updateState == CodePushUpdateState.RUNNING.getValue() && currentUpdateIsPending) {
533+
} else if ((int)updateState == CodePushUpdateState.RUNNING.getValue() && currentUpdateIsPending) {
528534
// The caller wants the running update, but the current
529535
// one is pending, so we need to grab the previous.
530536
JSONObject previousPackage = mUpdateManager.getPreviousPackage();
@@ -625,8 +631,9 @@ protected Void doInBackground(Void... params) {
625631
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
626632
}
627633

634+
@Override
628635
@ReactMethod
629-
public void installUpdate(final ReadableMap updatePackage, final int installMode, final int minimumBackgroundDuration, final Promise promise) {
636+
public void installUpdate(final ReadableMap updatePackage, final double installMode, final double minimumBackgroundDuration, final Promise promise) {
630637
AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
631638
@Override
632639
protected Void doInBackground(Void... params) {
@@ -640,17 +647,17 @@ protected Void doInBackground(Void... params) {
640647
mSettingsManager.savePendingUpdate(pendingHash, /* isLoading */false);
641648
}
642649

643-
if (installMode == CodePushInstallMode.ON_NEXT_RESUME.getValue() ||
650+
if ((int)installMode == CodePushInstallMode.ON_NEXT_RESUME.getValue() ||
644651
// We also add the resume listener if the installMode is IMMEDIATE, because
645652
// if the current activity is backgrounded, we want to reload the bundle when
646653
// it comes back into the foreground.
647-
installMode == CodePushInstallMode.IMMEDIATE.getValue() ||
648-
installMode == CodePushInstallMode.ON_NEXT_SUSPEND.getValue()) {
654+
(int)installMode == CodePushInstallMode.IMMEDIATE.getValue() ||
655+
(int)installMode == CodePushInstallMode.ON_NEXT_SUSPEND.getValue()) {
649656

650657
// Store the minimum duration on the native module as an instance
651658
// variable instead of relying on a closure below, so that any
652659
// subsequent resume-based installs could override it.
653-
CodePushNativeModule.this.mMinimumBackgroundDuration = minimumBackgroundDuration;
660+
CodePushNativeModule.this.mMinimumBackgroundDuration = (int)minimumBackgroundDuration;
654661

655662
if (mLifecycleEventListener == null) {
656663
// Ensure we do not add the listener twice.
@@ -672,7 +679,7 @@ public void onHostResume() {
672679
// the foreground, so explicitly wait for it to be backgrounded first
673680
if (lastPausedDate != null) {
674681
long durationInBackground = (new Date().getTime() - lastPausedDate.getTime()) / 1000;
675-
if (installMode == CodePushInstallMode.IMMEDIATE.getValue()
682+
if ((int)installMode == CodePushInstallMode.IMMEDIATE.getValue()
676683
|| durationInBackground >= CodePushNativeModule.this.mMinimumBackgroundDuration) {
677684
CodePushUtils.log("Loading bundle on resume");
678685
restartAppInternal(false);
@@ -686,8 +693,8 @@ public void onHostPause() {
686693
// resumed, we can detect how long it was in the background.
687694
lastPausedDate = new Date();
688695

689-
if (installMode == CodePushInstallMode.ON_NEXT_SUSPEND.getValue() && mSettingsManager.isPendingUpdate(null)) {
690-
appSuspendHandler.postDelayed(loadBundleRunnable, minimumBackgroundDuration * 1000);
696+
if ((int)installMode == CodePushInstallMode.ON_NEXT_SUSPEND.getValue() && mSettingsManager.isPendingUpdate(null)) {
697+
appSuspendHandler.postDelayed(loadBundleRunnable, (int)minimumBackgroundDuration * 1000);
691698
}
692699
}
693700

@@ -700,7 +707,11 @@ public void onHostDestroy() {
700707
}
701708
}
702709

703-
promise.resolve("");
710+
if ((int)installMode == CodePushInstallMode.IMMEDIATE.getValue()) {
711+
loadBundle();
712+
}
713+
714+
promise.resolve(null);
704715
} catch(CodePushUnknownException e) {
705716
CodePushUtils.log(e);
706717
promise.reject(e);
@@ -822,13 +833,15 @@ public void clearUpdates() {
822833
mCodePush.clearUpdates();
823834
}
824835

836+
@Override
825837
@ReactMethod
826838
public void addListener(String eventName) {
827839
// Set up any upstream listeners or background tasks as necessary
828840
}
829841

842+
@Override
830843
@ReactMethod
831-
public void removeListeners(Integer count) {
844+
public void removeListeners(double count) {
832845
// Remove upstream listeners, stop unnecessary background tasks
833846
}
834847

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.microsoft.codepush.react;
2+
3+
import com.facebook.react.bridge.ReactApplicationContext;
4+
5+
public abstract class CodePushNativeModuleSpec extends NativeRNCodePushSpec {
6+
public CodePushNativeModuleSpec(ReactApplicationContext reactContext) {
7+
super(reactContext);
8+
}
9+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.microsoft.codepush.react;
2+
3+
import com.facebook.react.bridge.ReactApplicationContext;
4+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
5+
import com.facebook.react.bridge.Promise;
6+
import com.facebook.react.bridge.ReadableMap;
7+
8+
public abstract class CodePushNativeModuleSpec extends ReactContextBaseJavaModule {
9+
public CodePushNativeModuleSpec(ReactApplicationContext reactContext) {
10+
super(reactContext);
11+
}
12+
13+
public abstract void allow(Promise promise);
14+
public abstract void clearPendingRestart(Promise promise);
15+
public abstract void disallow(Promise promise);
16+
public abstract void restartApp(boolean onlyIfUpdateIsPending, Promise promise);
17+
public abstract void downloadUpdate(ReadableMap updatePackage, boolean notifyProgress, Promise promise);
18+
public abstract void getConfiguration(Promise promise);
19+
public abstract void getUpdateMetadata(double updateState, Promise promise);
20+
public abstract void getNewStatusReport(Promise promise);
21+
public abstract void installUpdate(ReadableMap updatePackage, double installMode, double minimumBackgroundDuration, Promise promise);
22+
public abstract void isFailedUpdate(String packageHash, Promise promise);
23+
public abstract void isFirstRun(String packageHash, Promise promise);
24+
public abstract void notifyApplicationReady(Promise promise);
25+
public abstract void recordStatusReported(ReadableMap statusReport);
26+
public abstract void saveStatusReportForRetry(ReadableMap statusReport);
27+
public abstract void downloadAndReplaceCurrentBundle(String remoteBundleUrl);
28+
public abstract void clearUpdates();
29+
public abstract void getLatestRollbackInfo(Promise promise);
30+
public abstract void setLatestRollbackInfo(String packageHash, Promise promise);
31+
public abstract void addListener(String eventName);
32+
public abstract void removeListeners(double count);
33+
public abstract java.util.Map<String, Object> getTypedExportedConstants();
34+
}

package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,13 @@
8181
"postlink": "node node_modules/@srcpush/react-native-code-push/scripts/postlink/run",
8282
"postunlink": "node node_modules/@srcpush/react-native-code-push/scripts/postunlink/run"
8383
}
84+
},
85+
"codegenConfig": {
86+
"name": "RNCodePush",
87+
"type": "modules",
88+
"jsSrcsDir": "src",
89+
"android": {
90+
"javaPackageName": "com.microsoft.codepush.react"
91+
}
8492
}
8593
}

src/NativeCodePush.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { TurboModule } from 'react-native';
2+
import { TurboModuleRegistry } from 'react-native';
3+
4+
export interface Spec extends TurboModule {
5+
getConstants(): {
6+
codePushInstallModeImmediate: number;
7+
codePushInstallModeOnNextRestart: number;
8+
codePushInstallModeOnNextResume: number;
9+
codePushInstallModeOnNextSuspend: number;
10+
codePushUpdateStateRunning: number;
11+
codePushUpdateStatePending: number;
12+
codePushUpdateStateLatest: number;
13+
};
14+
15+
allow(): Promise<void>;
16+
clearPendingRestart(): Promise<void>;
17+
disallow(): Promise<void>;
18+
restartApp(onlyIfUpdateIsPending: boolean): Promise<void>;
19+
downloadUpdate(updatePackage: Object, notifyProgress: boolean): Promise<Object>;
20+
getConfiguration(): Promise<Object>;
21+
getUpdateMetadata(updateState: number): Promise<Object>;
22+
getNewStatusReport(): Promise<Object>;
23+
installUpdate(updatePackage: Object, installMode: number, minimumBackgroundDuration: number): Promise<void>;
24+
isFailedUpdate(packageHash: string): Promise<boolean>;
25+
isFirstRun(packageHash: string): Promise<boolean>;
26+
notifyApplicationReady(): Promise<void>;
27+
recordStatusReported(statusReport: Object): void;
28+
saveStatusReportForRetry(statusReport: Object): void;
29+
downloadAndReplaceCurrentBundle(remoteBundleUrl: string): void;
30+
clearUpdates(): void;
31+
getLatestRollbackInfo(): Promise<Object>;
32+
setLatestRollbackInfo(packageHash: string): Promise<void>;
33+
34+
// Events
35+
addListener(eventName: string): void;
36+
removeListeners(count: number): void;
37+
}
38+
39+
export default TurboModuleRegistry.get<Spec>('CodePush');

0 commit comments

Comments
 (0)