@capgo/capacitor-updater 8.47.5 → 8.47.6

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.
@@ -115,15 +115,13 @@ public class DownloadService extends Worker {
115
115
  }
116
116
 
117
117
  StringBuilder sanitized = new StringBuilder();
118
- value
119
- .codePoints()
120
- .forEach((cp) -> {
121
- boolean isVisibleAscii = cp >= 0x20 && cp <= 0x7E;
122
- boolean isIso88591 = cp >= 0xA0 && cp <= 0xFF;
123
- if (isVisibleAscii || isIso88591) {
124
- sanitized.appendCodePoint(cp);
125
- }
126
- });
118
+ value.codePoints().forEach((cp) -> {
119
+ boolean isVisibleAscii = cp >= 0x20 && cp <= 0x7E;
120
+ boolean isIso88591 = cp >= 0xA0 && cp <= 0xFF;
121
+ if (isVisibleAscii || isIso88591) {
122
+ sanitized.appendCodePoint(cp);
123
+ }
124
+ });
127
125
 
128
126
  String result = sanitized.toString().trim();
129
127
  return result.isEmpty() ? "unknown" : result;
@@ -255,27 +253,26 @@ public class DownloadService extends Worker {
255
253
  .post(RequestBody.create(json.toString(), MediaType.get("application/json")))
256
254
  .build();
257
255
 
258
- sharedClient
259
- .newCall(request)
260
- .enqueue(
261
- new Callback() {
262
- @Override
263
- public void onFailure(@NonNull Call call, @NonNull IOException e) {
264
- if (logger != null) {
265
- logger.error("Failed to send stats: " + e.getMessage());
266
- }
256
+ sharedClient.newCall(request).enqueue(
257
+ new Callback() {
258
+ @Override
259
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {
260
+ if (logger != null) {
261
+ logger.error("Failed to send stats: " + e.getMessage());
267
262
  }
263
+ }
268
264
 
269
- @Override
270
- public void onResponse(@NonNull Call call, @NonNull Response response) {
271
- try (ResponseBody body = response.body()) {
272
- // nothing else to do, just closing body
273
- } catch (Exception ignored) {} finally {
274
- response.close();
275
- }
265
+ @Override
266
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
267
+ try (ResponseBody body = response.body()) {
268
+ // nothing else to do, just closing body
269
+ } catch (Exception ignored) {
270
+ } finally {
271
+ response.close();
276
272
  }
277
273
  }
278
- );
274
+ }
275
+ );
279
276
  } catch (Exception e) {
280
277
  if (logger != null) {
281
278
  logger.error("sendStatsAsync error: " + e.getMessage());
@@ -111,49 +111,43 @@ public class ShakeMenu implements ShakeDetector.Listener {
111
111
  AlertDialog dialog = builder.create();
112
112
  dialog.setOnDismissListener((dialogInterface) -> isShowing = false);
113
113
  dialog.show();
114
- dialog
115
- .getButton(AlertDialog.BUTTON_POSITIVE)
116
- .setOnClickListener((view) -> {
117
- setPreviewMenuButtonsEnabled(dialog, false);
118
- new Thread(() -> {
119
- try {
120
- if (!plugin.leavePreviewSessionFromShakeMenu()) {
121
- activity.runOnUiThread(() -> showError("Could not leave the test app."));
122
- }
123
- } catch (Exception e) {
124
- logger.error("Error leaving test app: " + e.getMessage());
125
- activity.runOnUiThread(() -> showError("Error leaving test app: " + e.getMessage()));
126
- } finally {
127
- activity.runOnUiThread(() -> {
128
- dialog.dismiss();
129
- isShowing = false;
130
- });
114
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener((view) -> {
115
+ setPreviewMenuButtonsEnabled(dialog, false);
116
+ new Thread(() -> {
117
+ try {
118
+ if (!plugin.leavePreviewSessionFromShakeMenu()) {
119
+ activity.runOnUiThread(() -> showError("Could not leave the test app."));
131
120
  }
132
- })
133
- .start();
134
- });
135
- dialog
136
- .getButton(AlertDialog.BUTTON_NEUTRAL)
137
- .setOnClickListener((view) -> {
138
- setPreviewMenuButtonsEnabled(dialog, false);
139
- new Thread(() -> {
140
- try {
141
- logger.info("Reloading webview");
142
- if (!plugin.reloadPreviewSessionFromShakeMenu()) {
143
- activity.runOnUiThread(() -> showError("Could not reload the test app."));
144
- }
145
- } catch (Exception e) {
146
- logger.error("Error in Reload action: " + e.getMessage());
147
- activity.runOnUiThread(() -> showError("Error reloading test app: " + e.getMessage()));
148
- } finally {
149
- activity.runOnUiThread(() -> {
150
- dialog.dismiss();
151
- isShowing = false;
152
- });
121
+ } catch (Exception e) {
122
+ logger.error("Error leaving test app: " + e.getMessage());
123
+ activity.runOnUiThread(() -> showError("Error leaving test app: " + e.getMessage()));
124
+ } finally {
125
+ activity.runOnUiThread(() -> {
126
+ dialog.dismiss();
127
+ isShowing = false;
128
+ });
129
+ }
130
+ }).start();
131
+ });
132
+ dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener((view) -> {
133
+ setPreviewMenuButtonsEnabled(dialog, false);
134
+ new Thread(() -> {
135
+ try {
136
+ logger.info("Reloading webview");
137
+ if (!plugin.reloadPreviewSessionFromShakeMenu()) {
138
+ activity.runOnUiThread(() -> showError("Could not reload the test app."));
153
139
  }
154
- })
155
- .start();
156
- });
140
+ } catch (Exception e) {
141
+ logger.error("Error in Reload action: " + e.getMessage());
142
+ activity.runOnUiThread(() -> showError("Error reloading test app: " + e.getMessage()));
143
+ } finally {
144
+ activity.runOnUiThread(() -> {
145
+ dialog.dismiss();
146
+ isShowing = false;
147
+ });
148
+ }
149
+ }).start();
150
+ });
157
151
  } catch (Exception e) {
158
152
  logger.error("Error showing shake menu: " + e.getMessage());
159
153
  isShowing = false;
@@ -351,8 +345,7 @@ public class ShakeMenu implements ShakeDetector.Listener {
351
345
  presentChannelPicker(channels);
352
346
  });
353
347
  });
354
- })
355
- .start();
348
+ }).start();
356
349
  } catch (Exception e) {
357
350
  logger.error("Error showing channel selector: " + e.getMessage());
358
351
  isShowing = false;
@@ -543,13 +536,14 @@ public class ShakeMenu implements ShakeDetector.Listener {
543
536
  String latestKind = getString(latestRes, "kind");
544
537
  String latestMessage = getString(latestRes, "message");
545
538
 
546
- String detail = latestMessage != null && !latestMessage.isEmpty()
547
- ? latestMessage
548
- : latestError != null && !latestError.isEmpty()
549
- ? latestError
550
- : latestKind != null && !latestKind.isEmpty()
551
- ? latestKind
552
- : "server did not provide a message";
539
+ String detail =
540
+ latestMessage != null && !latestMessage.isEmpty()
541
+ ? latestMessage
542
+ : latestError != null && !latestError.isEmpty()
543
+ ? latestError
544
+ : latestKind != null && !latestKind.isEmpty()
545
+ ? latestKind
546
+ : "server did not provide a message";
553
547
 
554
548
  // Handle update errors first (before "no new version" check)
555
549
  if (
@@ -668,8 +662,7 @@ public class ShakeMenu implements ShakeDetector.Listener {
668
662
  });
669
663
  }
670
664
  );
671
- })
672
- .start();
665
+ }).start();
673
666
  } catch (Exception e) {
674
667
  logger.error("Error selecting channel: " + e.getMessage());
675
668
  isShowing = false;
@@ -79,7 +79,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
79
79
  CAPPluginMethod(name: "completeFlexibleUpdate", returnType: CAPPluginReturnPromise)
80
80
  ]
81
81
  public var implementation = CapgoUpdater()
82
- private let pluginVersion: String = "8.47.5"
82
+ private let pluginVersion: String = "8.47.6"
83
83
  static let updateUrlDefault = "https://plugin.capgo.app/updates"
84
84
  static let statsUrlDefault = "https://plugin.capgo.app/stats"
85
85
  static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
@@ -165,6 +165,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
165
165
  public var previewSessionEnabled = false
166
166
  private var previewSessionAlertPending = false
167
167
  private var isLeavingPreviewForIncomingLink = false
168
+ private var previewTransitionClearWorkItem: DispatchWorkItem?
168
169
  let semaphoreReady = DispatchSemaphore(value: 0)
169
170
 
170
171
  private var delayUpdateUtils: DelayUpdateUtils!
@@ -986,7 +987,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
986
987
  let current: BundleInfo = self.implementation.getCurrentBundle()
987
988
  let next: BundleInfo? = self.implementation.getNextBundle()
988
989
 
989
- if let next = next, !next.isErrorStatus(), next.getId() != current.getId() {
990
+ if !self.isPreviewSessionStateActive(),
991
+ let next = next,
992
+ !next.isErrorStatus(),
993
+ next.getId() != current.getId() {
990
994
  let previousState = self.implementation.captureResetState()
991
995
  let previousBundleName = self.implementation.getCurrentBundle().getVersionName()
992
996
  logger.info("Applying pending bundle before reload: \(next.toString())")
@@ -1025,6 +1029,28 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
1025
1029
  }
1026
1030
  }
1027
1031
 
1032
+ private func applyDownloadedBundleForDirectUpdate(_ next: BundleInfo) -> Bool {
1033
+ let previousState = self.implementation.captureResetState()
1034
+ let previousBundleName = self.implementation.getCurrentBundle().getVersionName()
1035
+
1036
+ guard self.implementation.stagePendingReload(bundle: next) else {
1037
+ self.implementation.restoreResetState(previousState)
1038
+ logger.error("Direct update failed to stage downloaded bundle: \(next.toString())")
1039
+ return false
1040
+ }
1041
+
1042
+ if self._reload() {
1043
+ self.implementation.finalizePendingReload(bundle: next, previousBundleName: previousBundleName)
1044
+ _ = self.implementation.setNextBundle(next: Optional<String>.none)
1045
+ return true
1046
+ }
1047
+
1048
+ self.implementation.restoreResetState(previousState)
1049
+ self.restoreLiveBundleStateAfterFailedReload()
1050
+ logger.error("Direct update reload failed after staging bundle: \(next.toString())")
1051
+ return false
1052
+ }
1053
+
1028
1054
  @objc func next(_ call: CAPPluginCall) {
1029
1055
  guard let id = call.getString("id") else {
1030
1056
  logger.error("Next called without id")
@@ -1060,6 +1086,37 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
1060
1086
  }
1061
1087
  }
1062
1088
 
1089
+ private func isPreviewSessionStateActive() -> Bool {
1090
+ self.previewSessionEnabled || self.isLeavingPreviewForIncomingLink || self.implementation.previewSession
1091
+ }
1092
+
1093
+ private func shouldBlockAutoUpdateForPreviewSession() -> Bool {
1094
+ guard self.isPreviewSessionStateActive() else {
1095
+ return false
1096
+ }
1097
+
1098
+ logger.info("Preview session is active. Skipping normal auto-update work.")
1099
+ return true
1100
+ }
1101
+
1102
+ private func clearIncomingPreviewTransition() {
1103
+ self.previewTransitionClearWorkItem?.cancel()
1104
+ self.previewTransitionClearWorkItem = nil
1105
+ self.isLeavingPreviewForIncomingLink = false
1106
+ if !self.previewSessionEnabled {
1107
+ self.implementation.previewSession = false
1108
+ }
1109
+ }
1110
+
1111
+ private func scheduleIncomingPreviewTransitionFallbackClear() {
1112
+ self.previewTransitionClearWorkItem?.cancel()
1113
+ let workItem = DispatchWorkItem { [weak self] in
1114
+ self?.clearIncomingPreviewTransition()
1115
+ }
1116
+ self.previewTransitionClearWorkItem = workItem
1117
+ DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(self.appReadyTimeout), execute: workItem)
1118
+ }
1119
+
1063
1120
  @objc func startPreviewSession(_ call: CAPPluginCall) {
1064
1121
  guard self.allowPreview else {
1065
1122
  logger.error("startPreviewSession called without allowPreview")
@@ -1117,6 +1174,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
1117
1174
  UserDefaults.standard.removeObject(forKey: self.previewPayloadUrlDefaultsKey)
1118
1175
  }
1119
1176
 
1177
+ self.clearIncomingPreviewTransition()
1120
1178
  self.previewSessionEnabled = true
1121
1179
  self.previewSessionAlertPending = true
1122
1180
  self.implementation.previewSession = true
@@ -1159,7 +1217,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
1159
1217
  }
1160
1218
  }
1161
1219
 
1162
- private func leavePreviewSessionWithoutReload() -> Bool {
1220
+ private func leavePreviewSessionWithoutReload(keepPreviewGuard: Bool = false) -> Bool {
1163
1221
  let previewBundle = self.implementation.getCurrentBundle()
1164
1222
  guard let previewFallbackBundle = self.implementation.getPreviewFallbackBundle(), !previewFallbackBundle.isErrorStatus() else {
1165
1223
  logger.error("No preview fallback bundle available")
@@ -1174,12 +1232,50 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
1174
1232
  return false
1175
1233
  }
1176
1234
 
1177
- self.endPreviewSession()
1235
+ self.endPreviewSession(keepPreviewGuard: keepPreviewGuard)
1178
1236
  let restoredNextBundle = self.implementation.getNextBundle()
1179
1237
  self.deletePreviewBundleIfUnused(previewBundle, previewFallbackBundle: previewFallbackBundle, restoredNextBundle: restoredNextBundle)
1180
1238
  return true
1181
1239
  }
1182
1240
 
1241
+ private func leavePreviewSessionForIncomingPreviewLink() -> Bool {
1242
+ let previewBundle = self.implementation.getCurrentBundle()
1243
+ guard let previewFallbackBundle = self.implementation.getPreviewFallbackBundle(), !previewFallbackBundle.isErrorStatus() else {
1244
+ logger.error("No preview fallback bundle available")
1245
+ self.clearIncomingPreviewTransition()
1246
+ return false
1247
+ }
1248
+ guard self.implementation.canSet(bundle: previewFallbackBundle) else {
1249
+ logger.error("Preview fallback bundle is not installable")
1250
+ self.clearIncomingPreviewTransition()
1251
+ return false
1252
+ }
1253
+
1254
+ let previousState = self.implementation.captureResetState()
1255
+ guard self.implementation.stagePreviewFallbackReload(bundle: previewFallbackBundle) else {
1256
+ logger.error("Could not stage preview fallback bundle")
1257
+ self.clearIncomingPreviewTransition()
1258
+ return false
1259
+ }
1260
+
1261
+ let didReload = self._reload()
1262
+ if didReload {
1263
+ self.endPreviewSession(keepPreviewGuard: true)
1264
+ let restoredNextBundle = self.implementation.getNextBundle()
1265
+ self.deletePreviewBundleIfUnused(
1266
+ previewBundle,
1267
+ previewFallbackBundle: previewFallbackBundle,
1268
+ restoredNextBundle: restoredNextBundle
1269
+ )
1270
+ self.scheduleIncomingPreviewTransitionFallbackClear()
1271
+ } else {
1272
+ self.implementation.restoreResetState(previousState)
1273
+ self.restoreLiveBundleStateAfterFailedReload()
1274
+ self.clearIncomingPreviewTransition()
1275
+ }
1276
+ return didReload
1277
+ }
1278
+
1183
1279
  private func deletePreviewBundleIfUnused(
1184
1280
  _ previewBundle: BundleInfo,
1185
1281
  previewFallbackBundle: BundleInfo?,
@@ -1228,7 +1324,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
1228
1324
  return false
1229
1325
  }
1230
1326
 
1231
- private func endPreviewSession() {
1327
+ private func endPreviewSession(keepPreviewGuard: Bool = false) {
1232
1328
  let previousShakeMenuEnabled = UserDefaults.standard.object(forKey: self.previewPreviousShakeMenuDefaultsKey) as? Bool
1233
1329
  ?? getConfig().getBoolean("shakeMenu", false)
1234
1330
  let previousShakeChannelSelectorEnabled = UserDefaults.standard.object(forKey: self.previewPreviousShakeChannelSelectorDefaultsKey) as? Bool
@@ -1239,8 +1335,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
1239
1335
 
1240
1336
  self.previewSessionEnabled = false
1241
1337
  self.previewSessionAlertPending = false
1242
- self.isLeavingPreviewForIncomingLink = false
1243
- self.implementation.previewSession = false
1338
+ if keepPreviewGuard {
1339
+ self.implementation.previewSession = true
1340
+ } else {
1341
+ self.clearIncomingPreviewTransition()
1342
+ }
1244
1343
  self.shakeMenuEnabled = previousShakeMenuEnabled
1245
1344
  self.shakeChannelSelectorEnabled = previousShakeChannelSelectorEnabled
1246
1345
  _ = self.implementation.setPreviewFallbackBundle(fallback: nil)
@@ -1395,7 +1494,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
1395
1494
  self.isLeavingPreviewForIncomingLink = true
1396
1495
  logger.info("Preview deeplink received while preview session is active; restoring fallback before routing")
1397
1496
  DispatchQueue.global(qos: .userInitiated).async {
1398
- let didLeave = self.leavePreviewSessionFromShakeMenu()
1497
+ let didLeave = self.leavePreviewSessionForIncomingPreviewLink()
1399
1498
  if !didLeave {
1400
1499
  self.logger.error("Could not leave preview session before routing incoming preview deeplink")
1401
1500
  self.isLeavingPreviewForIncomingLink = false
@@ -1701,6 +1800,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
1701
1800
  logger.error("Error no url or wrong format")
1702
1801
  return "unavailable"
1703
1802
  }
1803
+ if self.shouldBlockAutoUpdateForPreviewSession() {
1804
+ return "preview_session"
1805
+ }
1704
1806
  if self.isDownloadStuckOrTimedOut() {
1705
1807
  logger.info("Download already in progress, skipping duplicate download request")
1706
1808
  return "already_running"
@@ -1937,6 +2039,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
1937
2039
  let bundle = self.implementation.getCurrentBundle()
1938
2040
  self.implementation.setSuccess(bundle: bundle, autoDeletePrevious: self.autoDeletePrevious)
1939
2041
  logger.info("Current bundle loaded successfully. [notifyAppReady was called] \(bundle.toString())")
2042
+ self.clearIncomingPreviewTransition()
1940
2043
 
1941
2044
  call.resolve(["bundle": bundle.toJSON()])
1942
2045
  }
@@ -1987,6 +2090,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
1987
2090
  // Note: _checkCancelDelay method has been moved to DelayUpdateUtils class
1988
2091
 
1989
2092
  private func _isAutoUpdateEnabled() -> Bool {
2093
+ if self.isPreviewSessionStateActive() {
2094
+ return false
2095
+ }
1990
2096
  let instanceDescriptor = (self.bridge?.viewController as? CAPBridgeViewController)?.instanceDescriptor()
1991
2097
  if instanceDescriptor?.serverURL != nil {
1992
2098
  logger.warn("AutoUpdate is automatic disabled when serverUrl is set.")
@@ -2024,6 +2130,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2024
2130
  logger.info("Built-in bundle is active. We skip the check for notifyAppReady.")
2025
2131
  return
2026
2132
  }
2133
+ if self.isPreviewSessionStateActive() {
2134
+ logger.info("Preview session is active. We skip the check for notifyAppReady.")
2135
+ return
2136
+ }
2027
2137
 
2028
2138
  logger.info("Current bundle is: \(current.toString())")
2029
2139
 
@@ -2693,6 +2803,13 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2693
2803
  return true
2694
2804
  }
2695
2805
 
2806
+ private func clearDownloadInProgressState() {
2807
+ downloadLock.lock()
2808
+ defer { downloadLock.unlock() }
2809
+ downloadInProgress = false
2810
+ downloadStartTime = nil
2811
+ }
2812
+
2696
2813
  func runBackgroundDownloadWork(_ work: @escaping () -> Void) {
2697
2814
  // Live update checks/downloads are user-visible work. Using `.background`
2698
2815
  // lets the scheduler starve them for minutes while the app is active.
@@ -2718,6 +2835,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2718
2835
  }
2719
2836
 
2720
2837
  func backgroundDownload() {
2838
+ if self.shouldBlockAutoUpdateForPreviewSession() {
2839
+ return
2840
+ }
2721
2841
  // Set download in progress flag (thread-safe)
2722
2842
  downloadLock.lock()
2723
2843
  downloadInProgress = true
@@ -2746,10 +2866,19 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2746
2866
  self.runBackgroundDownloadWork {
2747
2867
  // Wait for cleanup to complete before starting download
2748
2868
  self.waitForCleanupIfNeeded()
2869
+ if self.shouldBlockAutoUpdateForPreviewSession() {
2870
+ self.clearDownloadInProgressState()
2871
+ return
2872
+ }
2749
2873
  self.beginDownloadBackgroundTask()
2750
2874
  self.logger.info("Check for update via \(self.updateUrl)")
2751
2875
  let res = self.implementation.getLatest(url: url, channel: nil)
2752
2876
  let current = self.implementation.getCurrentBundle()
2877
+ if self.shouldBlockAutoUpdateForPreviewSession() {
2878
+ self.clearDownloadInProgressState()
2879
+ self.endBackGroundTask()
2880
+ return
2881
+ }
2753
2882
 
2754
2883
  // Handle network errors and other failures first
2755
2884
  let backendError = res.error ?? ""
@@ -2861,6 +2990,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2861
2990
  )
2862
2991
  return
2863
2992
  }
2993
+ if self.shouldBlockAutoUpdateForPreviewSession() {
2994
+ self.clearDownloadInProgressState()
2995
+ self.endBackGroundTask()
2996
+ return
2997
+ }
2864
2998
  res.checksum = try CryptoCipher.decryptChecksum(checksum: res.checksum, publicKey: self.implementation.publicKey)
2865
2999
  CryptoCipher.logChecksumInfo(label: "Bundle checksum", hexChecksum: next.getChecksum())
2866
3000
  CryptoCipher.logChecksumInfo(label: "Expected checksum", hexChecksum: res.checksum)
@@ -2880,6 +3014,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2880
3014
  )
2881
3015
  return
2882
3016
  }
3017
+ if self.shouldBlockAutoUpdateForPreviewSession() {
3018
+ self.clearDownloadInProgressState()
3019
+ self.endBackGroundTask()
3020
+ return
3021
+ }
2883
3022
  let directUpdateAllowed = plannedDirectUpdate && !self.autoSplashscreenTimedOut
2884
3023
  if directUpdateAllowed {
2885
3024
  let delayUpdatePreferences = UserDefaults.standard.string(forKey: DelayUpdateUtils.DELAY_CONDITION_PREFERENCES) ?? "[]"
@@ -2899,7 +3038,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2899
3038
  )
2900
3039
  return
2901
3040
  }
2902
- if self.implementation.set(bundle: next) && self._reload() {
3041
+ if self.applyDownloadedBundleForDirectUpdate(next) {
2903
3042
  self.notifyBundleSet(next)
2904
3043
  self.endBackGroundTaskWithNotif(
2905
3044
  msg: "update installed",
@@ -2909,10 +3048,13 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2909
3048
  plannedDirectUpdate: plannedDirectUpdate
2910
3049
  )
2911
3050
  } else {
3051
+ _ = self.implementation.setNextBundle(next: next.getId())
3052
+ self.notifyListeners("updateAvailable", data: ["bundle": next.toJSON()])
2912
3053
  self.endBackGroundTaskWithNotif(
2913
- msg: "Update install failed",
3054
+ msg: "Direct update reload failed, update will install next background",
2914
3055
  latestVersionName: latestVersionName,
2915
- current: next,
3056
+ current: self.implementation.getCurrentBundle(),
3057
+ error: false,
2916
3058
  plannedDirectUpdate: plannedDirectUpdate
2917
3059
  )
2918
3060
  }
@@ -2968,6 +3110,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
2968
3110
  }
2969
3111
 
2970
3112
  private func installNext() {
3113
+ if self.shouldBlockAutoUpdateForPreviewSession() {
3114
+ return
3115
+ }
2971
3116
  let delayUpdatePreferences = UserDefaults.standard.string(forKey: DelayUpdateUtils.DELAY_CONDITION_PREFERENCES) ?? "[]"
2972
3117
  let delayConditionList: [DelayCondition] = fromJsonArr(json: delayUpdatePreferences).map { obj -> DelayCondition in
2973
3118
  let kind: String = obj.value(forKey: "kind") as! String
@@ -3059,8 +3204,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
3059
3204
  return
3060
3205
  }
3061
3206
  DispatchQueue.global(qos: .utility).async {
3207
+ if self.shouldBlockAutoUpdateForPreviewSession() {
3208
+ return
3209
+ }
3062
3210
  let res = self.implementation.getLatest(url: url, channel: nil)
3063
3211
  let current = self.implementation.getCurrentBundle()
3212
+ if self.shouldBlockAutoUpdateForPreviewSession() {
3213
+ return
3214
+ }
3064
3215
 
3065
3216
  if res.version != current.getVersionName() {
3066
3217
  self.logger.info("New version found: \(res.version)")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-updater",
3
- "version": "8.47.5",
3
+ "version": "8.47.6",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Live update for capacitor apps",
6
6
  "main": "dist/plugin.cjs.js",