@turbopush/react-native-code-push 9.0.2 → 10.2.3
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/.gitconfig +6 -0
- package/CodePush.podspec +4 -3
- package/README.md +57 -71
- package/android/app/build.gradle +15 -0
- package/android/app/proguard-rules.pro +8 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +37 -34
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushDialog.java +4 -4
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java +202 -45
- package/android/app/src/main/java/com/microsoft/codepush/react/ReactHostHolder.java +11 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/ReactInstanceHolder.java +3 -0
- package/android/codepush.gradle +2 -2
- package/docs/api-android.md +24 -55
- package/docs/multi-deployment-testing-android.md +1 -94
- package/docs/setup-android.md +23 -421
- package/docs/setup-ios.md +62 -204
- package/expo.js +324 -0
- package/ios/CodePush/CodePushConfig.m +1 -1
- package/package.json +10 -6
- package/react-native.config.js +2 -1
- package/tsconfig.json +4 -1
- package/windows/CodePush/CodePushConfig.cpp +1 -1
- package/windows-legacy/CodePush.Shared/CodePushConstants.cs +1 -1
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushBuilder.java +0 -37
- package/scripts/postlink/android/postlink.js +0 -87
- package/scripts/postlink/ios/postlink.js +0 -116
- package/scripts/postlink/run.js +0 -11
- package/scripts/postunlink/android/postunlink.js +0 -74
- package/scripts/postunlink/ios/postunlink.js +0 -87
- package/scripts/postunlink/run.js +0 -11
- package/scripts/tools/linkToolsAndroid.js +0 -57
- package/scripts/tools/linkToolsIos.js +0 -130
|
@@ -5,24 +5,31 @@ 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.provider.Settings;
|
|
9
8
|
import android.view.View;
|
|
10
9
|
|
|
10
|
+
import androidx.annotation.OptIn;
|
|
11
|
+
|
|
11
12
|
import com.facebook.react.ReactApplication;
|
|
13
|
+
import com.facebook.react.ReactHost;
|
|
12
14
|
import com.facebook.react.ReactInstanceManager;
|
|
13
15
|
import com.facebook.react.ReactRootView;
|
|
14
16
|
import com.facebook.react.bridge.Arguments;
|
|
17
|
+
import com.facebook.react.bridge.BaseJavaModule;
|
|
15
18
|
import com.facebook.react.bridge.JSBundleLoader;
|
|
16
19
|
import com.facebook.react.bridge.LifecycleEventListener;
|
|
17
20
|
import com.facebook.react.bridge.Promise;
|
|
18
21
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
19
|
-
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
20
22
|
import com.facebook.react.bridge.ReactMethod;
|
|
21
23
|
import com.facebook.react.bridge.ReadableMap;
|
|
22
24
|
import com.facebook.react.bridge.WritableMap;
|
|
23
|
-
import com.facebook.react.
|
|
25
|
+
import com.facebook.react.common.annotations.UnstableReactNativeAPI;
|
|
26
|
+
import com.facebook.react.devsupport.interfaces.DevSupportManager;
|
|
27
|
+
import android.view.Choreographer;
|
|
24
28
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
25
29
|
import com.facebook.react.modules.core.ReactChoreographer;
|
|
30
|
+
import com.facebook.react.modules.debug.interfaces.DeveloperSettings;
|
|
31
|
+
import com.facebook.react.runtime.ReactHostDelegate;
|
|
32
|
+
import com.facebook.react.runtime.ReactHostImpl;
|
|
26
33
|
|
|
27
34
|
import org.json.JSONArray;
|
|
28
35
|
import org.json.JSONException;
|
|
@@ -30,6 +37,7 @@ import org.json.JSONObject;
|
|
|
30
37
|
|
|
31
38
|
import java.io.IOException;
|
|
32
39
|
import java.lang.reflect.Field;
|
|
40
|
+
import java.lang.reflect.Method;
|
|
33
41
|
import java.util.ArrayList;
|
|
34
42
|
import java.util.Date;
|
|
35
43
|
import java.util.HashMap;
|
|
@@ -37,7 +45,8 @@ import java.util.List;
|
|
|
37
45
|
import java.util.Map;
|
|
38
46
|
import java.util.UUID;
|
|
39
47
|
|
|
40
|
-
|
|
48
|
+
@OptIn(markerClass = UnstableReactNativeAPI.class)
|
|
49
|
+
public class CodePushNativeModule extends BaseJavaModule {
|
|
41
50
|
private String mBinaryContentsHash = null;
|
|
42
51
|
private String mClientUniqueId = null;
|
|
43
52
|
private LifecycleEventListener mLifecycleEventListener = null;
|
|
@@ -93,7 +102,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
|
|
|
93
102
|
}
|
|
94
103
|
|
|
95
104
|
private void loadBundleLegacy() {
|
|
96
|
-
final Activity currentActivity = getCurrentActivity();
|
|
105
|
+
final Activity currentActivity = getReactApplicationContext().getCurrentActivity();
|
|
97
106
|
if (currentActivity == null) {
|
|
98
107
|
// The currentActivity can be null if it is backgrounded / destroyed, so we simply
|
|
99
108
|
// no-op to prevent any null pointer exceptions.
|
|
@@ -124,61 +133,180 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
|
|
|
124
133
|
bundleLoaderField.setAccessible(true);
|
|
125
134
|
bundleLoaderField.set(instanceManager, latestJSBundleLoader);
|
|
126
135
|
} catch (Exception e) {
|
|
127
|
-
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");
|
|
128
137
|
throw new IllegalAccessException("Could not setJSBundle");
|
|
129
138
|
}
|
|
130
139
|
}
|
|
131
140
|
|
|
132
|
-
|
|
133
|
-
|
|
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 {
|
|
144
|
+
|
|
134
145
|
try {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
146
|
+
JSBundleLoader latestJSBundleLoader;
|
|
147
|
+
if (latestJSBundleFile.toLowerCase().startsWith("assets://")) {
|
|
148
|
+
latestJSBundleLoader = JSBundleLoader.createAssetLoader(getReactApplicationContext(), latestJSBundleFile, false);
|
|
149
|
+
} else {
|
|
150
|
+
latestJSBundleLoader = JSBundleLoader.createFileLoader(latestJSBundleFile);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
Field bundleLoaderField = reactHostDelegate.getClass().getDeclaredField("jsBundleLoader");
|
|
154
|
+
bundleLoaderField.setAccessible(true);
|
|
155
|
+
bundleLoaderField.set(reactHostDelegate, latestJSBundleLoader);
|
|
156
|
+
|
|
157
|
+
} catch (NoSuchFieldException nsfe) {
|
|
158
|
+
CodePushUtils.log("Field 'jsBundleLoader' NOT FOUND on " + (reactHostDelegate != null ? reactHostDelegate.getClass().getName() : "null") + ". This is an EXPECTED and IGNORED failure with ExpoReactHostDelegate. Will rely on reactHost.reload(). Original log: Unable to set JSBundle of ReactHostDelegate - CodePush may not support this version of React Native");
|
|
159
|
+
// DO NOT THROW for NoSuchFieldException.
|
|
160
|
+
}catch (Exception e) {
|
|
161
|
+
CodePushUtils.log("Unable to set JSBundle of ReactHostDelegate - CodePush may not support this version of React Native");
|
|
162
|
+
throw new IllegalAccessException("Could not setJSBundle");
|
|
139
163
|
}
|
|
164
|
+
}
|
|
140
165
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
166
|
+
private void loadBundle() {
|
|
167
|
+
clearLifecycleEventListener();
|
|
168
|
+
|
|
169
|
+
// ReactNative core components are changed on new architecture.
|
|
170
|
+
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
|
171
|
+
try {
|
|
172
|
+
DevSupportManager devSupportManager = null;
|
|
173
|
+
ReactHost reactHost = resolveReactHost();
|
|
174
|
+
if (reactHost != null) {
|
|
175
|
+
devSupportManager = reactHost.getDevSupportManager();
|
|
176
|
+
}
|
|
177
|
+
boolean isLiveReloadEnabled = isLiveReloadEnabled(devSupportManager);
|
|
178
|
+
|
|
179
|
+
mCodePush.clearDebugCacheIfNeeded(isLiveReloadEnabled);
|
|
180
|
+
} catch(Exception e) {
|
|
181
|
+
// If we got error in out reflection we should clear debug cache anyway.
|
|
182
|
+
mCodePush.clearDebugCacheIfNeeded(false);
|
|
147
183
|
}
|
|
148
184
|
|
|
149
|
-
String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFileName());
|
|
150
185
|
|
|
151
|
-
|
|
152
|
-
|
|
186
|
+
try {
|
|
187
|
+
// #1) Get the ReactHost instance, which is what includes the
|
|
188
|
+
// logic to reload the current React context.
|
|
189
|
+
final ReactHost reactHost = resolveReactHost();
|
|
190
|
+
if (reactHost == null) {
|
|
191
|
+
loadBundleLegacy(); // Fallback if reactHost can't be resolved
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
153
194
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
loadBundleLegacy();
|
|
195
|
+
String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFileName());
|
|
196
|
+
CodePushUtils.log("[MyDebug] Latest JS bundle for New Arch: " + latestJSBundleFile);
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
if (reactHost instanceof ReactHostImpl) {
|
|
201
|
+
ReactHostDelegate delegate = getReactHostDelegate((ReactHostImpl) reactHost);
|
|
202
|
+
if (delegate != null) {
|
|
203
|
+
// #2) Update the locally stored JS bundle file path
|
|
204
|
+
setJSBundle(delegate, latestJSBundleFile);
|
|
205
|
+
} else {
|
|
206
|
+
CodePushUtils.log("Could not get ReactHostDelegate from ReactHostImpl.");
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
CodePushUtils.log("ReactHost is not a direct ReactHostImpl instance (" + reactHost.getClass().getName() + "), skipping direct setJSBundle reflection attempt. This is expected with Expo.");
|
|
170
210
|
}
|
|
211
|
+
} catch (ClassCastException cce) {
|
|
212
|
+
CodePushUtils.log(new Exception("ClassCastException trying to get/use ReactHostDelegate. Skipping reflection call to setJSBundle. This is expected for Expo.", cce));
|
|
213
|
+
}catch (Exception e) {
|
|
214
|
+
// Catch any unexpected errors from the attempt to call setJSBundle, e.g., if getReactHostDelegate itself fails
|
|
215
|
+
CodePushUtils.log("Exception during the reflective setJSBundle block: " + e.getMessage());
|
|
171
216
|
}
|
|
172
|
-
});
|
|
173
217
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
218
|
+
// #3) Get the context creation method
|
|
219
|
+
try {
|
|
220
|
+
reactHost.reload("CodePush triggers reload");
|
|
221
|
+
mCodePush.initializeUpdateAfterRestart();
|
|
222
|
+
} catch (Exception e) {
|
|
223
|
+
// The recreation method threw an unknown exception
|
|
224
|
+
// so just simply fallback to restarting the Activity (if it exists)
|
|
225
|
+
loadBundleLegacy();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
} catch (Exception e) {
|
|
229
|
+
// reflection logic failed somewhere so fall back to restarting the Activity (if it exists)
|
|
230
|
+
CodePushUtils.log("Failed to load the bundle, falling back to restarting the Activity (if it exists). " + e.getMessage());
|
|
231
|
+
loadBundleLegacy();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
} else {
|
|
235
|
+
try {
|
|
236
|
+
DevSupportManager devSupportManager = null;
|
|
237
|
+
ReactInstanceManager reactInstanceManager = resolveInstanceManager();
|
|
238
|
+
if (reactInstanceManager != null) {
|
|
239
|
+
devSupportManager = reactInstanceManager.getDevSupportManager();
|
|
240
|
+
}
|
|
241
|
+
boolean isLiveReloadEnabled = isLiveReloadEnabled(devSupportManager);
|
|
242
|
+
|
|
243
|
+
mCodePush.clearDebugCacheIfNeeded(isLiveReloadEnabled);
|
|
244
|
+
} catch(Exception e) {
|
|
245
|
+
// If we got error in out reflection we should clear debug cache anyway.
|
|
246
|
+
mCodePush.clearDebugCacheIfNeeded(false);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
// #1) Get the ReactInstanceManager instance, which is what includes the
|
|
251
|
+
// logic to reload the current React context.
|
|
252
|
+
final ReactInstanceManager instanceManager = resolveInstanceManager();
|
|
253
|
+
if (instanceManager == null) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFileName());
|
|
258
|
+
|
|
259
|
+
// #2) Update the locally stored JS bundle file path
|
|
260
|
+
setJSBundle(instanceManager, latestJSBundleFile);
|
|
261
|
+
|
|
262
|
+
// #3) Get the context creation method and fire it on the UI thread (which RN enforces)
|
|
263
|
+
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
|
264
|
+
@Override
|
|
265
|
+
public void run() {
|
|
266
|
+
try {
|
|
267
|
+
// We don't need to resetReactRootViews anymore
|
|
268
|
+
// due the issue https://github.com/facebook/react-native/issues/14533
|
|
269
|
+
// has been fixed in RN 0.46.0
|
|
270
|
+
//resetReactRootViews(instanceManager);
|
|
271
|
+
|
|
272
|
+
instanceManager.recreateReactContextInBackground();
|
|
273
|
+
mCodePush.initializeUpdateAfterRestart();
|
|
274
|
+
} catch (Exception e) {
|
|
275
|
+
// The recreation method threw an unknown exception
|
|
276
|
+
// so just simply fallback to restarting the Activity (if it exists)
|
|
277
|
+
loadBundleLegacy();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
} catch (Exception e) {
|
|
283
|
+
// Our reflection logic failed somewhere
|
|
284
|
+
// so fall back to restarting the Activity (if it exists)
|
|
285
|
+
CodePushUtils.log("Failed to load the bundle, falling back to restarting the Activity (if it exists). " + e.getMessage());
|
|
286
|
+
loadBundleLegacy();
|
|
287
|
+
}
|
|
288
|
+
|
|
179
289
|
}
|
|
180
290
|
}
|
|
181
291
|
|
|
292
|
+
private boolean isLiveReloadEnabled(DevSupportManager devSupportManager) {
|
|
293
|
+
if (devSupportManager != null) {
|
|
294
|
+
DeveloperSettings devSettings = devSupportManager.getDevSettings();
|
|
295
|
+
Method[] methods = devSettings.getClass().getMethods();
|
|
296
|
+
for (Method m : methods) {
|
|
297
|
+
if (m.getName().equals("isReloadOnJSChangeEnabled")) {
|
|
298
|
+
try {
|
|
299
|
+
return (boolean) m.invoke(devSettings);
|
|
300
|
+
} catch (Exception x) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
|
|
182
310
|
// This workaround has been implemented in order to fix https://github.com/facebook/react-native/issues/14533
|
|
183
311
|
// resetReactRootViews allows to call recreateReactContextInBackground without any exceptions
|
|
184
312
|
// This fix also relates to https://github.com/microsoft/react-native-code-push/issues/878
|
|
@@ -208,7 +336,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
|
|
|
208
336
|
return instanceManager;
|
|
209
337
|
}
|
|
210
338
|
|
|
211
|
-
final Activity currentActivity = getCurrentActivity();
|
|
339
|
+
final Activity currentActivity = getReactApplicationContext().getCurrentActivity();
|
|
212
340
|
if (currentActivity == null) {
|
|
213
341
|
return null;
|
|
214
342
|
}
|
|
@@ -219,6 +347,21 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
|
|
|
219
347
|
return instanceManager;
|
|
220
348
|
}
|
|
221
349
|
|
|
350
|
+
private ReactHost resolveReactHost() throws NoSuchFieldException, IllegalAccessException {
|
|
351
|
+
ReactHost reactHost = CodePush.getReactHost();
|
|
352
|
+
if (reactHost != null) {
|
|
353
|
+
return reactHost;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
final Activity currentActivity = getReactApplicationContext().getCurrentActivity();
|
|
357
|
+
if (currentActivity == null) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
ReactApplication reactApplication = (ReactApplication) currentActivity.getApplication();
|
|
362
|
+
return reactApplication.getReactHost();
|
|
363
|
+
}
|
|
364
|
+
|
|
222
365
|
private void restartAppInternal(boolean onlyIfUpdateIsPending) {
|
|
223
366
|
if (this._restartInProgress) {
|
|
224
367
|
CodePushUtils.log("Restart request queued until the current restart is completed");
|
|
@@ -320,7 +463,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
|
|
|
320
463
|
getReactApplicationContext().runOnUiQueueThread(new Runnable() {
|
|
321
464
|
@Override
|
|
322
465
|
public void run() {
|
|
323
|
-
ReactChoreographer.getInstance().postFrameCallback(ReactChoreographer.CallbackType.TIMERS_EVENTS, new
|
|
466
|
+
ReactChoreographer.getInstance().postFrameCallback(ReactChoreographer.CallbackType.TIMERS_EVENTS, new Choreographer.FrameCallback() {
|
|
324
467
|
@Override
|
|
325
468
|
public void doFrame(long frameTimeNanos) {
|
|
326
469
|
if (!latestDownloadProgress.isCompleted()) {
|
|
@@ -711,4 +854,18 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
|
|
|
711
854
|
public void removeListeners(Integer count) {
|
|
712
855
|
// Remove upstream listeners, stop unnecessary background tasks
|
|
713
856
|
}
|
|
857
|
+
|
|
858
|
+
public ReactHostDelegate getReactHostDelegate(ReactHostImpl reactHostImpl) {
|
|
859
|
+
try {
|
|
860
|
+
Class<?> clazz = reactHostImpl.getClass();
|
|
861
|
+
Field field = clazz.getDeclaredField("mReactHostDelegate");
|
|
862
|
+
field.setAccessible(true);
|
|
863
|
+
|
|
864
|
+
// Get the value of the field for the provided instance
|
|
865
|
+
return (ReactHostDelegate) field.get(reactHostImpl);
|
|
866
|
+
} catch (NoSuchFieldException | IllegalAccessException e) {
|
|
867
|
+
e.printStackTrace();
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
714
871
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
package com.microsoft.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
|
+
}
|
package/android/codepush.gradle
CHANGED
|
@@ -69,9 +69,9 @@ gradle.projectsEvaluated {
|
|
|
69
69
|
|
|
70
70
|
def nodeModulesPath;
|
|
71
71
|
if (project.hasProperty('nodeModulesPath')) {
|
|
72
|
-
nodeModulesPath = "${project.nodeModulesPath}/react-native-code-push"
|
|
72
|
+
nodeModulesPath = "${project.nodeModulesPath}/@turbopush/react-native-code-push"
|
|
73
73
|
} else {
|
|
74
|
-
nodeModulesPath = findNodeModulePath(projectDir, "react-native-code-push")
|
|
74
|
+
nodeModulesPath = findNodeModulePath(projectDir, "@turbopush/react-native-code-push")
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
def targetName = variant.name.capitalize()
|
package/docs/api-android.md
CHANGED
|
@@ -1,83 +1,52 @@
|
|
|
1
|
-
###
|
|
2
|
-
|
|
3
|
-
### API for React Native 0.60 version and above
|
|
1
|
+
### Resource Configuration
|
|
4
2
|
|
|
5
3
|
Since `autolinking` uses `react-native.config.js` to link plugins, constructors are specified in that file. But you can override custom variables to manage the CodePush plugin by placing these values in string resources.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
```xml
|
|
10
|
-
<string moduleConfig="true" name="CodePushPublicKey">your-public-key</string>
|
|
11
|
-
```
|
|
5
|
+
- **Public Key** - used for bundle verification in the Code Signing Feature. Please refer to [Code Signing](setup-android.md#code-signing-setup) section for more details about the Code Signing Feature.
|
|
6
|
+
To set the public key, you should add the content of the public key to `strings.xml` with name `CodePushPublicKey`. CodePush automatically gets this property and enables the Code Signing feature. For example:
|
|
12
7
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<string moduleConfig="true" name="CodePushServerUrl">https://yourcodepush.server.com</string>
|
|
17
|
-
```
|
|
8
|
+
```xml
|
|
9
|
+
<string moduleConfig="true" name="CodePushPublicKey">your-public-key</string>
|
|
10
|
+
```
|
|
18
11
|
|
|
19
|
-
|
|
12
|
+
- **Server Url** - used for specifying CodePush Server Url.
|
|
13
|
+
The Default value: "https://codepush.appcenter.ms/" is overridden by adding your path to `strings.xml` with name `CodePushServerUrl`. CodePush automatically gets this property and will use this path to send requests. For example:
|
|
14
|
+
```xml
|
|
15
|
+
<string moduleConfig="true" name="CodePushServerUrl">https://yourcodepush.server.com</string>
|
|
16
|
+
```
|
|
20
17
|
|
|
21
18
|
The Java API is made available by importing the `com.microsoft.codepush.react.CodePush` class into your `MainActivity.java` file, and consists of a single public class named `CodePush`.
|
|
22
19
|
|
|
20
|
+
### Java API Reference (Android)
|
|
21
|
+
|
|
23
22
|
#### CodePush
|
|
24
23
|
|
|
25
24
|
Constructs the CodePush client runtime and represents the `ReactPackage` instance that you add to you app's list of packages.
|
|
26
25
|
|
|
27
26
|
##### Constructors
|
|
28
27
|
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
- __CodePush(String deploymentKey, Activity mainActivity, bool isDebugMode)__ - Equivalent to the previous constructor but allows you to specify whether you want the CodePush runtime to be in debug mode or not. When using this constructor, the `isDebugMode` parameter should always be set to `BuildConfig.DEBUG` in order to stay synchronized with your build type. When putting CodePush into debug mode, the following behaviors are enabled:
|
|
32
|
-
|
|
33
|
-
1. Old CodePush updates aren't deleted from storage whenever a new binary is deployed to the emulator/device. This behavior enables you to deploy new binaries, without bumping the version during development, and without continuously getting the same update every time your app calls `sync`.
|
|
34
|
-
|
|
35
|
-
2. The local cache that the React Native runtime maintains in debug mode is deleted whenever a CodePush update is installed. This ensures that when the app is restarted after an update is applied, you will see the expected changes. As soon as [this PR](https://github.com/facebook/react-native/pull/4738) is merged, we won't need to do this anymore.
|
|
36
|
-
|
|
37
|
-
- __CodePush(String deploymentKey, Context context, boolean isDebugMode, Integer publicKeyResourceDescriptor)__ - Equivalent to the previous constructor, but allows you to specify the public key resource descriptor needed to read public key content. Please refer to [Code Signing](setup-android.md#code-signing-setup) section for more details about the Code Signing Feature.
|
|
38
|
-
|
|
39
|
-
- __CodePush(String deploymentKey, Context context, boolean isDebugMode, String serverUrl)__ Constructor allows you to specify CodePush Server Url. The Default value: `"https://codepush.appcenter.ms/"` is overridden by value specified in `serverUrl`.
|
|
40
|
-
|
|
41
|
-
##### Builder
|
|
42
|
-
|
|
43
|
-
As an alternative to constructors *you can also use `CodePushBuilder`* to setup a CodePush instance configured with *only parameters you want*.
|
|
44
|
-
|
|
45
|
-
```java
|
|
46
|
-
@Override
|
|
47
|
-
protected List<ReactPackage> getPackages() {
|
|
48
|
-
return Arrays.<ReactPackage>asList(
|
|
49
|
-
new MainReactPackage(),
|
|
50
|
-
new CodePushBuilder("deployment-key-here",getApplicationContext())
|
|
51
|
-
.setIsDebugMode(BuildConfig.DEBUG)
|
|
52
|
-
.setPublicKeyResourceDescriptor(R.string.publicKey)
|
|
53
|
-
.setServerUrl("https://yourcodepush.server.com")
|
|
54
|
-
.build() //return configured CodePush instance
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
`CodePushBuilder` methods:
|
|
28
|
+
- **CodePush(String deploymentKey, Activity mainActivity)** - Creates a new instance of the CodePush runtime, that will be used to query the service for updates via the provided deployment key. The `mainActivity` parameter should always be set to `this` when configuring your React packages list inside the `MainActivity` class. This constructor puts the CodePush runtime into "release mode", so if you want to enable debugging behavior, use the following constructor instead.
|
|
60
29
|
|
|
61
|
-
|
|
30
|
+
- **CodePush(String deploymentKey, Activity mainActivity, bool isDebugMode)** - Equivalent to the previous constructor but allows you to specify whether you want the CodePush runtime to be in debug mode or not. When using this constructor, the `isDebugMode` parameter should always be set to `BuildConfig.DEBUG` in order to stay synchronized with your build type. When putting CodePush into debug mode, the following behaviors are enabled:
|
|
62
31
|
|
|
63
|
-
|
|
32
|
+
1. Old CodePush updates aren't deleted from storage whenever a new binary is deployed to the emulator/device. This behavior enables you to deploy new binaries, without bumping the version during development, and without continuously getting the same update every time your app calls `sync`.
|
|
64
33
|
|
|
65
|
-
|
|
34
|
+
2. The local cache that the React Native runtime maintains in debug mode is deleted whenever a CodePush update is installed. This ensures that when the app is restarted after an update is applied, you will see the expected changes. As soon as [this PR](https://github.com/facebook/react-native/pull/4738) is merged, we won't need to do this anymore.
|
|
66
35
|
|
|
67
|
-
|
|
36
|
+
- **CodePush(String deploymentKey, Context context, boolean isDebugMode, Integer publicKeyResourceDescriptor)** - Equivalent to the previous constructor, but allows you to specify the public key resource descriptor needed to read public key content. Please refer to [Code Signing](setup-android.md#code-signing-setup) section for more details about the Code Signing Feature.
|
|
68
37
|
|
|
69
|
-
|
|
38
|
+
- **CodePush(String deploymentKey, Context context, boolean isDebugMode, String serverUrl)** Constructor allows you to specify CodePush Server Url. The Default value: `"https://codepush.appcenter.ms/"` is overridden by value specified in `serverUrl`.
|
|
70
39
|
|
|
71
40
|
##### Public Methods
|
|
72
41
|
|
|
73
|
-
-
|
|
42
|
+
- **setDeploymentKey(String deploymentKey)** - Sets the deployment key that the app should use when querying for updates. This is a dynamic alternative to setting the deployment key in Codepush constructor/builder and/or specifying a deployment key in JS when calling `checkForUpdate` or `sync`.
|
|
74
43
|
|
|
75
44
|
##### Static Methods
|
|
76
45
|
|
|
77
|
-
-
|
|
46
|
+
- **getBundleUrl()** - Returns the path to the most recent version of your app's JS bundle file, assuming that the resource name is `index.android.bundle`. If your app is using a different bundle name, then use the overloaded version of this method which allows specifying it. This method has the same resolution behavior as the Objective-C equivalent described above.
|
|
78
47
|
|
|
79
|
-
-
|
|
48
|
+
- **getBundleUrl(String bundleName)** - Returns the path to the most recent version of your app's JS bundle file, using the specified resource name (like `index.android.bundle`). This method has the same resolution behavior as the Objective-C equivalent described above.
|
|
80
49
|
|
|
81
|
-
-
|
|
50
|
+
- **getPackageFolder()** - Returns the path to the current update folder.
|
|
82
51
|
|
|
83
|
-
-
|
|
52
|
+
- **overrideAppVersion(String appVersionOverride)** - Sets the version of the application's binary interface, which would otherwise default to the Play Store version specified as the `versionName` in the `build.gradle`. This should be called a single time, before the CodePush instance is constructed.
|
|
@@ -10,7 +10,7 @@ The [Android Gradle plugin](https://google.github.io/android-gradle-dsl/current/
|
|
|
10
10
|
|
|
11
11
|
To set this up, perform the following steps:
|
|
12
12
|
|
|
13
|
-
**For React Native >= v0.
|
|
13
|
+
**For React Native >= v0.76**
|
|
14
14
|
|
|
15
15
|
1. Open the project's app level `build.gradle` file (for example `android/app/build.gradle` in standard React Native projects)
|
|
16
16
|
|
|
@@ -51,98 +51,5 @@ To set this up, perform the following steps:
|
|
|
51
51
|
|
|
52
52
|
*NOTE: The naming convention for `releaseStaging` is significant due to [this line](https://github.com/facebook/react-native/blob/e083f9a139b3f8c5552528f8f8018529ef3193b9/react.gradle#L79).*
|
|
53
53
|
|
|
54
|
-
**For React Native v0.29 - v0.59**
|
|
55
|
-
|
|
56
|
-
1. Open up your `MainApplication.java` file and make the following changes:
|
|
57
|
-
|
|
58
|
-
```java
|
|
59
|
-
@Override
|
|
60
|
-
protected List<ReactPackage> getPackages() {
|
|
61
|
-
return Arrays.<ReactPackage>asList(
|
|
62
|
-
...
|
|
63
|
-
new CodePush(BuildConfig.CODEPUSH_KEY, MainApplication.this, BuildConfig.DEBUG), // Add/change this line.
|
|
64
|
-
...
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
2. Open your app's `build.gradle` file (for example `android/app/build.gradle` in standard React Native projects)
|
|
70
|
-
|
|
71
|
-
3. Find the `android { buildTypes {} }` section and define `buildConfigField` entries for both your `debug` and `release` build types, which reference your `Staging` and `Production` deployment keys respectively. If you prefer, you can define the key literals in your `gradle.properties` file, and then reference them here. Either way will work, and it's just a matter of personal preference.
|
|
72
|
-
|
|
73
|
-
```groovy
|
|
74
|
-
android {
|
|
75
|
-
...
|
|
76
|
-
buildTypes {
|
|
77
|
-
debug {
|
|
78
|
-
...
|
|
79
|
-
// Note: CodePush updates should not be tested in Debug mode as they are overriden by the RN packager. However, because CodePush checks for updates in all modes, we must supply a key.
|
|
80
|
-
buildConfigField "String", "CODEPUSH_KEY", '""'
|
|
81
|
-
...
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
releaseStaging {
|
|
85
|
-
...
|
|
86
|
-
buildConfigField "String", "CODEPUSH_KEY", '"<INSERT_STAGING_KEY>"'
|
|
87
|
-
// Note: It is a good idea to provide matchingFallbacks for the new buildType you create to prevent build issues
|
|
88
|
-
// Add the following line if not already there
|
|
89
|
-
matchingFallbacks = ['release']
|
|
90
|
-
...
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
release {
|
|
94
|
-
...
|
|
95
|
-
buildConfigField "String", "CODEPUSH_KEY", '"<INSERT_PRODUCTION_KEY>"'
|
|
96
|
-
...
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
...
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
*NOTE: The naming convention for `releaseStaging` is significant due to [this line](https://github.com/facebook/react-native/blob/e083f9a139b3f8c5552528f8f8018529ef3193b9/react.gradle#L79).*
|
|
104
|
-
|
|
105
|
-
4. Pass the deployment key to the `CodePush` constructor via the build config you defined, as opposed to a string literal.
|
|
106
|
-
|
|
107
|
-
**For React Native v0.19 - v0.28**
|
|
108
|
-
|
|
109
|
-
Open up your `MainActivity.java` file and make the following changes:
|
|
110
|
-
|
|
111
|
-
```java
|
|
112
|
-
@Override
|
|
113
|
-
protected List<ReactPackage> getPackages() {
|
|
114
|
-
return Arrays.<ReactPackage>asList(
|
|
115
|
-
...
|
|
116
|
-
new CodePush(BuildConfig.CODEPUSH_KEY, this, BuildConfig.DEBUG), // Add/change this line.
|
|
117
|
-
...
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
*Note: If you gave your build setting a different name in your Gradle file, simply make sure to reflect that in your Java code.*
|
|
123
|
-
|
|
124
|
-
And that's it! Now when you run or build your app, your debug builds will automatically be configured to sync with your `Staging` deployment, and your release builds will be configured to sync with your `Production` deployment.
|
|
125
|
-
|
|
126
|
-
*NOTE: By default, the `react-native run-android` command builds and deploys the debug version of your app, so if you want to test out a release/production build, simply run `react-native run-android --variant release. Refer to the [React Native docs](http://facebook.github.io/react-native/docs/signed-apk-android.html#conten) for details about how to configure and create release builds for your Android apps.*
|
|
127
|
-
|
|
128
|
-
If you want to be able to install both debug and release builds simultaneously on the same device (highly recommended!), then you need to ensure that your debug build has a unique identity and icon from your release build. Otherwise, neither the OS nor you will be able to differentiate between the two. You can achieve this by performing the following steps:
|
|
129
|
-
|
|
130
|
-
1. In your `build.gradle` file, specify the [`applicationIdSuffix`](http://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.BuildType.html#com.android.build.gradle.internal.dsl.BuildType:applicationIdSuffix) field for your debug build type, which gives your debug build a unique identity for the OS (like `com.foo` vs. `com.foo.debug`).
|
|
131
|
-
|
|
132
|
-
```groovy
|
|
133
|
-
buildTypes {
|
|
134
|
-
debug {
|
|
135
|
-
applicationIdSuffix ".debug"
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
2. Create the `app/src/debug/res` directory structure in your app, which allows overriding resources (like strings, icons, layouts) for your debug builds
|
|
141
|
-
|
|
142
|
-
3. Create a `values` directory underneath the debug res directory created in #2, and copy the existing `strings.xml` file from the `app/src/main/res/values` directory
|
|
143
|
-
|
|
144
|
-
4. Open up the new debug `strings.xml` file and change the `<string name="app_name">` element's value to something else (like `foo-debug`). This ensures that your debug build now has a distinct display name, so that you can differentiate it from your release build.
|
|
145
|
-
|
|
146
|
-
5. Optionally, create "mirrored" directories in the `app/src/debug/res` directory for all of your app's icons that you want to change for your debug build. This part isn't technically critical, but it can make it easier to quickly spot your debug builds on a device if its icon is noticeable different.
|
|
147
54
|
|
|
148
55
|
And that's it! View [here](http://tools.android.com/tech-docs/new-build-system/resource-merging) for more details on how resource merging works in Android.
|