@capawesome/capacitor-live-update 6.7.2 → 7.0.0
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/CapawesomeCapacitorLiveUpdate.podspec +2 -2
- package/Package.swift +32 -0
- package/README.md +91 -81
- package/android/build.gradle +9 -9
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdate.java +165 -133
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdateConfig.java +6 -27
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdateHttpClient.java +7 -2
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java +50 -78
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePreferences.java +12 -5
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/api/GetLatestBundleResponse.java +9 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/events/DownloadBundleProgressEvent.java +32 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/options/DownloadBundleOptions.java +1 -10
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/results/FetchLatestBundleResult.java +13 -1
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/results/ReadyResult.java +32 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/interfaces/DownloadProgressCallback.java +5 -0
- package/dist/docs.json +191 -158
- package/dist/esm/definitions.d.ts +107 -52
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +2 -2
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +1 -3
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +1 -3
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/Classes/Events/DownloadBundleProgressEvent.swift +25 -0
- package/ios/Plugin/Classes/Options/DownloadBundleOptions.swift +1 -7
- package/ios/Plugin/Classes/Results/{FetchLatestBundle.swift → FetchLatestBundleResult.swift} +6 -1
- package/ios/Plugin/Classes/Results/ReadyResult.swift +22 -0
- package/ios/Plugin/LiveUpdate.swift +167 -154
- package/ios/Plugin/LiveUpdateConfig.swift +2 -4
- package/ios/Plugin/LiveUpdateHttpClient.swift +6 -2
- package/ios/Plugin/LiveUpdatePlugin.swift +44 -38
- package/ios/Plugin/LiveUpdatePreferences.swift +6 -20
- package/ios/Plugin/Protocols/Api/GetLatestBundleResponse.swift +1 -0
- package/package.json +17 -18
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/options/SetBundleOptions.java +0 -18
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/results/GetBundleResult.java +0 -24
- package/ios/Plugin/Classes/Options/SetBundleOptions.swift +0 -14
- package/ios/Plugin/Classes/Results/GetBundleResult.swift +0 -16
- package/ios/Plugin/LiveUpdatePlugin.h +0 -10
- package/ios/Plugin/LiveUpdatePlugin.m +0 -27
|
@@ -10,21 +10,21 @@ import android.os.Handler;
|
|
|
10
10
|
import android.util.Base64;
|
|
11
11
|
import androidx.annotation.NonNull;
|
|
12
12
|
import androidx.annotation.Nullable;
|
|
13
|
+
import com.getcapacitor.Bridge;
|
|
13
14
|
import com.getcapacitor.Logger;
|
|
14
15
|
import com.getcapacitor.plugin.WebView;
|
|
15
16
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.Manifest;
|
|
16
17
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.ManifestItem;
|
|
17
18
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.api.GetLatestBundleResponse;
|
|
19
|
+
import io.capawesome.capacitorjs.plugins.liveupdate.classes.events.DownloadBundleProgressEvent;
|
|
18
20
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.options.DeleteBundleOptions;
|
|
19
21
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.options.DownloadBundleOptions;
|
|
20
22
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.options.FetchLatestBundleOptions;
|
|
21
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.classes.options.SetBundleOptions;
|
|
22
23
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.options.SetChannelOptions;
|
|
23
24
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.options.SetCustomIdOptions;
|
|
24
25
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.options.SetNextBundleOptions;
|
|
25
26
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.options.SyncOptions;
|
|
26
27
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.results.FetchLatestBundleResult;
|
|
27
|
-
import io.capawesome.capacitorjs.plugins.liveupdate.classes.results.GetBundleResult;
|
|
28
28
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.results.GetBundlesResult;
|
|
29
29
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.results.GetChannelResult;
|
|
30
30
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.results.GetCurrentBundleResult;
|
|
@@ -33,8 +33,10 @@ import io.capawesome.capacitorjs.plugins.liveupdate.classes.results.GetDeviceIdR
|
|
|
33
33
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.results.GetNextBundleResult;
|
|
34
34
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.results.GetVersionCodeResult;
|
|
35
35
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.results.GetVersionNameResult;
|
|
36
|
+
import io.capawesome.capacitorjs.plugins.liveupdate.classes.results.ReadyResult;
|
|
36
37
|
import io.capawesome.capacitorjs.plugins.liveupdate.classes.results.SyncResult;
|
|
37
38
|
import io.capawesome.capacitorjs.plugins.liveupdate.enums.ArtifactType;
|
|
39
|
+
import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.DownloadProgressCallback;
|
|
38
40
|
import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.EmptyCallback;
|
|
39
41
|
import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.NonEmptyCallback;
|
|
40
42
|
import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.Result;
|
|
@@ -68,10 +70,7 @@ public class LiveUpdate {
|
|
|
68
70
|
@NonNull
|
|
69
71
|
private final LiveUpdateConfig config;
|
|
70
72
|
|
|
71
|
-
private final String defaultWebAssetDir;
|
|
72
|
-
|
|
73
|
-
@NonNull
|
|
74
|
-
private final String host;
|
|
73
|
+
private final String defaultWebAssetDir = Bridge.DEFAULT_WEB_ASSET_DIR;
|
|
75
74
|
|
|
76
75
|
@NonNull
|
|
77
76
|
private final LiveUpdateHttpClient httpClient;
|
|
@@ -89,39 +88,29 @@ public class LiveUpdate {
|
|
|
89
88
|
private final Handler rollbackHandler = new Handler();
|
|
90
89
|
private final String manifestFileName = "capawesome-live-update-manifest.json"; // DO NOT CHANGE!
|
|
91
90
|
|
|
91
|
+
private boolean rollbackPerformed = false;
|
|
92
|
+
|
|
92
93
|
public LiveUpdate(@NonNull LiveUpdateConfig config, @NonNull LiveUpdatePlugin plugin) throws PackageManager.NameNotFoundException {
|
|
93
94
|
this.config = config;
|
|
94
|
-
this.defaultWebAssetDir = plugin.getBridge().DEFAULT_WEB_ASSET_DIR;
|
|
95
|
-
if (config.getLocation() != null && config.getLocation().equals("eu")) {
|
|
96
|
-
this.host = "api.cloud.capawesome.eu";
|
|
97
|
-
} else {
|
|
98
|
-
this.host = "api.cloud.capawesome.io";
|
|
99
|
-
}
|
|
100
95
|
this.httpClient = new LiveUpdateHttpClient(config);
|
|
101
96
|
this.plugin = plugin;
|
|
102
97
|
this.preferences = new LiveUpdatePreferences(plugin.getContext());
|
|
103
98
|
this.webViewSettingsEditor = plugin.getContext().getSharedPreferences(WebView.WEBVIEW_PREFS_NAME, Activity.MODE_PRIVATE).edit();
|
|
104
99
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
reset();
|
|
109
|
-
} else {
|
|
110
|
-
startRollbackTimer();
|
|
111
|
-
}
|
|
112
|
-
saveCurrentVersionCode();
|
|
113
|
-
}
|
|
100
|
+
// Start the rollback timer to rollback to the default bundle
|
|
101
|
+
// if the app is not ready after a certain time
|
|
102
|
+
startRollbackTimer();
|
|
114
103
|
}
|
|
115
104
|
|
|
116
105
|
public void deleteBundle(@NonNull DeleteBundleOptions options, @NonNull EmptyCallback callback) {
|
|
117
106
|
String bundleId = options.getBundleId();
|
|
118
107
|
|
|
119
|
-
if (!
|
|
108
|
+
if (!hasBundleById(bundleId)) {
|
|
120
109
|
Exception exception = new Exception(LiveUpdatePlugin.ERROR_BUNDLE_NOT_FOUND);
|
|
121
110
|
callback.error(exception);
|
|
122
111
|
return;
|
|
123
112
|
}
|
|
124
|
-
|
|
113
|
+
deleteBundleById(bundleId);
|
|
125
114
|
|
|
126
115
|
callback.success();
|
|
127
116
|
}
|
|
@@ -129,11 +118,10 @@ public class LiveUpdate {
|
|
|
129
118
|
public void downloadBundle(@NonNull DownloadBundleOptions options, @NonNull EmptyCallback callback) throws Exception {
|
|
130
119
|
ArtifactType artifactType = options.getArtifactType();
|
|
131
120
|
String bundleId = options.getBundleId();
|
|
132
|
-
String checksum = options.getChecksum();
|
|
133
121
|
String url = options.getUrl();
|
|
134
122
|
|
|
135
123
|
// Check if the bundle already exists
|
|
136
|
-
if (
|
|
124
|
+
if (hasBundleById(bundleId)) {
|
|
137
125
|
Exception exception = new Exception(LiveUpdatePlugin.ERROR_BUNDLE_EXISTS);
|
|
138
126
|
callback.error(exception);
|
|
139
127
|
return;
|
|
@@ -143,7 +131,7 @@ public class LiveUpdate {
|
|
|
143
131
|
if (artifactType == ArtifactType.MANIFEST) {
|
|
144
132
|
downloadBundleOfTypeManifest(bundleId, url);
|
|
145
133
|
} else {
|
|
146
|
-
downloadBundleOfTypeZip(bundleId, url
|
|
134
|
+
downloadBundleOfTypeZip(bundleId, url);
|
|
147
135
|
}
|
|
148
136
|
callback.success();
|
|
149
137
|
}
|
|
@@ -152,17 +140,9 @@ public class LiveUpdate {
|
|
|
152
140
|
GetLatestBundleResponse response = fetchLatestBundle(options);
|
|
153
141
|
ArtifactType artifactType = response == null ? null : response.getArtifactType();
|
|
154
142
|
String bundleId = response == null ? null : response.getBundleId();
|
|
143
|
+
JSONObject customProperties = response == null ? null : response.getCustomProperties();
|
|
155
144
|
String downloadUrl = response == null ? null : response.getUrl();
|
|
156
|
-
FetchLatestBundleResult result = new FetchLatestBundleResult(artifactType, bundleId, downloadUrl);
|
|
157
|
-
callback.success(result);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
public void getBundle(@NonNull NonEmptyCallback callback) {
|
|
161
|
-
String bundleId = getCurrentBundleId();
|
|
162
|
-
if (bundleId.equals(defaultWebAssetDir)) {
|
|
163
|
-
bundleId = null;
|
|
164
|
-
}
|
|
165
|
-
GetBundleResult result = new GetBundleResult(bundleId);
|
|
145
|
+
FetchLatestBundleResult result = new FetchLatestBundleResult(artifactType, bundleId, customProperties, downloadUrl);
|
|
166
146
|
callback.success(result);
|
|
167
147
|
}
|
|
168
148
|
|
|
@@ -180,9 +160,6 @@ public class LiveUpdate {
|
|
|
180
160
|
|
|
181
161
|
public void getCurrentBundle(@NonNull NonEmptyCallback<GetCurrentBundleResult> callback) {
|
|
182
162
|
String bundleId = getCurrentBundleId();
|
|
183
|
-
if (bundleId.equals(defaultWebAssetDir)) {
|
|
184
|
-
bundleId = null;
|
|
185
|
-
}
|
|
186
163
|
GetCurrentBundleResult result = new GetCurrentBundleResult(bundleId);
|
|
187
164
|
callback.success(result);
|
|
188
165
|
}
|
|
@@ -201,9 +178,6 @@ public class LiveUpdate {
|
|
|
201
178
|
|
|
202
179
|
public void getNextBundle(@NonNull NonEmptyCallback<GetNextBundleResult> callback) {
|
|
203
180
|
String bundleId = getNextBundleId();
|
|
204
|
-
if (bundleId.equals(defaultWebAssetDir)) {
|
|
205
|
-
bundleId = null;
|
|
206
|
-
}
|
|
207
181
|
GetNextBundleResult result = new GetNextBundleResult(bundleId);
|
|
208
182
|
callback.success(result);
|
|
209
183
|
}
|
|
@@ -220,14 +194,27 @@ public class LiveUpdate {
|
|
|
220
194
|
callback.success(result);
|
|
221
195
|
}
|
|
222
196
|
|
|
223
|
-
public void ready() {
|
|
197
|
+
public void ready(@NonNull NonEmptyCallback callback) {
|
|
224
198
|
Logger.debug(LiveUpdatePlugin.TAG, "App is ready.");
|
|
199
|
+
if (config.getReadyTimeout() <= 0) {
|
|
200
|
+
Logger.warn(LiveUpdatePlugin.TAG, "Ready timeout is set to 0. Automatic rollback is disabled.");
|
|
201
|
+
}
|
|
225
202
|
// Stop the rollback timer
|
|
226
203
|
stopRollbackTimer();
|
|
227
204
|
// Delete unused bundles
|
|
228
205
|
if (config.getAutoDeleteBundles()) {
|
|
229
206
|
deleteUnusedBundles();
|
|
230
207
|
}
|
|
208
|
+
// Get the current and previous bundle IDs
|
|
209
|
+
String currentBundleId = getCurrentBundleId();
|
|
210
|
+
String previousBundleId = getPreviousBundleId();
|
|
211
|
+
// Return the result
|
|
212
|
+
ReadyResult result = new ReadyResult(currentBundleId, previousBundleId, rollbackPerformed);
|
|
213
|
+
callback.success(result);
|
|
214
|
+
// Set the new previous bundle ID
|
|
215
|
+
setPreviousBundleId(currentBundleId);
|
|
216
|
+
// Reset the rollback flag
|
|
217
|
+
rollbackPerformed = false;
|
|
231
218
|
}
|
|
232
219
|
|
|
233
220
|
public void reload() {
|
|
@@ -237,20 +224,7 @@ public class LiveUpdate {
|
|
|
237
224
|
}
|
|
238
225
|
|
|
239
226
|
public void reset() {
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
public void setBundle(@NonNull SetBundleOptions options, @NonNull EmptyCallback callback) {
|
|
244
|
-
String bundleId = options.getBundleId();
|
|
245
|
-
|
|
246
|
-
if (!hasBundle(bundleId)) {
|
|
247
|
-
Exception exception = new Exception(LiveUpdatePlugin.ERROR_BUNDLE_NOT_FOUND);
|
|
248
|
-
callback.error(exception);
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
setNextBundle(bundleId);
|
|
253
|
-
callback.success();
|
|
227
|
+
setNextBundleById(null);
|
|
254
228
|
}
|
|
255
229
|
|
|
256
230
|
public void setChannel(@NonNull SetChannelOptions options, @NonNull EmptyCallback callback) {
|
|
@@ -273,8 +247,8 @@ public class LiveUpdate {
|
|
|
273
247
|
if (bundleId == null) {
|
|
274
248
|
reset();
|
|
275
249
|
} else {
|
|
276
|
-
if (
|
|
277
|
-
|
|
250
|
+
if (hasBundleById(bundleId)) {
|
|
251
|
+
setNextBundleById(bundleId);
|
|
278
252
|
} else {
|
|
279
253
|
Exception exception = new Exception(LiveUpdatePlugin.ERROR_BUNDLE_NOT_FOUND);
|
|
280
254
|
callback.error(exception);
|
|
@@ -299,12 +273,12 @@ public class LiveUpdate {
|
|
|
299
273
|
String latestBundleId = response.getBundleId();
|
|
300
274
|
String url = response.getUrl();
|
|
301
275
|
// Check if the bundle already exists
|
|
302
|
-
if (
|
|
276
|
+
if (hasBundleById(latestBundleId)) {
|
|
303
277
|
String nextBundleId = null;
|
|
304
278
|
String currentBundleId = getCurrentBundleId();
|
|
305
279
|
if (!latestBundleId.equals(currentBundleId)) {
|
|
306
280
|
// Set the next bundle
|
|
307
|
-
|
|
281
|
+
setNextBundleById(latestBundleId);
|
|
308
282
|
nextBundleId = latestBundleId;
|
|
309
283
|
}
|
|
310
284
|
SyncResult syncResult = new SyncResult(nextBundleId);
|
|
@@ -315,10 +289,10 @@ public class LiveUpdate {
|
|
|
315
289
|
if (artifactType == ArtifactType.MANIFEST) {
|
|
316
290
|
downloadBundleOfTypeManifest(latestBundleId, url);
|
|
317
291
|
} else {
|
|
318
|
-
downloadBundleOfTypeZip(latestBundleId, url
|
|
292
|
+
downloadBundleOfTypeZip(latestBundleId, url);
|
|
319
293
|
}
|
|
320
294
|
// Set the next bundle
|
|
321
|
-
|
|
295
|
+
setNextBundleById(latestBundleId);
|
|
322
296
|
SyncResult syncResult = new SyncResult(latestBundleId);
|
|
323
297
|
callback.success(syncResult);
|
|
324
298
|
}
|
|
@@ -367,9 +341,10 @@ public class LiveUpdate {
|
|
|
367
341
|
return new File(plugin.getContext().getCacheDir(), fileName);
|
|
368
342
|
}
|
|
369
343
|
|
|
370
|
-
private void copyCurrentBundleFile(@NonNull
|
|
344
|
+
private void copyCurrentBundleFile(@NonNull ManifestItem fileToCopy, @NonNull File destinationDirectory) throws IOException {
|
|
345
|
+
String href = fileToCopy.getHref();
|
|
371
346
|
String currentBundleId = getCurrentBundleId();
|
|
372
|
-
if (currentBundleId
|
|
347
|
+
if (currentBundleId == null) {
|
|
373
348
|
// Create the source input stream
|
|
374
349
|
AssetManager assets = plugin.getContext().getAssets();
|
|
375
350
|
InputStream inputStream = assets.open(defaultWebAssetDir + "/" + href);
|
|
@@ -391,9 +366,9 @@ public class LiveUpdate {
|
|
|
391
366
|
}
|
|
392
367
|
}
|
|
393
368
|
|
|
394
|
-
private void copyCurrentBundleFiles(@NonNull List<
|
|
395
|
-
for (
|
|
396
|
-
copyCurrentBundleFile(
|
|
369
|
+
private void copyCurrentBundleFiles(@NonNull List<ManifestItem> filesToCopy, @NonNull File destinationDirectory) throws IOException {
|
|
370
|
+
for (ManifestItem fileToCopy : filesToCopy) {
|
|
371
|
+
copyCurrentBundleFile(fileToCopy, destinationDirectory);
|
|
397
372
|
}
|
|
398
373
|
}
|
|
399
374
|
|
|
@@ -446,14 +421,14 @@ public class LiveUpdate {
|
|
|
446
421
|
return file;
|
|
447
422
|
}
|
|
448
423
|
|
|
449
|
-
private void
|
|
424
|
+
private void deleteBundleById(@NonNull String bundleId) {
|
|
450
425
|
// Delete the bundle directory
|
|
451
426
|
File bundleDirectory = buildBundleDirectoryFor(bundleId);
|
|
452
427
|
deleteFileRecursively(bundleDirectory);
|
|
453
428
|
// Reset the next bundle if it is the deleted bundle
|
|
454
429
|
String nextBundleId = getNextBundleId();
|
|
455
430
|
if (bundleId.equals(nextBundleId)) {
|
|
456
|
-
|
|
431
|
+
setNextBundleById(null);
|
|
457
432
|
}
|
|
458
433
|
}
|
|
459
434
|
|
|
@@ -470,18 +445,19 @@ public class LiveUpdate {
|
|
|
470
445
|
String[] bundleIds = getBundleIds();
|
|
471
446
|
for (String bundleId : bundleIds) {
|
|
472
447
|
if (!isBundleInUse(bundleId)) {
|
|
473
|
-
|
|
448
|
+
deleteBundleById(bundleId);
|
|
474
449
|
}
|
|
475
450
|
}
|
|
476
451
|
}
|
|
477
452
|
|
|
478
|
-
private void downloadAndVerifyFile(@NonNull String url, @NonNull File file, @Nullable
|
|
453
|
+
private void downloadAndVerifyFile(@NonNull String url, @NonNull File file, @Nullable DownloadProgressCallback callback)
|
|
454
|
+
throws Exception {
|
|
479
455
|
Response response = httpClient.execute(url);
|
|
480
456
|
ResponseBody responseBody = response.body();
|
|
481
457
|
if (response.isSuccessful()) {
|
|
482
|
-
LiveUpdateHttpClient.writeResponseBodyToFile(responseBody, file);
|
|
458
|
+
LiveUpdateHttpClient.writeResponseBodyToFile(responseBody, file, callback);
|
|
483
459
|
// Verify the file
|
|
484
|
-
checksum =
|
|
460
|
+
String checksum = LiveUpdateHttpClient.getChecksumFromResponse(response);
|
|
485
461
|
String signature = LiveUpdateHttpClient.getSignatureFromResponse(response);
|
|
486
462
|
verifyFile(file, checksum, signature);
|
|
487
463
|
} else {
|
|
@@ -491,24 +467,39 @@ public class LiveUpdate {
|
|
|
491
467
|
}
|
|
492
468
|
}
|
|
493
469
|
|
|
494
|
-
private File downloadBundleFile(
|
|
470
|
+
private File downloadBundleFile(
|
|
471
|
+
@NonNull String baseUrl,
|
|
472
|
+
@NonNull String href,
|
|
473
|
+
@NonNull File destinationDirectory,
|
|
474
|
+
@Nullable DownloadProgressCallback callback
|
|
475
|
+
) throws Exception {
|
|
495
476
|
HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl).newBuilder();
|
|
496
477
|
urlBuilder.addQueryParameter("href", href);
|
|
497
478
|
String url = urlBuilder.build().toString();
|
|
498
479
|
|
|
499
480
|
File destinationFile = new File(destinationDirectory, href);
|
|
500
481
|
destinationFile.getParentFile().mkdirs();
|
|
501
|
-
downloadAndVerifyFile(url, destinationFile,
|
|
482
|
+
downloadAndVerifyFile(url, destinationFile, callback);
|
|
502
483
|
return destinationFile;
|
|
503
484
|
}
|
|
504
485
|
|
|
505
|
-
private void downloadBundleFiles(
|
|
506
|
-
|
|
486
|
+
private void downloadBundleFiles(
|
|
487
|
+
@NonNull String baseUrl,
|
|
488
|
+
@NonNull List<ManifestItem> filesToDownload,
|
|
489
|
+
@NonNull File destinationDirectory,
|
|
490
|
+
@Nullable DownloadProgressCallback callback
|
|
491
|
+
) throws Exception {
|
|
492
|
+
// Track the total downloaded bytes and total bytes to download for the progress callback
|
|
493
|
+
final long[] totalBytesDownloaded = { 0 };
|
|
494
|
+
long totalBytesToDownload = 0;
|
|
495
|
+
for (ManifestItem fileToDownload : filesToDownload) {
|
|
496
|
+
totalBytesToDownload += fileToDownload.getSizeInBytes();
|
|
497
|
+
}
|
|
507
498
|
// Limit the number of threads to twice the number of processors
|
|
508
499
|
int maxThreads = Runtime.getRuntime().availableProcessors() * 2;
|
|
509
500
|
List<Thread> threads = new ArrayList<>();
|
|
510
501
|
AtomicReference<Exception> exceptionReference = new AtomicReference<>();
|
|
511
|
-
for (
|
|
502
|
+
for (ManifestItem fileToDownload : filesToDownload) {
|
|
512
503
|
// Wait until a thread is available
|
|
513
504
|
while (threads.size() >= maxThreads) {
|
|
514
505
|
for (Thread thread : threads) {
|
|
@@ -524,13 +515,23 @@ public class LiveUpdate {
|
|
|
524
515
|
if (exceptionReference.get() != null) {
|
|
525
516
|
break;
|
|
526
517
|
}
|
|
518
|
+
// Final variable that can be accessed from within inner class
|
|
519
|
+
long finalTotalBytesToDownload = totalBytesToDownload;
|
|
527
520
|
// Start a new thread
|
|
528
521
|
Thread thread = new Thread(() -> {
|
|
529
522
|
try {
|
|
530
|
-
downloadBundleFile(baseUrl,
|
|
523
|
+
downloadBundleFile(baseUrl, fileToDownload.getHref(), destinationDirectory, (downloadedBytes, totalBytes) -> {
|
|
524
|
+
if (callback != null) {
|
|
525
|
+
callback.onProgress(totalBytesDownloaded[0] + downloadedBytes, finalTotalBytesToDownload);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
if (callback != null) {
|
|
529
|
+
totalBytesDownloaded[0] += fileToDownload.getSizeInBytes();
|
|
530
|
+
callback.onProgress(totalBytesDownloaded[0], finalTotalBytesToDownload);
|
|
531
|
+
}
|
|
531
532
|
} catch (Exception exception) {
|
|
532
533
|
exceptionReference.set(exception);
|
|
533
|
-
Logger.error(LiveUpdatePlugin.TAG, "Failed to download file: " +
|
|
534
|
+
Logger.error(LiveUpdatePlugin.TAG, "Failed to download file: " + fileToDownload.getHref(), exception);
|
|
534
535
|
}
|
|
535
536
|
});
|
|
536
537
|
threads.add(thread);
|
|
@@ -545,13 +546,17 @@ public class LiveUpdate {
|
|
|
545
546
|
if (exception != null) {
|
|
546
547
|
throw exception;
|
|
547
548
|
}
|
|
549
|
+
// Call the callback one last time to make sure the progress is at 100%
|
|
550
|
+
if (callback != null) {
|
|
551
|
+
callback.onProgress(totalBytesToDownload, totalBytesToDownload);
|
|
552
|
+
}
|
|
548
553
|
}
|
|
549
554
|
|
|
550
555
|
private void downloadBundleOfTypeManifest(@NonNull String bundleId, @NonNull String downloadUrl) throws Exception {
|
|
551
556
|
// Create a temporary directory
|
|
552
557
|
File temporaryDirectory = createTemporaryDirectory();
|
|
553
558
|
// Download the latest manifest
|
|
554
|
-
File latestManifestFile = downloadBundleFile(downloadUrl, manifestFileName, temporaryDirectory);
|
|
559
|
+
File latestManifestFile = downloadBundleFile(downloadUrl, manifestFileName, temporaryDirectory, null);
|
|
555
560
|
Manifest latestManifest = loadManifest(latestManifestFile);
|
|
556
561
|
// Load the current manifest
|
|
557
562
|
Manifest currentManifest = loadCurrentManifest();
|
|
@@ -565,26 +570,23 @@ public class LiveUpdate {
|
|
|
565
570
|
itemsToDownload.addAll(Manifest.findMissingItems(latestManifest, currentManifest));
|
|
566
571
|
}
|
|
567
572
|
// Copy the files
|
|
568
|
-
|
|
569
|
-
for (ManifestItem item : itemsToCopy) {
|
|
570
|
-
hrefsToCopy.add(item.getHref());
|
|
571
|
-
}
|
|
572
|
-
copyCurrentBundleFiles(hrefsToCopy, temporaryDirectory);
|
|
573
|
+
copyCurrentBundleFiles(itemsToCopy, temporaryDirectory);
|
|
573
574
|
// Download the files
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
}
|
|
578
|
-
downloadBundleFiles(downloadUrl, hrefsToDownload, temporaryDirectory);
|
|
575
|
+
downloadBundleFiles(downloadUrl, itemsToDownload, temporaryDirectory, (downloadedBytes, totalBytes) -> {
|
|
576
|
+
DownloadBundleProgressEvent event = new DownloadBundleProgressEvent(bundleId, downloadedBytes, totalBytes);
|
|
577
|
+
notifyDownloadBundleProgressListeners(event);
|
|
578
|
+
});
|
|
579
579
|
// Add the bundle
|
|
580
580
|
addBundleOfTypeManifest(bundleId, temporaryDirectory);
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
-
private void downloadBundleOfTypeZip(@NonNull String bundleId, @NonNull String downloadUrl
|
|
584
|
-
throws Exception {
|
|
583
|
+
private void downloadBundleOfTypeZip(@NonNull String bundleId, @NonNull String downloadUrl) throws Exception {
|
|
585
584
|
File file = buildTemporaryZipFile();
|
|
586
585
|
// Download the bundle
|
|
587
|
-
downloadAndVerifyFile(downloadUrl, file,
|
|
586
|
+
downloadAndVerifyFile(downloadUrl, file, (downloadedBytes, totalBytes) -> {
|
|
587
|
+
DownloadBundleProgressEvent event = new DownloadBundleProgressEvent(bundleId, downloadedBytes, totalBytes);
|
|
588
|
+
notifyDownloadBundleProgressListeners(event);
|
|
589
|
+
});
|
|
588
590
|
// Add the bundle
|
|
589
591
|
addBundleOfTypeZip(bundleId, file);
|
|
590
592
|
// Delete the temporary file
|
|
@@ -596,7 +598,7 @@ public class LiveUpdate {
|
|
|
596
598
|
String channel = options.getChannel() == null ? getChannel() : options.getChannel();
|
|
597
599
|
String url = new HttpUrl.Builder()
|
|
598
600
|
.scheme("https")
|
|
599
|
-
.host(
|
|
601
|
+
.host(config.getServerDomain())
|
|
600
602
|
.addPathSegment("v1")
|
|
601
603
|
.addPathSegment("apps")
|
|
602
604
|
.addPathSegment(config.getAppId())
|
|
@@ -678,12 +680,13 @@ public class LiveUpdate {
|
|
|
678
680
|
}
|
|
679
681
|
|
|
680
682
|
/**
|
|
681
|
-
* @return The current bundle ID
|
|
683
|
+
* @return The current bundle ID or `null` if the default bundle is in use.
|
|
682
684
|
*/
|
|
685
|
+
@Nullable
|
|
683
686
|
private String getCurrentBundleId() {
|
|
684
687
|
String currentPath = getCurrentCapacitorServerPath();
|
|
685
688
|
if (currentPath.equals(defaultWebAssetDir)) {
|
|
686
|
-
return
|
|
689
|
+
return null;
|
|
687
690
|
}
|
|
688
691
|
return new File(currentPath).getName();
|
|
689
692
|
}
|
|
@@ -706,13 +709,13 @@ public class LiveUpdate {
|
|
|
706
709
|
}
|
|
707
710
|
|
|
708
711
|
/**
|
|
709
|
-
* @return The next bundle ID
|
|
712
|
+
* @return The next bundle ID or `null` if the default bundle will be used.
|
|
710
713
|
*/
|
|
711
|
-
@
|
|
714
|
+
@Nullable
|
|
712
715
|
private String getNextBundleId() {
|
|
713
716
|
String nextPath = getNextCapacitorServerPath();
|
|
714
717
|
if (nextPath.equals(defaultWebAssetDir)) {
|
|
715
|
-
return
|
|
718
|
+
return null;
|
|
716
719
|
}
|
|
717
720
|
return new File(nextPath).getName();
|
|
718
721
|
}
|
|
@@ -728,6 +731,14 @@ public class LiveUpdate {
|
|
|
728
731
|
.getString(WebView.CAP_SERVER_PATH, defaultWebAssetDir);
|
|
729
732
|
}
|
|
730
733
|
|
|
734
|
+
/**
|
|
735
|
+
* @return The previous bundle ID or `null` if the default bundle was used.
|
|
736
|
+
*/
|
|
737
|
+
@Nullable
|
|
738
|
+
private String getPreviousBundleId() {
|
|
739
|
+
return preferences.getPreviousBundleId();
|
|
740
|
+
}
|
|
741
|
+
|
|
731
742
|
private String getVersionCode() throws PackageManager.NameNotFoundException {
|
|
732
743
|
return String.valueOf(getPackageInfo().versionCode);
|
|
733
744
|
}
|
|
@@ -736,17 +747,23 @@ public class LiveUpdate {
|
|
|
736
747
|
return getPackageInfo().versionName;
|
|
737
748
|
}
|
|
738
749
|
|
|
739
|
-
private boolean
|
|
750
|
+
private boolean hasBundleById(@NonNull String bundleId) {
|
|
740
751
|
File bundleDirectory = buildBundleDirectoryFor(bundleId);
|
|
741
752
|
return bundleDirectory.exists();
|
|
742
753
|
}
|
|
743
754
|
|
|
755
|
+
private boolean isBundleInUse(@NonNull String bundleId) {
|
|
756
|
+
String currentBundleId = getCurrentBundleId();
|
|
757
|
+
String nextBundleId = getNextBundleId();
|
|
758
|
+
return bundleId.equals(currentBundleId) || bundleId.equals(nextBundleId);
|
|
759
|
+
}
|
|
760
|
+
|
|
744
761
|
@Nullable
|
|
745
762
|
private Manifest loadCurrentManifest() throws Exception {
|
|
746
763
|
String currentBundleId = getCurrentBundleId();
|
|
747
|
-
if (currentBundleId
|
|
764
|
+
if (currentBundleId == null) {
|
|
748
765
|
AssetManager assets = plugin.getContext().getAssets();
|
|
749
|
-
|
|
766
|
+
boolean manifestFileExists = Arrays.asList(assets.list(defaultWebAssetDir)).contains(manifestFileName);
|
|
750
767
|
if (manifestFileExists) {
|
|
751
768
|
InputStream inputStream = assets.open(defaultWebAssetDir + "/" + manifestFileName);
|
|
752
769
|
BufferedSource source = Okio.buffer(Okio.source(inputStream));
|
|
@@ -776,25 +793,25 @@ public class LiveUpdate {
|
|
|
776
793
|
return loadManifest(source);
|
|
777
794
|
}
|
|
778
795
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
String nextBundleId = getNextBundleId();
|
|
782
|
-
return bundleId.equals(currentBundleId) || bundleId.equals(nextBundleId);
|
|
796
|
+
public void notifyDownloadBundleProgressListeners(@NonNull final DownloadBundleProgressEvent event) {
|
|
797
|
+
plugin.notifyDownloadBundleProgressListeners(event);
|
|
783
798
|
}
|
|
784
799
|
|
|
785
800
|
private void rollback() {
|
|
786
|
-
|
|
801
|
+
// Set the rollback flag
|
|
802
|
+
rollbackPerformed = true;
|
|
803
|
+
// Set the new previous bundle ID
|
|
804
|
+
String currentBundleId = getCurrentBundleId();
|
|
805
|
+
setPreviousBundleId(currentBundleId);
|
|
806
|
+
// Log the rollback result
|
|
807
|
+
if (currentBundleId == null) {
|
|
787
808
|
Logger.debug(LiveUpdatePlugin.TAG, "App is not ready. Default bundle is already in use.");
|
|
788
|
-
|
|
809
|
+
} else {
|
|
810
|
+
Logger.debug(LiveUpdatePlugin.TAG, "App is not ready. Rolling back to default bundle.");
|
|
811
|
+
// Rollback to the default bundle
|
|
812
|
+
setNextBundleById(null);
|
|
813
|
+
setCurrentBundleById(null);
|
|
789
814
|
}
|
|
790
|
-
Logger.debug(LiveUpdatePlugin.TAG, "App is not ready. Rolling back to default bundle.");
|
|
791
|
-
setNextCapacitorServerPathToDefaultWebAssetDir();
|
|
792
|
-
setCurrentCapacitorServerPathToDefaultWebAssetDir();
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
private void saveCurrentVersionCode() throws PackageManager.NameNotFoundException {
|
|
796
|
-
int currentVersionCode = getPackageInfo().versionCode;
|
|
797
|
-
preferences.setLastVersionCode(currentVersionCode);
|
|
798
815
|
}
|
|
799
816
|
|
|
800
817
|
@Nullable
|
|
@@ -822,6 +839,18 @@ public class LiveUpdate {
|
|
|
822
839
|
return null;
|
|
823
840
|
}
|
|
824
841
|
|
|
842
|
+
/**
|
|
843
|
+
* @param bundleId The bundle ID to set as the current bundle. If `null`, the default bundle will be used.
|
|
844
|
+
*/
|
|
845
|
+
private void setCurrentBundleById(@Nullable String bundleId) {
|
|
846
|
+
if (bundleId == null) {
|
|
847
|
+
setCurrentCapacitorServerPath(defaultWebAssetDir);
|
|
848
|
+
} else {
|
|
849
|
+
File bundleDirectory = buildBundleDirectoryFor(bundleId);
|
|
850
|
+
setCurrentCapacitorServerPath(bundleDirectory.getPath());
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
825
854
|
private void setCurrentCapacitorServerPath(@NonNull String path) {
|
|
826
855
|
if (path.equals(defaultWebAssetDir)) {
|
|
827
856
|
this.plugin.getBridge().setServerAssetPath(path);
|
|
@@ -831,13 +860,16 @@ public class LiveUpdate {
|
|
|
831
860
|
this.plugin.getBridge().reload();
|
|
832
861
|
}
|
|
833
862
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
863
|
+
/**
|
|
864
|
+
* @param bundleId The bundle ID to set as the next bundle. If `null`, the default bundle will be used.
|
|
865
|
+
*/
|
|
866
|
+
private void setNextBundleById(@Nullable String bundleId) {
|
|
867
|
+
if (bundleId == null) {
|
|
868
|
+
setNextCapacitorServerPath(defaultWebAssetDir);
|
|
869
|
+
} else {
|
|
870
|
+
File bundleDirectory = buildBundleDirectoryFor(bundleId);
|
|
871
|
+
setNextCapacitorServerPath(bundleDirectory.getPath());
|
|
872
|
+
}
|
|
841
873
|
}
|
|
842
874
|
|
|
843
875
|
private void setNextCapacitorServerPath(@NonNull String path) {
|
|
@@ -845,11 +877,17 @@ public class LiveUpdate {
|
|
|
845
877
|
this.webViewSettingsEditor.commit();
|
|
846
878
|
}
|
|
847
879
|
|
|
848
|
-
|
|
849
|
-
|
|
880
|
+
/**
|
|
881
|
+
* @param bundleId The bundle ID to save as the previous bundle. If `null`, the value will be removed.
|
|
882
|
+
*/
|
|
883
|
+
private void setPreviousBundleId(@Nullable String bundleId) {
|
|
884
|
+
preferences.setPreviousBundleId(bundleId);
|
|
850
885
|
}
|
|
851
886
|
|
|
852
887
|
private void startRollbackTimer() {
|
|
888
|
+
if (config.getReadyTimeout() <= 0) {
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
853
891
|
stopRollbackTimer();
|
|
854
892
|
rollbackHandler.postDelayed(() -> rollback(), config.getReadyTimeout());
|
|
855
893
|
}
|
|
@@ -916,12 +954,6 @@ public class LiveUpdate {
|
|
|
916
954
|
}
|
|
917
955
|
}
|
|
918
956
|
|
|
919
|
-
private boolean wasUpdated() throws PackageManager.NameNotFoundException {
|
|
920
|
-
int lastVersionCode = preferences.getLastVersionCode();
|
|
921
|
-
int currentVersionCode = getPackageInfo().versionCode;
|
|
922
|
-
return lastVersionCode != currentVersionCode;
|
|
923
|
-
}
|
|
924
|
-
|
|
925
957
|
private PackageInfo getPackageInfo() throws PackageManager.NameNotFoundException {
|
|
926
958
|
String packageName = this.plugin.getContext().getPackageName();
|
|
927
959
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|