@capawesome/capacitor-live-update 6.9.0 → 6.9.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
|
@@ -103,6 +103,7 @@ public class LiveUpdate {
|
|
|
103
103
|
private final SharedPreferences.Editor webViewSettingsEditor;
|
|
104
104
|
|
|
105
105
|
private final String bundlesDirectory = "_capacitor_live_update_bundles"; // DO NOT CHANGE!
|
|
106
|
+
private final String downloadsDirectory = "capawesome_capacitor_live_update_downloads";
|
|
106
107
|
private final Handler rollbackHandler = new Handler();
|
|
107
108
|
private final String manifestFileName = "capawesome-live-update-manifest.json"; // DO NOT CHANGE!
|
|
108
109
|
|
|
@@ -121,6 +122,9 @@ public class LiveUpdate {
|
|
|
121
122
|
// Set the device ID on the HTTP client
|
|
122
123
|
this.httpClient.setDeviceId(getDeviceId());
|
|
123
124
|
|
|
125
|
+
// Delete the leftovers of downloads that were interrupted by a process kill
|
|
126
|
+
tryDeleteDownloadsDirectory();
|
|
127
|
+
|
|
124
128
|
if (config.getEnabled()) {
|
|
125
129
|
boolean wasUpdated = wasUpdated();
|
|
126
130
|
if (wasUpdated) {
|
|
@@ -560,7 +564,10 @@ public class LiveUpdate {
|
|
|
560
564
|
|
|
561
565
|
// Move the bundle directory to the bundles directory
|
|
562
566
|
File bundleDirectory = buildBundleDirectoryFor(bundleId);
|
|
563
|
-
indexHtmlFile.getParentFile().renameTo(bundleDirectory);
|
|
567
|
+
boolean moved = indexHtmlFile.getParentFile().renameTo(bundleDirectory);
|
|
568
|
+
if (!moved) {
|
|
569
|
+
throw new Exception(LiveUpdatePlugin.ERROR_BUNDLE_MOVE_FAILED);
|
|
570
|
+
}
|
|
564
571
|
}
|
|
565
572
|
|
|
566
573
|
private void addBundleOfTypeManifest(@NonNull String bundleId, @NonNull File directory) throws Exception {
|
|
@@ -569,7 +576,7 @@ public class LiveUpdate {
|
|
|
569
576
|
|
|
570
577
|
private void addBundleOfTypeZip(@NonNull String bundleId, @NonNull File zipFile) throws Exception {
|
|
571
578
|
// Unzip the file to the bundle directory
|
|
572
|
-
File unzippedDirectory =
|
|
579
|
+
File unzippedDirectory = unzipDownloadedBundle(zipFile);
|
|
573
580
|
// Add the bundle
|
|
574
581
|
addBundle(bundleId, unzippedDirectory);
|
|
575
582
|
}
|
|
@@ -582,14 +589,8 @@ public class LiveUpdate {
|
|
|
582
589
|
return new File(plugin.getContext().getFilesDir(), bundlesDirectory + "/" + bundleId);
|
|
583
590
|
}
|
|
584
591
|
|
|
585
|
-
private File
|
|
586
|
-
|
|
587
|
-
return new File(plugin.getContext().getCacheDir(), fileName);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
private File buildTemporaryZipFile() {
|
|
591
|
-
String fileName = UUID.randomUUID().toString() + ".zip";
|
|
592
|
-
return new File(plugin.getContext().getCacheDir(), fileName);
|
|
592
|
+
private File buildDownloadsDirectory() {
|
|
593
|
+
return new File(plugin.getContext().getCacheDir(), downloadsDirectory);
|
|
593
594
|
}
|
|
594
595
|
|
|
595
596
|
private void copyCurrentBundleFile(@NonNull ManifestItem fileToCopy, @NonNull File destinationDirectory) throws IOException {
|
|
@@ -676,10 +677,10 @@ public class LiveUpdate {
|
|
|
676
677
|
}
|
|
677
678
|
}
|
|
678
679
|
|
|
679
|
-
private File
|
|
680
|
-
File
|
|
681
|
-
|
|
682
|
-
return
|
|
680
|
+
private File createTemporaryDownloadDirectory() {
|
|
681
|
+
File temporaryDirectory = new File(buildDownloadsDirectory(), UUID.randomUUID().toString());
|
|
682
|
+
temporaryDirectory.mkdirs();
|
|
683
|
+
return temporaryDirectory;
|
|
683
684
|
}
|
|
684
685
|
|
|
685
686
|
private void deleteBundleById(@NonNull String bundleId) {
|
|
@@ -705,6 +706,23 @@ public class LiveUpdate {
|
|
|
705
706
|
file.delete();
|
|
706
707
|
}
|
|
707
708
|
|
|
709
|
+
@NonNull
|
|
710
|
+
private EmptyCallback deleteTemporaryDirectoryOnCompletion(@NonNull File temporaryDirectory, @NonNull EmptyCallback callback) {
|
|
711
|
+
return new EmptyCallback() {
|
|
712
|
+
@Override
|
|
713
|
+
public void success() {
|
|
714
|
+
deleteFileRecursively(temporaryDirectory);
|
|
715
|
+
callback.success();
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
@Override
|
|
719
|
+
public void error(@NonNull Exception exception) {
|
|
720
|
+
deleteFileRecursively(temporaryDirectory);
|
|
721
|
+
callback.error(exception);
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
708
726
|
private void deleteUnusedBundles() {
|
|
709
727
|
String[] bundleIds = getDownloadedBundleIds();
|
|
710
728
|
for (String bundleId : bundleIds) {
|
|
@@ -875,10 +893,10 @@ public class LiveUpdate {
|
|
|
875
893
|
@NonNull String downloadUrl,
|
|
876
894
|
@NonNull EmptyCallback completionCallback
|
|
877
895
|
) {
|
|
896
|
+
// Create a temporary directory that is deleted once the download completed or failed
|
|
897
|
+
File temporaryDirectory = createTemporaryDownloadDirectory();
|
|
898
|
+
EmptyCallback callback = deleteTemporaryDirectoryOnCompletion(temporaryDirectory, completionCallback);
|
|
878
899
|
try {
|
|
879
|
-
// Create a temporary directory
|
|
880
|
-
File temporaryDirectory = createTemporaryDirectory();
|
|
881
|
-
|
|
882
900
|
// Download the latest manifest
|
|
883
901
|
downloadBundleFile(
|
|
884
902
|
downloadUrl,
|
|
@@ -928,31 +946,31 @@ public class LiveUpdate {
|
|
|
928
946
|
try {
|
|
929
947
|
// Add the bundle
|
|
930
948
|
addBundleOfTypeManifest(bundleId, temporaryDirectory);
|
|
931
|
-
|
|
949
|
+
callback.success();
|
|
932
950
|
} catch (Exception e) {
|
|
933
|
-
|
|
951
|
+
callback.error(e);
|
|
934
952
|
}
|
|
935
953
|
}
|
|
936
954
|
|
|
937
955
|
@Override
|
|
938
956
|
public void error(@NonNull Exception exception) {
|
|
939
|
-
|
|
957
|
+
callback.error(exception);
|
|
940
958
|
}
|
|
941
959
|
}
|
|
942
960
|
);
|
|
943
961
|
} catch (Exception e) {
|
|
944
|
-
|
|
962
|
+
callback.error(e);
|
|
945
963
|
}
|
|
946
964
|
}
|
|
947
965
|
|
|
948
966
|
@Override
|
|
949
967
|
public void error(@NonNull Exception exception) {
|
|
950
|
-
|
|
968
|
+
callback.error(exception);
|
|
951
969
|
}
|
|
952
970
|
}
|
|
953
971
|
);
|
|
954
972
|
} catch (Exception e) {
|
|
955
|
-
|
|
973
|
+
callback.error(e);
|
|
956
974
|
}
|
|
957
975
|
}
|
|
958
976
|
|
|
@@ -963,39 +981,42 @@ public class LiveUpdate {
|
|
|
963
981
|
@NonNull String downloadUrl,
|
|
964
982
|
@NonNull EmptyCallback completionCallback
|
|
965
983
|
) {
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
984
|
+
// Create a temporary directory that is deleted once the download completed or failed
|
|
985
|
+
File temporaryDirectory = createTemporaryDownloadDirectory();
|
|
986
|
+
EmptyCallback callback = deleteTemporaryDirectoryOnCompletion(temporaryDirectory, completionCallback);
|
|
987
|
+
File zipFile = new File(temporaryDirectory, "bundle.zip");
|
|
988
|
+
try {
|
|
989
|
+
// Download the bundle
|
|
990
|
+
downloadAndVerifyFile(
|
|
991
|
+
downloadUrl,
|
|
992
|
+
zipFile,
|
|
993
|
+
checksum,
|
|
994
|
+
signature,
|
|
995
|
+
(downloadedBytes, totalBytes) -> {
|
|
996
|
+
DownloadBundleProgressEvent event = new DownloadBundleProgressEvent(bundleId, downloadedBytes, totalBytes);
|
|
997
|
+
notifyDownloadBundleProgressListeners(event);
|
|
998
|
+
},
|
|
999
|
+
new EmptyCallback() {
|
|
1000
|
+
@Override
|
|
1001
|
+
public void success() {
|
|
1002
|
+
try {
|
|
1003
|
+
// Add the bundle
|
|
1004
|
+
addBundleOfTypeZip(bundleId, zipFile);
|
|
1005
|
+
callback.success();
|
|
1006
|
+
} catch (Exception e) {
|
|
1007
|
+
callback.error(e);
|
|
1008
|
+
}
|
|
988
1009
|
}
|
|
989
|
-
}
|
|
990
1010
|
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
completionCallback.error(exception);
|
|
1011
|
+
@Override
|
|
1012
|
+
public void error(@NonNull Exception exception) {
|
|
1013
|
+
callback.error(exception);
|
|
1014
|
+
}
|
|
996
1015
|
}
|
|
997
|
-
|
|
998
|
-
)
|
|
1016
|
+
);
|
|
1017
|
+
} catch (Exception e) {
|
|
1018
|
+
callback.error(e);
|
|
1019
|
+
}
|
|
999
1020
|
}
|
|
1000
1021
|
|
|
1001
1022
|
private void fetchLatestBundleInternal(
|
|
@@ -1458,8 +1479,12 @@ public class LiveUpdate {
|
|
|
1458
1479
|
}
|
|
1459
1480
|
}
|
|
1460
1481
|
|
|
1461
|
-
private
|
|
1462
|
-
|
|
1482
|
+
private void tryDeleteDownloadsDirectory() {
|
|
1483
|
+
deleteFileRecursively(buildDownloadsDirectory());
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
private File unzipDownloadedBundle(@NonNull File zipFile) throws IOException {
|
|
1487
|
+
File destination = new File(zipFile.getParentFile(), "bundle");
|
|
1463
1488
|
String destinationPath = destination.getPath();
|
|
1464
1489
|
ZipFile zip = new ZipFile(zipFile);
|
|
1465
1490
|
// 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
|
@@ -39,12 +39,13 @@ import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.Result;
|
|
|
39
39
|
public class LiveUpdatePlugin extends Plugin {
|
|
40
40
|
|
|
41
41
|
public static final String TAG = "LiveUpdate";
|
|
42
|
-
public static final String VERSION = "6.9.
|
|
42
|
+
public static final String VERSION = "6.9.1";
|
|
43
43
|
public static final String SHARED_PREFERENCES_NAME = "CapawesomeLiveUpdate"; // DO NOT CHANGE
|
|
44
44
|
public static final String ERROR_APP_ID_MISSING = "appId must be configured.";
|
|
45
45
|
public static final String ERROR_BUNDLE_EXISTS = "bundle already exists.";
|
|
46
46
|
public static final String ERROR_BUNDLE_ID_MISSING = "bundleId must be provided.";
|
|
47
47
|
public static final String ERROR_BUNDLE_INDEX_HTML_MISSING = "The bundle does not contain an index.html file.";
|
|
48
|
+
public static final String ERROR_BUNDLE_MOVE_FAILED = "Bundle could not be moved.";
|
|
48
49
|
public static final String ERROR_BUNDLE_NOT_FOUND = "bundle not found.";
|
|
49
50
|
public static final String ERROR_CHECKSUM_CALCULATION_FAILED = "Failed to calculate checksum.";
|
|
50
51
|
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!
|
|
@@ -34,6 +35,9 @@ import CommonCrypto
|
|
|
34
35
|
// Set the device ID on the HTTP client
|
|
35
36
|
httpClient.setDeviceId(getDeviceId())
|
|
36
37
|
|
|
38
|
+
// Delete the leftovers of downloads that were interrupted by a process kill
|
|
39
|
+
tryDeleteDownloadsDirectory()
|
|
40
|
+
|
|
37
41
|
if config.enabled {
|
|
38
42
|
let wasUpdated = wasUpdated()
|
|
39
43
|
if wasUpdated {
|
|
@@ -381,7 +385,7 @@ import CommonCrypto
|
|
|
381
385
|
|
|
382
386
|
private func addBundleOfTypeZip(bundleId: String, zipFile: URL) async throws {
|
|
383
387
|
// Unzip the bundle
|
|
384
|
-
let unzippedDirectory = try self.
|
|
388
|
+
let unzippedDirectory = try self.unzipDownloadedBundle(zipFile: zipFile)
|
|
385
389
|
// Add the bundle
|
|
386
390
|
try self.addBundle(bundleId: bundleId, directory: unzippedDirectory)
|
|
387
391
|
}
|
|
@@ -405,6 +409,10 @@ import CommonCrypto
|
|
|
405
409
|
return url
|
|
406
410
|
}
|
|
407
411
|
|
|
412
|
+
private func buildDownloadsDirectoryUrl() -> URL {
|
|
413
|
+
return cachesDirectoryUrl.appendingPathComponent(downloadsDirectory)
|
|
414
|
+
}
|
|
415
|
+
|
|
408
416
|
private func copyCurrentBundleFile(fileToCopy: ManifestItem, toDirectory: URL) throws {
|
|
409
417
|
let currentBundleId = getCurrentBundleId()
|
|
410
418
|
let destination = toDirectory.appendingPathComponent(fileToCopy.href)
|
|
@@ -454,8 +462,8 @@ import CommonCrypto
|
|
|
454
462
|
}
|
|
455
463
|
}
|
|
456
464
|
|
|
457
|
-
private func
|
|
458
|
-
let temporaryDirectory =
|
|
465
|
+
private func createTemporaryDownloadDirectory() throws -> URL {
|
|
466
|
+
let temporaryDirectory = buildDownloadsDirectoryUrl().appendingPathComponent(UUID().uuidString)
|
|
459
467
|
try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true, attributes: nil)
|
|
460
468
|
return temporaryDirectory
|
|
461
469
|
}
|
|
@@ -562,8 +570,9 @@ import CommonCrypto
|
|
|
562
570
|
}
|
|
563
571
|
|
|
564
572
|
private func downloadBundleOfTypeManifest(bundleId: String, url: String) async throws {
|
|
565
|
-
// Create a temporary directory
|
|
566
|
-
let temporaryDirectory = try
|
|
573
|
+
// Create a temporary directory that is deleted once the download completed or failed
|
|
574
|
+
let temporaryDirectory = try createTemporaryDownloadDirectory()
|
|
575
|
+
defer { tryDeleteDirectory(temporaryDirectory) }
|
|
567
576
|
// Download the latest manifest
|
|
568
577
|
let latestManifestFile = try await downloadBundleFile(baseUrl: url, href: manifestFileName, directory: temporaryDirectory, callback: nil)
|
|
569
578
|
let latestManifest = try loadManifest(file: latestManifestFile)
|
|
@@ -594,15 +603,17 @@ import CommonCrypto
|
|
|
594
603
|
}
|
|
595
604
|
|
|
596
605
|
private func downloadBundleOfTypeZip(bundleId: String, checksum: String?, signature: String?, url: String) async throws {
|
|
597
|
-
|
|
598
|
-
let
|
|
606
|
+
// Create a temporary directory that is deleted once the download completed or failed
|
|
607
|
+
let temporaryDirectory = try createTemporaryDownloadDirectory()
|
|
608
|
+
defer { tryDeleteDirectory(temporaryDirectory) }
|
|
609
|
+
let zipFile = temporaryDirectory.appendingPathComponent("bundle.zip")
|
|
599
610
|
// Download the bundle
|
|
600
|
-
try await downloadAndVerifyFile(url: url, file:
|
|
611
|
+
try await downloadAndVerifyFile(url: url, file: zipFile, checksum: checksum, signature: signature, callback: { progress in
|
|
601
612
|
let event = DownloadBundleProgressEvent(bundleId: bundleId, downloadedBytes: progress.completedUnitCount, totalBytes: progress.totalUnitCount)
|
|
602
613
|
self.notifyDownloadBundleProgressListeners(event)
|
|
603
614
|
})
|
|
604
615
|
// Add the bundle
|
|
605
|
-
try await addBundleOfTypeZip(bundleId: bundleId, zipFile:
|
|
616
|
+
try await addBundleOfTypeZip(bundleId: bundleId, zipFile: zipFile)
|
|
606
617
|
}
|
|
607
618
|
|
|
608
619
|
private func fetchLatestBundle(_ options: FetchLatestBundleOptions) async throws -> GetLatestBundleResponse? {
|
|
@@ -984,8 +995,23 @@ import CommonCrypto
|
|
|
984
995
|
}
|
|
985
996
|
}
|
|
986
997
|
|
|
987
|
-
private func
|
|
988
|
-
|
|
998
|
+
private func tryDeleteDirectory(_ url: URL) {
|
|
999
|
+
guard FileManager.default.fileExists(atPath: url.path) else {
|
|
1000
|
+
return
|
|
1001
|
+
}
|
|
1002
|
+
do {
|
|
1003
|
+
try FileManager.default.removeItem(at: url)
|
|
1004
|
+
} catch {
|
|
1005
|
+
CAPLog.print("[", LiveUpdatePlugin.tag, "] ", "Failed to delete directory: \(url.path)")
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
private func tryDeleteDownloadsDirectory() {
|
|
1010
|
+
tryDeleteDirectory(buildDownloadsDirectoryUrl())
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
private func unzipDownloadedBundle(zipFile: URL) throws -> URL {
|
|
1014
|
+
let destinationDirectory = zipFile.deletingLastPathComponent().appendingPathComponent("bundle")
|
|
989
1015
|
try FileManager.default.createDirectory(at: destinationDirectory, withIntermediateDirectories: true, attributes: nil)
|
|
990
1016
|
try FileManager.default.unzipItem(at: zipFile, to: destinationDirectory)
|
|
991
1017
|
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 = "6.9.
|
|
11
|
+
public static let version = "6.9.1"
|
|
12
12
|
public static let userDefaultsPrefix = "CapawesomeLiveUpdate" // DO NOT CHANGE
|
|
13
13
|
|
|
14
14
|
public let identifier = "LiveUpdatePlugin"
|