@capawesome/capacitor-live-update 7.5.0 → 7.5.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/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdate.java +80 -55
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java +2 -1
- package/ios/Plugin/LiveUpdate.swift +37 -11
- package/ios/Plugin/LiveUpdatePlugin.swift +1 -1
- package/package.json +1 -1
|
@@ -102,6 +102,7 @@ public class LiveUpdate {
|
|
|
102
102
|
private final SharedPreferences.Editor webViewSettingsEditor;
|
|
103
103
|
|
|
104
104
|
private final String bundlesDirectory = "_capacitor_live_update_bundles"; // DO NOT CHANGE!
|
|
105
|
+
private final String downloadsDirectory = "capawesome_capacitor_live_update_downloads";
|
|
105
106
|
private final Handler rollbackHandler = new Handler();
|
|
106
107
|
private final String manifestFileName = "capawesome-live-update-manifest.json"; // DO NOT CHANGE!
|
|
107
108
|
|
|
@@ -123,6 +124,9 @@ public class LiveUpdate {
|
|
|
123
124
|
// Set the device ID on the HTTP client (after any potential config reset)
|
|
124
125
|
this.httpClient.setDeviceId(getDeviceId());
|
|
125
126
|
|
|
127
|
+
// Delete the leftovers of downloads that were interrupted by a process kill
|
|
128
|
+
tryDeleteDownloadsDirectory();
|
|
129
|
+
|
|
126
130
|
// Start the rollback timer to rollback to the default bundle
|
|
127
131
|
// if the app is not ready after a certain time
|
|
128
132
|
startRollbackTimer();
|
|
@@ -530,7 +534,10 @@ public class LiveUpdate {
|
|
|
530
534
|
|
|
531
535
|
// Move the bundle directory to the bundles directory
|
|
532
536
|
File bundleDirectory = buildBundleDirectoryFor(bundleId);
|
|
533
|
-
indexHtmlFile.getParentFile().renameTo(bundleDirectory);
|
|
537
|
+
boolean moved = indexHtmlFile.getParentFile().renameTo(bundleDirectory);
|
|
538
|
+
if (!moved) {
|
|
539
|
+
throw new Exception(LiveUpdatePlugin.ERROR_BUNDLE_MOVE_FAILED);
|
|
540
|
+
}
|
|
534
541
|
}
|
|
535
542
|
|
|
536
543
|
private void addBundleOfTypeManifest(@NonNull String bundleId, @NonNull File directory) throws Exception {
|
|
@@ -539,7 +546,7 @@ public class LiveUpdate {
|
|
|
539
546
|
|
|
540
547
|
private void addBundleOfTypeZip(@NonNull String bundleId, @NonNull File zipFile) throws Exception {
|
|
541
548
|
// Unzip the file to the bundle directory
|
|
542
|
-
File unzippedDirectory =
|
|
549
|
+
File unzippedDirectory = unzipDownloadedBundle(zipFile);
|
|
543
550
|
// Add the bundle
|
|
544
551
|
addBundle(bundleId, unzippedDirectory);
|
|
545
552
|
}
|
|
@@ -552,14 +559,8 @@ public class LiveUpdate {
|
|
|
552
559
|
return new File(plugin.getContext().getFilesDir(), bundlesDirectory + "/" + bundleId);
|
|
553
560
|
}
|
|
554
561
|
|
|
555
|
-
private File
|
|
556
|
-
|
|
557
|
-
return new File(plugin.getContext().getCacheDir(), fileName);
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
private File buildTemporaryZipFile() {
|
|
561
|
-
String fileName = UUID.randomUUID().toString() + ".zip";
|
|
562
|
-
return new File(plugin.getContext().getCacheDir(), fileName);
|
|
562
|
+
private File buildDownloadsDirectory() {
|
|
563
|
+
return new File(plugin.getContext().getCacheDir(), downloadsDirectory);
|
|
563
564
|
}
|
|
564
565
|
|
|
565
566
|
private void copyCurrentBundleFile(@NonNull ManifestItem fileToCopy, @NonNull File destinationDirectory) throws IOException {
|
|
@@ -646,10 +647,10 @@ public class LiveUpdate {
|
|
|
646
647
|
}
|
|
647
648
|
}
|
|
648
649
|
|
|
649
|
-
private File
|
|
650
|
-
File
|
|
651
|
-
|
|
652
|
-
return
|
|
650
|
+
private File createTemporaryDownloadDirectory() {
|
|
651
|
+
File temporaryDirectory = new File(buildDownloadsDirectory(), UUID.randomUUID().toString());
|
|
652
|
+
temporaryDirectory.mkdirs();
|
|
653
|
+
return temporaryDirectory;
|
|
653
654
|
}
|
|
654
655
|
|
|
655
656
|
private void deleteBundleById(@NonNull String bundleId) {
|
|
@@ -675,6 +676,23 @@ public class LiveUpdate {
|
|
|
675
676
|
file.delete();
|
|
676
677
|
}
|
|
677
678
|
|
|
679
|
+
@NonNull
|
|
680
|
+
private EmptyCallback deleteTemporaryDirectoryOnCompletion(@NonNull File temporaryDirectory, @NonNull EmptyCallback callback) {
|
|
681
|
+
return new EmptyCallback() {
|
|
682
|
+
@Override
|
|
683
|
+
public void success() {
|
|
684
|
+
deleteFileRecursively(temporaryDirectory);
|
|
685
|
+
callback.success();
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
@Override
|
|
689
|
+
public void error(@NonNull Exception exception) {
|
|
690
|
+
deleteFileRecursively(temporaryDirectory);
|
|
691
|
+
callback.error(exception);
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
678
696
|
private void deleteUnusedBundles() {
|
|
679
697
|
String[] bundleIds = getDownloadedBundleIds();
|
|
680
698
|
for (String bundleId : bundleIds) {
|
|
@@ -845,10 +863,10 @@ public class LiveUpdate {
|
|
|
845
863
|
@NonNull String downloadUrl,
|
|
846
864
|
@NonNull EmptyCallback completionCallback
|
|
847
865
|
) {
|
|
866
|
+
// Create a temporary directory that is deleted once the download completed or failed
|
|
867
|
+
File temporaryDirectory = createTemporaryDownloadDirectory();
|
|
868
|
+
EmptyCallback callback = deleteTemporaryDirectoryOnCompletion(temporaryDirectory, completionCallback);
|
|
848
869
|
try {
|
|
849
|
-
// Create a temporary directory
|
|
850
|
-
File temporaryDirectory = createTemporaryDirectory();
|
|
851
|
-
|
|
852
870
|
// Download the latest manifest
|
|
853
871
|
downloadBundleFile(
|
|
854
872
|
downloadUrl,
|
|
@@ -898,31 +916,31 @@ public class LiveUpdate {
|
|
|
898
916
|
try {
|
|
899
917
|
// Add the bundle
|
|
900
918
|
addBundleOfTypeManifest(bundleId, temporaryDirectory);
|
|
901
|
-
|
|
919
|
+
callback.success();
|
|
902
920
|
} catch (Exception e) {
|
|
903
|
-
|
|
921
|
+
callback.error(e);
|
|
904
922
|
}
|
|
905
923
|
}
|
|
906
924
|
|
|
907
925
|
@Override
|
|
908
926
|
public void error(@NonNull Exception exception) {
|
|
909
|
-
|
|
927
|
+
callback.error(exception);
|
|
910
928
|
}
|
|
911
929
|
}
|
|
912
930
|
);
|
|
913
931
|
} catch (Exception e) {
|
|
914
|
-
|
|
932
|
+
callback.error(e);
|
|
915
933
|
}
|
|
916
934
|
}
|
|
917
935
|
|
|
918
936
|
@Override
|
|
919
937
|
public void error(@NonNull Exception exception) {
|
|
920
|
-
|
|
938
|
+
callback.error(exception);
|
|
921
939
|
}
|
|
922
940
|
}
|
|
923
941
|
);
|
|
924
942
|
} catch (Exception e) {
|
|
925
|
-
|
|
943
|
+
callback.error(e);
|
|
926
944
|
}
|
|
927
945
|
}
|
|
928
946
|
|
|
@@ -933,39 +951,42 @@ public class LiveUpdate {
|
|
|
933
951
|
@NonNull String downloadUrl,
|
|
934
952
|
@NonNull EmptyCallback completionCallback
|
|
935
953
|
) {
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
954
|
+
// Create a temporary directory that is deleted once the download completed or failed
|
|
955
|
+
File temporaryDirectory = createTemporaryDownloadDirectory();
|
|
956
|
+
EmptyCallback callback = deleteTemporaryDirectoryOnCompletion(temporaryDirectory, completionCallback);
|
|
957
|
+
File zipFile = new File(temporaryDirectory, "bundle.zip");
|
|
958
|
+
try {
|
|
959
|
+
// Download the bundle
|
|
960
|
+
downloadAndVerifyFile(
|
|
961
|
+
downloadUrl,
|
|
962
|
+
zipFile,
|
|
963
|
+
checksum,
|
|
964
|
+
signature,
|
|
965
|
+
(downloadedBytes, totalBytes) -> {
|
|
966
|
+
DownloadBundleProgressEvent event = new DownloadBundleProgressEvent(bundleId, downloadedBytes, totalBytes);
|
|
967
|
+
notifyDownloadBundleProgressListeners(event);
|
|
968
|
+
},
|
|
969
|
+
new EmptyCallback() {
|
|
970
|
+
@Override
|
|
971
|
+
public void success() {
|
|
972
|
+
try {
|
|
973
|
+
// Add the bundle
|
|
974
|
+
addBundleOfTypeZip(bundleId, zipFile);
|
|
975
|
+
callback.success();
|
|
976
|
+
} catch (Exception e) {
|
|
977
|
+
callback.error(e);
|
|
978
|
+
}
|
|
958
979
|
}
|
|
959
|
-
}
|
|
960
980
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
completionCallback.error(exception);
|
|
981
|
+
@Override
|
|
982
|
+
public void error(@NonNull Exception exception) {
|
|
983
|
+
callback.error(exception);
|
|
984
|
+
}
|
|
966
985
|
}
|
|
967
|
-
|
|
968
|
-
)
|
|
986
|
+
);
|
|
987
|
+
} catch (Exception e) {
|
|
988
|
+
callback.error(e);
|
|
989
|
+
}
|
|
969
990
|
}
|
|
970
991
|
|
|
971
992
|
private void fetchLatestBundleInternal(
|
|
@@ -1431,8 +1452,12 @@ public class LiveUpdate {
|
|
|
1431
1452
|
}
|
|
1432
1453
|
}
|
|
1433
1454
|
|
|
1434
|
-
private
|
|
1435
|
-
|
|
1455
|
+
private void tryDeleteDownloadsDirectory() {
|
|
1456
|
+
deleteFileRecursively(buildDownloadsDirectory());
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
private File unzipDownloadedBundle(@NonNull File zipFile) throws IOException {
|
|
1460
|
+
File destination = new File(zipFile.getParentFile(), "bundle");
|
|
1436
1461
|
String destinationPath = destination.getPath();
|
|
1437
1462
|
ZipFile zip = new ZipFile(zipFile);
|
|
1438
1463
|
// Clear stored Unix permissions to prevent EACCES errors on newer Android versions
|
package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java
CHANGED
|
@@ -37,12 +37,13 @@ import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.Result;
|
|
|
37
37
|
public class LiveUpdatePlugin extends Plugin {
|
|
38
38
|
|
|
39
39
|
public static final String TAG = "LiveUpdate";
|
|
40
|
-
public static final String VERSION = "7.5.
|
|
40
|
+
public static final String VERSION = "7.5.1";
|
|
41
41
|
public static final String SHARED_PREFERENCES_NAME = "CapawesomeLiveUpdate"; // DO NOT CHANGE
|
|
42
42
|
public static final String ERROR_APP_ID_MISSING = "appId must be configured.";
|
|
43
43
|
public static final String ERROR_BUNDLE_EXISTS = "bundle already exists.";
|
|
44
44
|
public static final String ERROR_BUNDLE_ID_MISSING = "bundleId must be provided.";
|
|
45
45
|
public static final String ERROR_BUNDLE_INDEX_HTML_MISSING = "The bundle does not contain an index.html file.";
|
|
46
|
+
public static final String ERROR_BUNDLE_MOVE_FAILED = "Bundle could not be moved.";
|
|
46
47
|
public static final String ERROR_BUNDLE_NOT_FOUND = "bundle not found.";
|
|
47
48
|
public static final String ERROR_CHECKSUM_CALCULATION_FAILED = "Failed to calculate checksum.";
|
|
48
49
|
public static final String ERROR_CHECKSUM_MISMATCH = "Checksum mismatch.";
|
|
@@ -13,6 +13,7 @@ import CommonCrypto
|
|
|
13
13
|
private let config: LiveUpdateConfig
|
|
14
14
|
private let defaultWebAssetDir = "public" // DO NOT CHANGE! (See https://dub.sh/Buvz4yj)
|
|
15
15
|
private let defaultServerPathKey = "serverBasePath" // DO NOT CHANGE! (See https://dub.sh/ceDl0zT)
|
|
16
|
+
private let downloadsDirectory = "capawesome_capacitor_live_update_downloads"
|
|
16
17
|
private let httpClient: LiveUpdateHttpClient
|
|
17
18
|
private let libraryDirectoryUrl = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!
|
|
18
19
|
private let manifestFileName = "capawesome-live-update-manifest.json" // DO NOT CHANGE!
|
|
@@ -37,6 +38,9 @@ import CommonCrypto
|
|
|
37
38
|
// Set the device ID on the HTTP client (after any potential config reset)
|
|
38
39
|
self.httpClient.setDeviceId(getDeviceId())
|
|
39
40
|
|
|
41
|
+
// Delete the leftovers of downloads that were interrupted by a process kill
|
|
42
|
+
tryDeleteDownloadsDirectory()
|
|
43
|
+
|
|
40
44
|
// Start the rollback timer to rollback to the default bundle
|
|
41
45
|
// if the app is not ready after a certain time
|
|
42
46
|
startRollbackTimer()
|
|
@@ -352,7 +356,7 @@ import CommonCrypto
|
|
|
352
356
|
|
|
353
357
|
private func addBundleOfTypeZip(bundleId: String, zipFile: URL) async throws {
|
|
354
358
|
// Unzip the bundle
|
|
355
|
-
let unzippedDirectory = try self.
|
|
359
|
+
let unzippedDirectory = try self.unzipDownloadedBundle(zipFile: zipFile)
|
|
356
360
|
// Add the bundle
|
|
357
361
|
try self.addBundle(bundleId: bundleId, directory: unzippedDirectory)
|
|
358
362
|
}
|
|
@@ -376,6 +380,10 @@ import CommonCrypto
|
|
|
376
380
|
return url
|
|
377
381
|
}
|
|
378
382
|
|
|
383
|
+
private func buildDownloadsDirectoryUrl() -> URL {
|
|
384
|
+
return cachesDirectoryUrl.appendingPathComponent(downloadsDirectory)
|
|
385
|
+
}
|
|
386
|
+
|
|
379
387
|
private func copyCurrentBundleFile(fileToCopy: ManifestItem, toDirectory: URL) throws {
|
|
380
388
|
let currentBundleId = getCurrentBundleId()
|
|
381
389
|
let destination = toDirectory.appendingPathComponent(fileToCopy.href)
|
|
@@ -425,8 +433,8 @@ import CommonCrypto
|
|
|
425
433
|
}
|
|
426
434
|
}
|
|
427
435
|
|
|
428
|
-
private func
|
|
429
|
-
let temporaryDirectory =
|
|
436
|
+
private func createTemporaryDownloadDirectory() throws -> URL {
|
|
437
|
+
let temporaryDirectory = buildDownloadsDirectoryUrl().appendingPathComponent(UUID().uuidString)
|
|
430
438
|
try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true, attributes: nil)
|
|
431
439
|
return temporaryDirectory
|
|
432
440
|
}
|
|
@@ -533,8 +541,9 @@ import CommonCrypto
|
|
|
533
541
|
}
|
|
534
542
|
|
|
535
543
|
private func downloadBundleOfTypeManifest(bundleId: String, url: String) async throws {
|
|
536
|
-
// Create a temporary directory
|
|
537
|
-
let temporaryDirectory = try
|
|
544
|
+
// Create a temporary directory that is deleted once the download completed or failed
|
|
545
|
+
let temporaryDirectory = try createTemporaryDownloadDirectory()
|
|
546
|
+
defer { tryDeleteDirectory(temporaryDirectory) }
|
|
538
547
|
// Download the latest manifest
|
|
539
548
|
let latestManifestFile = try await downloadBundleFile(baseUrl: url, href: manifestFileName, directory: temporaryDirectory, callback: nil)
|
|
540
549
|
let latestManifest = try loadManifest(file: latestManifestFile)
|
|
@@ -565,15 +574,17 @@ import CommonCrypto
|
|
|
565
574
|
}
|
|
566
575
|
|
|
567
576
|
private func downloadBundleOfTypeZip(bundleId: String, checksum: String?, signature: String?, url: String) async throws {
|
|
568
|
-
|
|
569
|
-
let
|
|
577
|
+
// Create a temporary directory that is deleted once the download completed or failed
|
|
578
|
+
let temporaryDirectory = try createTemporaryDownloadDirectory()
|
|
579
|
+
defer { tryDeleteDirectory(temporaryDirectory) }
|
|
580
|
+
let zipFile = temporaryDirectory.appendingPathComponent("bundle.zip")
|
|
570
581
|
// Download the bundle
|
|
571
|
-
try await downloadAndVerifyFile(url: url, file:
|
|
582
|
+
try await downloadAndVerifyFile(url: url, file: zipFile, checksum: checksum, signature: signature, callback: { progress in
|
|
572
583
|
let event = DownloadBundleProgressEvent(bundleId: bundleId, downloadedBytes: progress.completedUnitCount, totalBytes: progress.totalUnitCount)
|
|
573
584
|
self.notifyDownloadBundleProgressListeners(event)
|
|
574
585
|
})
|
|
575
586
|
// Add the bundle
|
|
576
|
-
try await addBundleOfTypeZip(bundleId: bundleId, zipFile:
|
|
587
|
+
try await addBundleOfTypeZip(bundleId: bundleId, zipFile: zipFile)
|
|
577
588
|
}
|
|
578
589
|
|
|
579
590
|
private func fetchLatestBundle(_ options: FetchLatestBundleOptions) async throws -> GetLatestBundleResponse? {
|
|
@@ -959,8 +970,23 @@ import CommonCrypto
|
|
|
959
970
|
}
|
|
960
971
|
}
|
|
961
972
|
|
|
962
|
-
private func
|
|
963
|
-
|
|
973
|
+
private func tryDeleteDirectory(_ url: URL) {
|
|
974
|
+
guard FileManager.default.fileExists(atPath: url.path) else {
|
|
975
|
+
return
|
|
976
|
+
}
|
|
977
|
+
do {
|
|
978
|
+
try FileManager.default.removeItem(at: url)
|
|
979
|
+
} catch {
|
|
980
|
+
CAPLog.print("[", LiveUpdatePlugin.tag, "] ", "Failed to delete directory: \(url.path)")
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
private func tryDeleteDownloadsDirectory() {
|
|
985
|
+
tryDeleteDirectory(buildDownloadsDirectoryUrl())
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
private func unzipDownloadedBundle(zipFile: URL) throws -> URL {
|
|
989
|
+
let destinationDirectory = zipFile.deletingLastPathComponent().appendingPathComponent("bundle")
|
|
964
990
|
try FileManager.default.createDirectory(at: destinationDirectory, withIntermediateDirectories: true, attributes: nil)
|
|
965
991
|
try FileManager.default.unzipItem(at: zipFile, to: destinationDirectory)
|
|
966
992
|
return destinationDirectory
|
|
@@ -8,7 +8,7 @@ import Capacitor
|
|
|
8
8
|
@objc(LiveUpdatePlugin)
|
|
9
9
|
public class LiveUpdatePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
10
10
|
public static let tag = "LiveUpdate"
|
|
11
|
-
public static let version = "7.5.
|
|
11
|
+
public static let version = "7.5.1"
|
|
12
12
|
public static let userDefaultsPrefix = "CapawesomeLiveUpdate" // DO NOT CHANGE
|
|
13
13
|
|
|
14
14
|
public let identifier = "LiveUpdatePlugin"
|