@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.
@@ -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 = unzipFile(zipFile);
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 buildTemporaryDirectory() {
586
- String fileName = UUID.randomUUID().toString();
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 createTemporaryDirectory() {
680
- File file = buildTemporaryDirectory();
681
- file.mkdir();
682
- return file;
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
- completionCallback.success();
949
+ callback.success();
932
950
  } catch (Exception e) {
933
- completionCallback.error(e);
951
+ callback.error(e);
934
952
  }
935
953
  }
936
954
 
937
955
  @Override
938
956
  public void error(@NonNull Exception exception) {
939
- completionCallback.error(exception);
957
+ callback.error(exception);
940
958
  }
941
959
  }
942
960
  );
943
961
  } catch (Exception e) {
944
- completionCallback.error(e);
962
+ callback.error(e);
945
963
  }
946
964
  }
947
965
 
948
966
  @Override
949
967
  public void error(@NonNull Exception exception) {
950
- completionCallback.error(exception);
968
+ callback.error(exception);
951
969
  }
952
970
  }
953
971
  );
954
972
  } catch (Exception e) {
955
- completionCallback.error(e);
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
- File file = buildTemporaryZipFile();
967
- // Download the bundle
968
- downloadAndVerifyFile(
969
- downloadUrl,
970
- file,
971
- checksum,
972
- signature,
973
- (downloadedBytes, totalBytes) -> {
974
- DownloadBundleProgressEvent event = new DownloadBundleProgressEvent(bundleId, downloadedBytes, totalBytes);
975
- notifyDownloadBundleProgressListeners(event);
976
- },
977
- new EmptyCallback() {
978
- @Override
979
- public void success() {
980
- try {
981
- // Add the bundle
982
- addBundleOfTypeZip(bundleId, file);
983
- // Delete the temporary file
984
- file.delete();
985
- completionCallback.success();
986
- } catch (Exception e) {
987
- completionCallback.error(e);
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
- @Override
992
- public void error(@NonNull Exception exception) {
993
- // Delete the temporary file on error
994
- file.delete();
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 File unzipFile(@NonNull File zipFile) throws IOException {
1462
- File destination = buildTemporaryDirectory();
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
@@ -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.0";
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.unzipFile(zipFile: zipFile)
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 createTemporaryDirectory() throws -> URL {
458
- let temporaryDirectory = cachesDirectoryUrl.appendingPathComponent(UUID().uuidString)
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 createTemporaryDirectory()
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
- let timestamp = String(Int(Date().timeIntervalSince1970))
598
- let temporaryZipFileUrl = self.cachesDirectoryUrl.appendingPathComponent(timestamp + ".zip")
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: temporaryZipFileUrl, checksum: checksum, signature: signature, callback: { progress in
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: temporaryZipFileUrl)
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 unzipFile(zipFile: URL) throws -> URL {
988
- let destinationDirectory = zipFile.deletingPathExtension()
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.0"
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"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capawesome/capacitor-live-update",
3
- "version": "6.9.0",
3
+ "version": "6.9.1",
4
4
  "description": "Capacitor plugin to update your app remotely in real-time.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",