@appcircle/react-native-code-push 0.0.4 → 0.1.0-beta.1
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/CodePush.podspec +1 -0
- package/README.md +10 -0
- package/android/app/build.gradle +5 -5
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +8 -13
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java +74 -35
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateManager.java +49 -48
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateUtils.java +3 -6
- package/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java +16 -9
- package/android/app/src/main/java/com/microsoft/codepush/react/ReactInstanceHolder.java +0 -3
- package/android/build.gradle +3 -4
- package/android/gradle/wrapper/gradle-wrapper.properties +1 -1
- package/android/gradle.properties +0 -2
- package/android/settings.gradle +2 -1
- package/ios/CodePush/CodePush.m +14 -5
- package/package.json +4 -3
- package/react-native.config.js +1 -1
- package/scripts/tools/linkToolsAndroid.js +1 -1
package/CodePush.podspec
CHANGED
|
@@ -16,6 +16,7 @@ Pod::Spec.new do |s|
|
|
|
16
16
|
s.library = 'z'
|
|
17
17
|
s.source_files = 'ios/CodePush/*.{h,m}'
|
|
18
18
|
s.public_header_files = ['ios/CodePush/CodePush.h']
|
|
19
|
+
s.pod_target_xcconfig = { "DEFINES_MODULE" => "YES" }
|
|
19
20
|
|
|
20
21
|
# Note: Even though there are copy/pasted versions of some of these dependencies in the repo,
|
|
21
22
|
# we explicitly let CocoaPods pull in the versions below so all dependencies are resolved and
|
package/README.md
CHANGED
|
@@ -37,6 +37,16 @@ Run the following command to add the Appcircle CodePush SDK to your project.
|
|
|
37
37
|
npm install @appcircle/react-native-code-push
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
## Supported Versions
|
|
41
|
+
|
|
42
|
+
| React Native version(s) | Supporting CodePush version(s) |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `<v0.76` | Consider `microsoft/code-push-react-native` |
|
|
45
|
+
| `v0.76`, `v0.77`, `v0.78`, `v0.79` | `v0.0.3+` (Available for Old/New Architecture) |
|
|
46
|
+
| `v0.80` | `v0.0.4+` (Available for Old/New Architecture) |
|
|
47
|
+
| `v0.81+` | `v0.1.0+` (Available for Old/New Architecture) |
|
|
48
|
+
| `v0.82+` | `v0.1.0+` (Available for New Architecture) |
|
|
49
|
+
|
|
40
50
|
## Platform Setup
|
|
41
51
|
|
|
42
52
|
### iOS Setup
|
package/android/app/build.gradle
CHANGED
|
@@ -10,10 +10,6 @@ def isNewArchitectureEnabled() {
|
|
|
10
10
|
|
|
11
11
|
def IS_NEW_ARCHITECTURE_ENABLED = isNewArchitectureEnabled()
|
|
12
12
|
|
|
13
|
-
if (IS_NEW_ARCHITECTURE_ENABLED) {
|
|
14
|
-
apply plugin: "com.facebook.react"
|
|
15
|
-
}
|
|
16
|
-
|
|
17
13
|
def DEFAULT_COMPILE_SDK_VERSION = 26
|
|
18
14
|
def DEFAULT_BUILD_TOOLS_VERSION = "26.0.3"
|
|
19
15
|
def DEFAULT_TARGET_SDK_VERSION = 26
|
|
@@ -40,9 +36,13 @@ android {
|
|
|
40
36
|
defaultConfig {
|
|
41
37
|
consumerProguardFiles 'proguard-rules.pro'
|
|
42
38
|
}
|
|
39
|
+
|
|
40
|
+
buildFeatures {
|
|
41
|
+
buildConfig true
|
|
42
|
+
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
dependencies {
|
|
46
|
-
implementation
|
|
46
|
+
implementation 'com.facebook.react:react-android:0.82.1'
|
|
47
47
|
implementation 'com.nimbusds:nimbus-jose-jwt:9.37.3'
|
|
48
48
|
}
|
|
@@ -141,10 +141,10 @@ public class CodePush implements ReactPackage {
|
|
|
141
141
|
|
|
142
142
|
private String getCustomPropertyFromStringsIfExist(String propertyName) {
|
|
143
143
|
String property;
|
|
144
|
-
|
|
144
|
+
|
|
145
145
|
String packageName = mContext.getPackageName();
|
|
146
146
|
int resId = mContext.getResources().getIdentifier("CodePush" + propertyName, "string", packageName);
|
|
147
|
-
|
|
147
|
+
|
|
148
148
|
if (resId != 0) {
|
|
149
149
|
property = mContext.getString(resId);
|
|
150
150
|
|
|
@@ -152,7 +152,7 @@ public class CodePush implements ReactPackage {
|
|
|
152
152
|
return property;
|
|
153
153
|
} else {
|
|
154
154
|
CodePushUtils.log("Specified " + propertyName + " is empty");
|
|
155
|
-
}
|
|
155
|
+
}
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
return null;
|
|
@@ -402,10 +402,6 @@ public class CodePush implements ReactPackage {
|
|
|
402
402
|
mReactInstanceHolder = reactInstanceHolder;
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
-
public static void setReactHost(ReactHostHolder reactHostHolder) {
|
|
406
|
-
mReactHostHolder = reactHostHolder;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
405
|
static ReactInstanceManager getReactInstanceManager() {
|
|
410
406
|
if (mReactInstanceHolder == null) {
|
|
411
407
|
return null;
|
|
@@ -413,6 +409,10 @@ public class CodePush implements ReactPackage {
|
|
|
413
409
|
return mReactInstanceHolder.getReactInstanceManager();
|
|
414
410
|
}
|
|
415
411
|
|
|
412
|
+
public static void setReactHost(ReactHostHolder reactHostHolder) {
|
|
413
|
+
mReactHostHolder = reactHostHolder;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
416
|
static ReactHost getReactHost() {
|
|
417
417
|
if (mReactHostHolder == null) {
|
|
418
418
|
return null;
|
|
@@ -432,13 +432,8 @@ public class CodePush implements ReactPackage {
|
|
|
432
432
|
return nativeModules;
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
-
// Deprecated in RN v0.47.
|
|
436
|
-
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
|
437
|
-
return new ArrayList<>();
|
|
438
|
-
}
|
|
439
|
-
|
|
440
435
|
@Override
|
|
441
436
|
public List<ViewManager> createViewManagers(ReactApplicationContext reactApplicationContext) {
|
|
442
437
|
return new ArrayList<>();
|
|
443
438
|
}
|
|
444
|
-
}
|
|
439
|
+
}
|
|
@@ -118,6 +118,25 @@ public class CodePushNativeModule extends BaseJavaModule {
|
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
private Field findBundleLoaderField(Class<?> delegateClass) {
|
|
122
|
+
Class<?> currentClass = delegateClass;
|
|
123
|
+
while (currentClass != null) {
|
|
124
|
+
try {
|
|
125
|
+
return currentClass.getDeclaredField("jsBundleLoader");
|
|
126
|
+
} catch (NoSuchFieldException ignored) {
|
|
127
|
+
// Continue searching the class hierarchy and alternate field names.
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
return currentClass.getDeclaredField("_jsBundleLoader");
|
|
132
|
+
} catch (NoSuchFieldException ignored) {
|
|
133
|
+
currentClass = currentClass.getSuperclass();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
121
140
|
// Use reflection to find and set the appropriate fields on ReactInstanceManager. See #556 for a proposal for a less brittle way
|
|
122
141
|
// to approach this.
|
|
123
142
|
private void setJSBundle(ReactInstanceManager instanceManager, String latestJSBundleFile) throws IllegalAccessException {
|
|
@@ -149,10 +168,17 @@ public class CodePushNativeModule extends BaseJavaModule {
|
|
|
149
168
|
latestJSBundleLoader = JSBundleLoader.createFileLoader(latestJSBundleFile);
|
|
150
169
|
}
|
|
151
170
|
|
|
152
|
-
Field bundleLoaderField = reactHostDelegate.getClass()
|
|
171
|
+
Field bundleLoaderField = findBundleLoaderField(reactHostDelegate.getClass());
|
|
172
|
+
if (bundleLoaderField == null) {
|
|
173
|
+
throw new NoSuchFieldException("jsBundleLoader");
|
|
174
|
+
}
|
|
153
175
|
bundleLoaderField.setAccessible(true);
|
|
154
176
|
bundleLoaderField.set(reactHostDelegate, latestJSBundleLoader);
|
|
155
|
-
|
|
177
|
+
|
|
178
|
+
} catch (NoSuchFieldException nsfe) {
|
|
179
|
+
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");
|
|
180
|
+
// DO NOT THROW for NoSuchFieldException.
|
|
181
|
+
}catch (Exception e) {
|
|
156
182
|
CodePushUtils.log("Unable to set JSBundle of ReactHostDelegate - CodePush may not support this version of React Native");
|
|
157
183
|
throw new IllegalAccessException("Could not setJSBundle");
|
|
158
184
|
}
|
|
@@ -177,18 +203,38 @@ public class CodePushNativeModule extends BaseJavaModule {
|
|
|
177
203
|
mCodePush.clearDebugCacheIfNeeded(false);
|
|
178
204
|
}
|
|
179
205
|
|
|
206
|
+
|
|
180
207
|
try {
|
|
181
208
|
// #1) Get the ReactHost instance, which is what includes the
|
|
182
209
|
// logic to reload the current React context.
|
|
183
210
|
final ReactHost reactHost = resolveReactHost();
|
|
184
211
|
if (reactHost == null) {
|
|
212
|
+
loadBundleLegacy(); // Fallback if reactHost can't be resolved
|
|
185
213
|
return;
|
|
186
214
|
}
|
|
187
215
|
|
|
188
216
|
String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFileName());
|
|
217
|
+
CodePushUtils.log("[MyDebug] Latest JS bundle for New Arch: " + latestJSBundleFile);
|
|
189
218
|
|
|
190
|
-
|
|
191
|
-
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
if (reactHost instanceof ReactHostImpl) {
|
|
222
|
+
ReactHostDelegate delegate = getReactHostDelegate((ReactHostImpl) reactHost);
|
|
223
|
+
if (delegate != null) {
|
|
224
|
+
// #2) Update the locally stored JS bundle file path
|
|
225
|
+
setJSBundle(delegate, latestJSBundleFile);
|
|
226
|
+
} else {
|
|
227
|
+
CodePushUtils.log("Could not get ReactHostDelegate from ReactHostImpl.");
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
CodePushUtils.log("ReactHost is not a direct ReactHostImpl instance (" + reactHost.getClass().getName() + "), skipping direct setJSBundle reflection attempt. This is expected with Expo.");
|
|
231
|
+
}
|
|
232
|
+
} catch (ClassCastException cce) {
|
|
233
|
+
CodePushUtils.log(new Exception("ClassCastException trying to get/use ReactHostDelegate. Skipping reflection call to setJSBundle. This is expected for Expo.", cce));
|
|
234
|
+
}catch (Exception e) {
|
|
235
|
+
// Catch any unexpected errors from the attempt to call setJSBundle, e.g., if getReactHostDelegate itself fails
|
|
236
|
+
CodePushUtils.log("Exception during the reflective setJSBundle block: " + e.getMessage());
|
|
237
|
+
}
|
|
192
238
|
|
|
193
239
|
// #3) Get the context creation method
|
|
194
240
|
try {
|
|
@@ -201,8 +247,7 @@ public class CodePushNativeModule extends BaseJavaModule {
|
|
|
201
247
|
}
|
|
202
248
|
|
|
203
249
|
} catch (Exception e) {
|
|
204
|
-
//
|
|
205
|
-
// so fall back to restarting the Activity (if it exists)
|
|
250
|
+
// reflection logic failed somewhere so fall back to restarting the Activity (if it exists)
|
|
206
251
|
CodePushUtils.log("Failed to load the bundle, falling back to restarting the Activity (if it exists). " + e.getMessage());
|
|
207
252
|
loadBundleLegacy();
|
|
208
253
|
}
|
|
@@ -241,11 +286,6 @@ public class CodePushNativeModule extends BaseJavaModule {
|
|
|
241
286
|
@Override
|
|
242
287
|
public void run() {
|
|
243
288
|
try {
|
|
244
|
-
// We don't need to resetReactRootViews anymore
|
|
245
|
-
// due the issue https://github.com/facebook/react-native/issues/14533
|
|
246
|
-
// has been fixed in RN 0.46.0
|
|
247
|
-
//resetReactRootViews(instanceManager);
|
|
248
|
-
|
|
249
289
|
instanceManager.recreateReactContextInBackground();
|
|
250
290
|
mCodePush.initializeUpdateAfterRestart();
|
|
251
291
|
} catch (Exception e) {
|
|
@@ -284,20 +324,6 @@ public class CodePushNativeModule extends BaseJavaModule {
|
|
|
284
324
|
return false;
|
|
285
325
|
}
|
|
286
326
|
|
|
287
|
-
// This workaround has been implemented in order to fix https://github.com/facebook/react-native/issues/14533
|
|
288
|
-
// resetReactRootViews allows to call recreateReactContextInBackground without any exceptions
|
|
289
|
-
// This fix also relates to https://github.com/microsoft/react-native-code-push/issues/878
|
|
290
|
-
private void resetReactRootViews(ReactInstanceManager instanceManager) throws NoSuchFieldException, IllegalAccessException {
|
|
291
|
-
Field mAttachedRootViewsField = instanceManager.getClass().getDeclaredField("mAttachedRootViews");
|
|
292
|
-
mAttachedRootViewsField.setAccessible(true);
|
|
293
|
-
List<ReactRootView> mAttachedRootViews = (List<ReactRootView>)mAttachedRootViewsField.get(instanceManager);
|
|
294
|
-
for (ReactRootView reactRootView : mAttachedRootViews) {
|
|
295
|
-
reactRootView.removeAllViews();
|
|
296
|
-
reactRootView.setId(View.NO_ID);
|
|
297
|
-
}
|
|
298
|
-
mAttachedRootViewsField.set(instanceManager, mAttachedRootViews);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
327
|
private void clearLifecycleEventListener() {
|
|
302
328
|
// Remove LifecycleEventListener to prevent infinite restart loop
|
|
303
329
|
if (mLifecycleEventListener != null) {
|
|
@@ -833,16 +859,29 @@ public class CodePushNativeModule extends BaseJavaModule {
|
|
|
833
859
|
}
|
|
834
860
|
|
|
835
861
|
public ReactHostDelegate getReactHostDelegate(ReactHostImpl reactHostImpl) {
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
862
|
+
Class<?> currentClass = reactHostImpl.getClass();
|
|
863
|
+
|
|
864
|
+
// Traverse up the class hierarchy in case the delegate field is declared in a parent.
|
|
865
|
+
while (currentClass != null) {
|
|
866
|
+
Field[] fields = currentClass.getDeclaredFields();
|
|
867
|
+
|
|
868
|
+
for (Field field : fields) {
|
|
869
|
+
try {
|
|
870
|
+
field.setAccessible(true);
|
|
871
|
+
Object value = field.get(reactHostImpl);
|
|
872
|
+
|
|
873
|
+
if (value instanceof ReactHostDelegate) {
|
|
874
|
+
return (ReactHostDelegate) value;
|
|
875
|
+
}
|
|
876
|
+
} catch (Exception ignored) {
|
|
877
|
+
// Keep scanning fields.
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
currentClass = currentClass.getSuperclass();
|
|
846
882
|
}
|
|
883
|
+
|
|
884
|
+
CodePushUtils.log("Reflection failed: Could not find any field of type ReactHostDelegate in ReactHostImpl.");
|
|
885
|
+
return null;
|
|
847
886
|
}
|
|
848
887
|
}
|
|
@@ -157,6 +157,9 @@ public class CodePushUpdateManager {
|
|
|
157
157
|
|
|
158
158
|
String downloadUrlString = updatePackage.optString(CodePushConstants.DOWNLOAD_URL_KEY, null);
|
|
159
159
|
HttpURLConnection connection = null;
|
|
160
|
+
BufferedInputStream bin = null;
|
|
161
|
+
FileOutputStream fos = null;
|
|
162
|
+
BufferedOutputStream bout = null;
|
|
160
163
|
File downloadFile = null;
|
|
161
164
|
boolean isZip = false;
|
|
162
165
|
|
|
@@ -175,51 +178,52 @@ public class CodePushUpdateManager {
|
|
|
175
178
|
}
|
|
176
179
|
|
|
177
180
|
connection.setRequestProperty("Accept-Encoding", "identity");
|
|
181
|
+
bin = new BufferedInputStream(connection.getInputStream());
|
|
178
182
|
|
|
179
183
|
long totalBytes = connection.getContentLength();
|
|
180
184
|
long receivedBytes = 0;
|
|
181
185
|
|
|
182
186
|
File downloadFolder = new File(getCodePushPath());
|
|
183
187
|
downloadFolder.mkdirs();
|
|
184
|
-
|
|
185
188
|
downloadFile = new File(downloadFolder, CodePushConstants.DOWNLOAD_FILE_NAME);
|
|
189
|
+
fos = new FileOutputStream(downloadFile);
|
|
190
|
+
bout = new BufferedOutputStream(fos, CodePushConstants.DOWNLOAD_BUFFER_SIZE);
|
|
186
191
|
byte[] data = new byte[CodePushConstants.DOWNLOAD_BUFFER_SIZE];
|
|
187
192
|
byte[] header = new byte[4];
|
|
188
193
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (receivedBytes < 4) {
|
|
197
|
-
for (int i = 0; i < numBytesRead; i++) {
|
|
198
|
-
int headerOffset = (int) (receivedBytes) + i;
|
|
199
|
-
if (headerOffset >= 4) {
|
|
200
|
-
break;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
header[headerOffset] = data[i];
|
|
194
|
+
int numBytesRead = 0;
|
|
195
|
+
while ((numBytesRead = bin.read(data, 0, CodePushConstants.DOWNLOAD_BUFFER_SIZE)) >= 0) {
|
|
196
|
+
if (receivedBytes < 4) {
|
|
197
|
+
for (int i = 0; i < numBytesRead; i++) {
|
|
198
|
+
int headerOffset = (int) (receivedBytes) + i;
|
|
199
|
+
if (headerOffset >= 4) {
|
|
200
|
+
break;
|
|
204
201
|
}
|
|
205
|
-
}
|
|
206
202
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
progressCallback.call(new DownloadProgress(totalBytes, receivedBytes));
|
|
203
|
+
header[headerOffset] = data[i];
|
|
204
|
+
}
|
|
210
205
|
}
|
|
211
206
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
207
|
+
receivedBytes += numBytesRead;
|
|
208
|
+
bout.write(data, 0, numBytesRead);
|
|
209
|
+
progressCallback.call(new DownloadProgress(totalBytes, receivedBytes));
|
|
210
|
+
}
|
|
215
211
|
|
|
216
|
-
|
|
212
|
+
if (totalBytes != receivedBytes) {
|
|
213
|
+
throw new CodePushUnknownException("Received " + receivedBytes + " bytes, expected " + totalBytes);
|
|
217
214
|
}
|
|
215
|
+
|
|
216
|
+
isZip = ByteBuffer.wrap(header).getInt() == 0x504b0304;
|
|
218
217
|
} catch (MalformedURLException e) {
|
|
219
218
|
throw new CodePushMalformedDataException(downloadUrlString, e);
|
|
220
219
|
} finally {
|
|
221
|
-
|
|
222
|
-
|
|
220
|
+
try {
|
|
221
|
+
if (bout != null) bout.close();
|
|
222
|
+
if (fos != null) fos.close();
|
|
223
|
+
if (bin != null) bin.close();
|
|
224
|
+
if (connection != null) connection.disconnect();
|
|
225
|
+
} catch (IOException e) {
|
|
226
|
+
throw new CodePushUnknownException("Error closing IO resources.", e);
|
|
223
227
|
}
|
|
224
228
|
}
|
|
225
229
|
|
|
@@ -343,35 +347,32 @@ public class CodePushUpdateManager {
|
|
|
343
347
|
public void downloadAndReplaceCurrentBundle(String remoteBundleUrl, String bundleFileName) throws IOException {
|
|
344
348
|
URL downloadUrl;
|
|
345
349
|
HttpURLConnection connection = null;
|
|
346
|
-
|
|
350
|
+
BufferedInputStream bin = null;
|
|
351
|
+
FileOutputStream fos = null;
|
|
352
|
+
BufferedOutputStream bout = null;
|
|
347
353
|
try {
|
|
348
354
|
downloadUrl = new URL(remoteBundleUrl);
|
|
349
355
|
connection = (HttpURLConnection) (downloadUrl.openConnection());
|
|
350
|
-
|
|
356
|
+
bin = new BufferedInputStream(connection.getInputStream());
|
|
351
357
|
File downloadFile = new File(getCurrentPackageBundlePath(bundleFileName));
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
BufferedOutputStream bout = new BufferedOutputStream(fos, CodePushConstants.DOWNLOAD_BUFFER_SIZE)
|
|
360
|
-
) {
|
|
361
|
-
byte[] data = new byte[CodePushConstants.DOWNLOAD_BUFFER_SIZE];
|
|
362
|
-
int numBytesRead;
|
|
363
|
-
while ((numBytesRead = bin.read(data, 0, CodePushConstants.DOWNLOAD_BUFFER_SIZE)) >= 0) {
|
|
364
|
-
bout.write(data, 0, numBytesRead);
|
|
365
|
-
}
|
|
358
|
+
downloadFile.delete();
|
|
359
|
+
fos = new FileOutputStream(downloadFile);
|
|
360
|
+
bout = new BufferedOutputStream(fos, CodePushConstants.DOWNLOAD_BUFFER_SIZE);
|
|
361
|
+
byte[] data = new byte[CodePushConstants.DOWNLOAD_BUFFER_SIZE];
|
|
362
|
+
int numBytesRead = 0;
|
|
363
|
+
while ((numBytesRead = bin.read(data, 0, CodePushConstants.DOWNLOAD_BUFFER_SIZE)) >= 0) {
|
|
364
|
+
bout.write(data, 0, numBytesRead);
|
|
366
365
|
}
|
|
367
|
-
|
|
368
366
|
} catch (MalformedURLException e) {
|
|
369
367
|
throw new CodePushMalformedDataException(remoteBundleUrl, e);
|
|
370
|
-
} catch (IOException e) {
|
|
371
|
-
throw new CodePushUnknownException("Error handling IO streams.", e);
|
|
372
368
|
} finally {
|
|
373
|
-
|
|
374
|
-
|
|
369
|
+
try {
|
|
370
|
+
if (bout != null) bout.close();
|
|
371
|
+
if (fos != null) fos.close();
|
|
372
|
+
if (bin != null) bin.close();
|
|
373
|
+
if (connection != null) connection.disconnect();
|
|
374
|
+
} catch (IOException e) {
|
|
375
|
+
throw new CodePushUnknownException("Error closing IO resources.", e);
|
|
375
376
|
}
|
|
376
377
|
}
|
|
377
378
|
}
|
|
@@ -379,4 +380,4 @@ public class CodePushUpdateManager {
|
|
|
379
380
|
public void clearUpdates() {
|
|
380
381
|
FileUtils.deleteDirectoryAtPath(getCodePushPath());
|
|
381
382
|
}
|
|
382
|
-
}
|
|
383
|
+
}
|
|
@@ -62,14 +62,11 @@ public class CodePushUpdateUtils {
|
|
|
62
62
|
if (file.isDirectory()) {
|
|
63
63
|
addContentsOfFolderToManifest(fullFilePath, relativePath, manifest);
|
|
64
64
|
} else {
|
|
65
|
-
try
|
|
66
|
-
manifest.add(relativePath + ":" + computeHash(
|
|
65
|
+
try {
|
|
66
|
+
manifest.add(relativePath + ":" + computeHash(new FileInputStream(file)));
|
|
67
67
|
} catch (FileNotFoundException e) {
|
|
68
68
|
// Should not happen.
|
|
69
69
|
throw new CodePushUnknownException("Unable to compute hash of update contents.", e);
|
|
70
|
-
} catch (IOException e) {
|
|
71
|
-
// Should not happen.
|
|
72
|
-
throw new CodePushUnknownException("Error occurred while reading file for hashing.", e);
|
|
73
70
|
}
|
|
74
71
|
}
|
|
75
72
|
}
|
|
@@ -275,4 +272,4 @@ public class CodePushUpdateUtils {
|
|
|
275
272
|
|
|
276
273
|
CodePushUtils.log("The update contents succeeded the code signing check.");
|
|
277
274
|
}
|
|
278
|
-
}
|
|
275
|
+
}
|
|
@@ -29,19 +29,26 @@ public class FileUtils {
|
|
|
29
29
|
CodePushUtils.appendPathComponent(destinationDirectoryPath, sourceFile.getName()));
|
|
30
30
|
} else {
|
|
31
31
|
File destFile = new File(destDir, sourceFile.getName());
|
|
32
|
+
FileInputStream fromFileStream = null;
|
|
33
|
+
BufferedInputStream fromBufferedStream = null;
|
|
34
|
+
FileOutputStream destStream = null;
|
|
32
35
|
byte[] buffer = new byte[WRITE_BUFFER_SIZE];
|
|
33
|
-
try
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
)
|
|
38
|
-
{
|
|
36
|
+
try {
|
|
37
|
+
fromFileStream = new FileInputStream(sourceFile);
|
|
38
|
+
fromBufferedStream = new BufferedInputStream(fromFileStream);
|
|
39
|
+
destStream = new FileOutputStream(destFile);
|
|
39
40
|
int bytesRead;
|
|
40
41
|
while ((bytesRead = fromBufferedStream.read(buffer)) > 0) {
|
|
41
42
|
destStream.write(buffer, 0, bytesRead);
|
|
42
43
|
}
|
|
43
|
-
}
|
|
44
|
-
|
|
44
|
+
} finally {
|
|
45
|
+
try {
|
|
46
|
+
if (fromFileStream != null) fromFileStream.close();
|
|
47
|
+
if (fromBufferedStream != null) fromBufferedStream.close();
|
|
48
|
+
if (destStream != null) destStream.close();
|
|
49
|
+
} catch (IOException e) {
|
|
50
|
+
throw new CodePushUnknownException("Error closing IO resources.", e);
|
|
51
|
+
}
|
|
45
52
|
}
|
|
46
53
|
}
|
|
47
54
|
}
|
|
@@ -193,4 +200,4 @@ public class FileUtils {
|
|
|
193
200
|
if (out != null) out.close();
|
|
194
201
|
}
|
|
195
202
|
}
|
|
196
|
-
}
|
|
203
|
+
}
|
package/android/build.gradle
CHANGED
|
@@ -6,7 +6,8 @@ buildscript {
|
|
|
6
6
|
mavenCentral()
|
|
7
7
|
}
|
|
8
8
|
dependencies {
|
|
9
|
-
classpath
|
|
9
|
+
classpath("com.android.tools.build:gradle:8.12.0")
|
|
10
|
+
classpath("com.facebook.react:react-native-gradle-plugin")
|
|
10
11
|
|
|
11
12
|
// NOTE: Do not place your application dependencies here; they belong
|
|
12
13
|
// in the individual module build.gradle files
|
|
@@ -14,10 +15,8 @@ buildscript {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
allprojects {
|
|
17
|
-
android {
|
|
18
|
-
namespace "com.microsoft.codepush.react"
|
|
19
|
-
}
|
|
20
18
|
repositories {
|
|
19
|
+
google()
|
|
21
20
|
mavenLocal()
|
|
22
21
|
mavenCentral()
|
|
23
22
|
}
|
|
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|
|
2
2
|
distributionPath=wrapper/dists
|
|
3
3
|
zipStoreBase=GRADLE_USER_HOME
|
|
4
4
|
zipStorePath=wrapper/dists
|
|
5
|
-
distributionUrl=https\://services.gradle.org/distributions/gradle-
|
|
5
|
+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip
|
package/android/settings.gradle
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
include ':app'
|
|
1
|
+
include ':app'
|
|
2
|
+
includeBuild('../node_modules/@react-native/gradle-plugin')
|
package/ios/CodePush/CodePush.m
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
long long _latestExpectedContentLength;
|
|
33
33
|
long long _latestReceivedConentLength;
|
|
34
34
|
BOOL _didUpdateProgress;
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
BOOL _allowed;
|
|
37
37
|
BOOL _restartInProgress;
|
|
38
38
|
NSMutableArray *_restartQueue;
|
|
@@ -377,7 +377,7 @@ static NSString *const LatestRollbackCountKey = @"count";
|
|
|
377
377
|
_allowed = YES;
|
|
378
378
|
_restartInProgress = NO;
|
|
379
379
|
_restartQueue = [NSMutableArray arrayWithCapacity:1];
|
|
380
|
-
|
|
380
|
+
|
|
381
381
|
self = [super init];
|
|
382
382
|
if (self) {
|
|
383
383
|
[self initializeUpdateAfterRestart];
|
|
@@ -538,10 +538,19 @@ static NSString *const LatestRollbackCountKey = @"count";
|
|
|
538
538
|
// file (since Chrome wouldn't support it). Otherwise, update
|
|
539
539
|
// the current bundle URL to point at the latest update
|
|
540
540
|
if ([CodePush isUsingTestConfiguration] || ![super.bridge.bundleURL.scheme hasPrefix:@"http"]) {
|
|
541
|
-
[
|
|
541
|
+
RCTReloadCommandSetBundleURL([CodePush bundleURL]);
|
|
542
|
+
// [super.bridge setValue:[CodePush bundleURL] forKey:@"bundleURL"];
|
|
542
543
|
}
|
|
543
544
|
|
|
544
|
-
|
|
545
|
+
// Add a small delay to ensure Fabric surface initialization is complete
|
|
546
|
+
// before triggering reload. This fixes a race condition in React Native 0.82+
|
|
547
|
+
// with New Architecture where RCTInstance invalidate and RCTFabricSurface start
|
|
548
|
+
// can run concurrently, causing a crash when MountingCoordinator's mutex is accessed
|
|
549
|
+
// after being destroyed.
|
|
550
|
+
//
|
|
551
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
552
|
+
RCTTriggerReloadCommandListeners(@"react-native-code-push: Restart");
|
|
553
|
+
});
|
|
545
554
|
});
|
|
546
555
|
}
|
|
547
556
|
|
|
@@ -744,8 +753,8 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
|
|
|
744
753
|
if (expectedContentLength == receivedContentLength) {
|
|
745
754
|
_didUpdateProgress = NO;
|
|
746
755
|
self.paused = YES;
|
|
747
|
-
[self dispatchDownloadProgressEvent];
|
|
748
756
|
}
|
|
757
|
+
[self dispatchDownloadProgressEvent];
|
|
749
758
|
}
|
|
750
759
|
// The download completed
|
|
751
760
|
doneCallback:^{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appcircle/react-native-code-push",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.1.0-beta.1",
|
|
4
4
|
"description": "React Native plugin for the CodePush service",
|
|
5
5
|
"main": "CodePush.js",
|
|
6
6
|
"typings": "typings/react-native-code-push.d.ts",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"xcode": "3.0.1"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
+
"react-native": "0.82.1",
|
|
56
57
|
"@types/assert": "^1.5.2",
|
|
57
58
|
"@types/mkdirp": "^1.0.1",
|
|
58
59
|
"@types/mocha": "^9.0.0",
|
|
@@ -83,8 +84,8 @@
|
|
|
83
84
|
]
|
|
84
85
|
},
|
|
85
86
|
"commands": {
|
|
86
|
-
"postlink": "node node_modules/react-native-code-push/scripts/postlink/run",
|
|
87
|
-
"postunlink": "node node_modules/react-native-code-push/scripts/postunlink/run"
|
|
87
|
+
"postlink": "node node_modules/@appcircle/react-native-code-push/scripts/postlink/run",
|
|
88
|
+
"postunlink": "node node_modules/@appcircle/react-native-code-push/scripts/postunlink/run"
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
}
|
package/react-native.config.js
CHANGED
|
@@ -13,7 +13,7 @@ exports.getJSBundleFileOverride = `
|
|
|
13
13
|
`;
|
|
14
14
|
exports.reactNativeHostInstantiation = "new ReactNativeHost(this) {";
|
|
15
15
|
exports.mainActivityClassDeclaration = "public class MainActivity extends ReactActivity {";
|
|
16
|
-
exports.codePushGradleLink = `\napply from: "../../node_modules/react-native-code-push/android/codepush.gradle"`;
|
|
16
|
+
exports.codePushGradleLink = `\napply from: "../../node_modules/@appcircle/react-native-code-push/android/codepush.gradle"`;
|
|
17
17
|
exports.deploymentKeyName = "CodePushDeploymentKey";
|
|
18
18
|
|
|
19
19
|
exports.getMainApplicationLocation = function () {
|