@capgo/capacitor-updater 8.52.0 → 8.52.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.
@@ -148,7 +148,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
148
148
  static final int APPLICATION_EXIT_REASON_USER_REQUESTED = 10;
149
149
  static final int APPLICATION_EXIT_REASON_DEPENDENCY_DIED = 12;
150
150
 
151
- private final String pluginVersion = "8.52.0";
151
+ private final String pluginVersion = "8.52.1";
152
152
  private static final String DELAY_CONDITION_PREFERENCES = "";
153
153
 
154
154
  private SharedPreferences.Editor editor;
@@ -150,6 +150,9 @@ public class CapgoUpdater {
150
150
  }
151
151
 
152
152
  private final Map<String, CompletableFuture<BundleInfo>> downloadFutures = new ConcurrentHashMap<>();
153
+ // Versions whose WorkManager download is observed by this process. WorkManager persists work across a
154
+ // process kill, but the observer that finishes the download does not survive it.
155
+ private final Set<String> observedDownloadVersions = ConcurrentHashMap.newKeySet();
153
156
  private final ExecutorService io = Executors.newSingleThreadExecutor();
154
157
 
155
158
  public CapgoUpdater(Logger logger) {
@@ -275,6 +278,19 @@ public class CapgoUpdater {
275
278
  return !directInstall && !previewSession;
276
279
  }
277
280
 
281
+ static boolean shouldRestartOrphanedDownload(final boolean observedByThisProcess) {
282
+ return !observedByThisProcess;
283
+ }
284
+
285
+ static boolean shouldReleaseLaunchWhileRetrying(
286
+ final boolean awaitedByCaller,
287
+ final boolean setNext,
288
+ final boolean directUpdate,
289
+ final boolean previewSession
290
+ ) {
291
+ return !awaitedByCaller && setNext && directUpdate && !previewSession;
292
+ }
293
+
278
294
  static String launchDownloadReadyStatus(final boolean success, final boolean setNext) {
279
295
  if (!success) {
280
296
  return "Error downloading file";
@@ -746,13 +762,14 @@ public class CapgoUpdater {
746
762
  this.backgroundDownloadSettled(readyBundle, launchDownloadReadyStatus(success, setNext));
747
763
  }
748
764
 
749
- private void observeWorkProgress(Context context, String id, boolean setNext) {
765
+ private void observeWorkProgress(Context context, String id, String observedVersion, boolean setNext) {
750
766
  if (!(context instanceof LifecycleOwner)) {
751
767
  logger.error("Context is not a LifecycleOwner, cannot observe work progress");
752
768
  return;
753
769
  }
754
770
 
755
771
  final AtomicBoolean terminalHandled = new AtomicBoolean(false);
772
+ final AtomicBoolean launchReleasedWhileRetrying = new AtomicBoolean(false);
756
773
  activity.runOnUiThread(() -> {
757
774
  WorkManager.getInstance(context)
758
775
  .getWorkInfosByTagLiveData(id)
@@ -763,12 +780,33 @@ public class CapgoUpdater {
763
780
  Data progress = workInfo.getProgress();
764
781
 
765
782
  switch (workInfo.getState()) {
783
+ case ENQUEUED:
784
+ // A run attempt already happened, so WorkManager scheduled a retry (network lost midway,
785
+ // 5xx, ...). Retries back off without limit, so a direct install must not keep the launch
786
+ // (and its splashscreen) blocked until the network comes back.
787
+ if (
788
+ workInfo.getRunAttemptCount() > 0 &&
789
+ shouldReleaseLaunchWhileRetrying(
790
+ downloadFutures.containsKey(id),
791
+ setNext,
792
+ Boolean.TRUE.equals(CapgoUpdater.this.directUpdate),
793
+ CapgoUpdater.this.previewSession
794
+ ) &&
795
+ launchReleasedWhileRetrying.compareAndSet(false, true)
796
+ ) {
797
+ logger.warn("Direct update download is retrying, continuing launch on the current bundle");
798
+ // Same fallback as the autoSplashscreen timeout: a later success installs on next background.
799
+ CapgoUpdater.this.directUpdate = false;
800
+ io.execute(() -> backgroundDownloadSettled(getCurrentBundle(), launchDownloadReadyStatus(false, false)));
801
+ }
802
+ break;
766
803
  case RUNNING:
767
804
  int percent = progress.getInt(DownloadService.PERCENT, 0);
768
805
  notifyDownload(id, percent);
769
806
  break;
770
807
  case SUCCEEDED:
771
808
  if (!terminalHandled.compareAndSet(false, true)) break;
809
+ observedDownloadVersions.remove(observedVersion);
772
810
  logger.info("Download succeeded: " + workInfo.getState());
773
811
  Data outputData = workInfo.getOutputData();
774
812
  String dest = outputData.getString(DownloadService.FILEDEST);
@@ -815,11 +853,14 @@ public class CapgoUpdater {
815
853
  future.complete(resultBundle);
816
854
  }
817
855
  final BundleInfo readyBundle = success && setNext ? resultBundle : null;
818
- notifyLaunchDownloadReady(future != null, success, directInstall, previewSession, setNext, readyBundle);
856
+ if (!launchReleasedWhileRetrying.get()) {
857
+ notifyLaunchDownloadReady(future != null, success, directInstall, previewSession, setNext, readyBundle);
858
+ }
819
859
  });
820
860
  break;
821
861
  case FAILED:
822
862
  if (!terminalHandled.compareAndSet(false, true)) break;
863
+ observedDownloadVersions.remove(observedVersion);
823
864
  Data failedData = workInfo.getOutputData();
824
865
  String error = failedData.getString(DownloadService.ERROR);
825
866
  logger.error("Download failed");
@@ -854,11 +895,14 @@ public class CapgoUpdater {
854
895
  if (failedFuture != null) {
855
896
  failedFuture.complete(failedBundle);
856
897
  }
857
- notifyLaunchDownloadReady(failedFuture != null, false, false, false, false, null);
898
+ if (!launchReleasedWhileRetrying.get()) {
899
+ notifyLaunchDownloadReady(failedFuture != null, false, false, false, false, null);
900
+ }
858
901
  });
859
902
  break;
860
903
  case CANCELLED:
861
904
  if (!terminalHandled.compareAndSet(false, true)) break;
905
+ observedDownloadVersions.remove(observedVersion);
862
906
  DataManager.getInstance().clearManifest(id);
863
907
  CompletableFuture<BundleInfo> cancelledFuture = downloadFutures.remove(id);
864
908
  if (cancelledFuture != null) {
@@ -884,7 +928,8 @@ public class CapgoUpdater {
884
928
  logger.error("Activity is null, cannot observe work progress");
885
929
  return;
886
930
  }
887
- observeWorkProgress(this.activity, id, setNext);
931
+ observedDownloadVersions.add(version);
932
+ observeWorkProgress(this.activity, id, version, setNext);
888
933
 
889
934
  if (manifest != null) {
890
935
  DataManager.getInstance().setManifest(id, manifest);
@@ -1534,6 +1579,13 @@ public class CapgoUpdater {
1534
1579
  return;
1535
1580
  }
1536
1581
  logger.info("Retrying failed download for version: " + version);
1582
+ } else if (shouldRestartOrphanedDownload(observedDownloadVersions.contains(version))) {
1583
+ // Left over from a killed process: nothing would finish it or release the launch, so start over.
1584
+ if (!DownloadWorkerManager.cancelVersionDownloadAndAwait(this.activity, version)) {
1585
+ logger.error("Failed to cancel orphaned download before restarting it");
1586
+ return;
1587
+ }
1588
+ logger.info("Restarting download orphaned by a previous process for version: " + version);
1537
1589
  } else {
1538
1590
  logger.info("Version already downloading: " + version);
1539
1591
  return;
@@ -95,7 +95,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
95
95
  deinit {
96
96
  implementation.shutdown()
97
97
  }
98
- private let pluginVersion: String = "8.52.0"
98
+ private let pluginVersion: String = "8.52.1"
99
99
  private let launchStartedAtMs = Int64(Date().timeIntervalSince1970 * 1000)
100
100
  static let updateUrlDefault = "https://plugin.capgo.app/updates"
101
101
  static let statsUrlDefault = "https://plugin.capgo.app/stats"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-updater",
3
- "version": "8.52.0",
3
+ "version": "8.52.1",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Live update for capacitor apps",
6
6
  "main": "dist/plugin.cjs.js",