@capgo/capacitor-updater 8.47.5 → 8.47.7
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/README.md +13 -20
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +380 -92
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +279 -259
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +23 -26
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeMenu.java +111 -62
- package/dist/docs.json +5 -5
- package/dist/esm/definitions.d.ts +16 -24
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +343 -46
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/InternalUtils.swift +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +25 -8
- package/package.json +1 -1
|
@@ -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.
|
|
82
|
+
private let pluginVersion: String = "8.47.7"
|
|
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"
|
|
@@ -89,6 +89,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
89
89
|
static let autoUpdateModeLaunch = "onLaunch"
|
|
90
90
|
static let autoUpdateModeAlways = "always"
|
|
91
91
|
static let autoUpdateModeOnlyDownload = "onlyDownload"
|
|
92
|
+
private static let previewLoaderTimeoutMs = 60000
|
|
92
93
|
private let keepUrlPathFlagKey = "__capgo_keep_url_path_after_reload"
|
|
93
94
|
private let customIdDefaultsKey = "CapacitorUpdater.customId"
|
|
94
95
|
private let updateUrlDefaultsKey = "CapacitorUpdater.updateUrl"
|
|
@@ -131,6 +132,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
131
132
|
private var autoSplashscreenTimeoutWorkItem: DispatchWorkItem?
|
|
132
133
|
private var splashscreenLoaderView: UIActivityIndicatorView?
|
|
133
134
|
private var splashscreenLoaderContainer: UIView?
|
|
135
|
+
private var previewTransitionLoaderView: UIActivityIndicatorView?
|
|
136
|
+
private var previewTransitionLoaderContainer: UIView?
|
|
137
|
+
private var previewTransitionLoaderTimeoutWorkItem: DispatchWorkItem?
|
|
138
|
+
private var previewTransitionLoaderRequested = false
|
|
134
139
|
private let splashscreenPluginName = "SplashScreen"
|
|
135
140
|
private let splashscreenRetryDelayMilliseconds = 100
|
|
136
141
|
private let splashscreenMaxRetries = 20
|
|
@@ -165,6 +170,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
165
170
|
public var previewSessionEnabled = false
|
|
166
171
|
private var previewSessionAlertPending = false
|
|
167
172
|
private var isLeavingPreviewForIncomingLink = false
|
|
173
|
+
private var previewTransitionClearWorkItem: DispatchWorkItem?
|
|
168
174
|
let semaphoreReady = DispatchSemaphore(value: 0)
|
|
169
175
|
|
|
170
176
|
private var delayUpdateUtils: DelayUpdateUtils!
|
|
@@ -242,7 +248,8 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
242
248
|
if previewSessionEnabled {
|
|
243
249
|
previewSessionAlertPending = UserDefaults.standard.object(forKey: previewSessionAlertPendingDefaultsKey) as? Bool ?? true
|
|
244
250
|
shakeMenuEnabled = true
|
|
245
|
-
shakeChannelSelectorEnabled =
|
|
251
|
+
shakeChannelSelectorEnabled = UserDefaults.standard.object(forKey: previewPreviousShakeChannelSelectorDefaultsKey) as? Bool
|
|
252
|
+
?? shakeChannelSelectorEnabled
|
|
246
253
|
}
|
|
247
254
|
periodCheckDelay = Self.normalizedPeriodCheckDelaySeconds(getConfig().getInt("periodCheckDelay", 0))
|
|
248
255
|
|
|
@@ -986,7 +993,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
986
993
|
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
987
994
|
let next: BundleInfo? = self.implementation.getNextBundle()
|
|
988
995
|
|
|
989
|
-
if
|
|
996
|
+
if !self.isPreviewSessionStateActive(),
|
|
997
|
+
let next = next,
|
|
998
|
+
!next.isErrorStatus(),
|
|
999
|
+
next.getId() != current.getId() {
|
|
990
1000
|
let previousState = self.implementation.captureResetState()
|
|
991
1001
|
let previousBundleName = self.implementation.getCurrentBundle().getVersionName()
|
|
992
1002
|
logger.info("Applying pending bundle before reload: \(next.toString())")
|
|
@@ -1025,6 +1035,28 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1025
1035
|
}
|
|
1026
1036
|
}
|
|
1027
1037
|
|
|
1038
|
+
private func applyDownloadedBundleForDirectUpdate(_ next: BundleInfo) -> Bool {
|
|
1039
|
+
let previousState = self.implementation.captureResetState()
|
|
1040
|
+
let previousBundleName = self.implementation.getCurrentBundle().getVersionName()
|
|
1041
|
+
|
|
1042
|
+
guard self.implementation.stagePendingReload(bundle: next) else {
|
|
1043
|
+
self.implementation.restoreResetState(previousState)
|
|
1044
|
+
logger.error("Direct update failed to stage downloaded bundle: \(next.toString())")
|
|
1045
|
+
return false
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
if self._reload() {
|
|
1049
|
+
self.implementation.finalizePendingReload(bundle: next, previousBundleName: previousBundleName)
|
|
1050
|
+
_ = self.implementation.setNextBundle(next: Optional<String>.none)
|
|
1051
|
+
return true
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
self.implementation.restoreResetState(previousState)
|
|
1055
|
+
self.restoreLiveBundleStateAfterFailedReload()
|
|
1056
|
+
logger.error("Direct update reload failed after staging bundle: \(next.toString())")
|
|
1057
|
+
return false
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1028
1060
|
@objc func next(_ call: CAPPluginCall) {
|
|
1029
1061
|
guard let id = call.getString("id") else {
|
|
1030
1062
|
logger.error("Next called without id")
|
|
@@ -1060,8 +1092,40 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1060
1092
|
}
|
|
1061
1093
|
}
|
|
1062
1094
|
|
|
1095
|
+
private func isPreviewSessionStateActive() -> Bool {
|
|
1096
|
+
self.previewSessionEnabled || self.isLeavingPreviewForIncomingLink || self.implementation.previewSession
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
private func shouldBlockAutoUpdateForPreviewSession() -> Bool {
|
|
1100
|
+
guard self.isPreviewSessionStateActive() else {
|
|
1101
|
+
return false
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
logger.info("Preview session is active. Skipping normal auto-update work.")
|
|
1105
|
+
return true
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
private func clearIncomingPreviewTransition() {
|
|
1109
|
+
self.previewTransitionClearWorkItem?.cancel()
|
|
1110
|
+
self.previewTransitionClearWorkItem = nil
|
|
1111
|
+
self.isLeavingPreviewForIncomingLink = false
|
|
1112
|
+
if !self.previewSessionEnabled {
|
|
1113
|
+
self.implementation.previewSession = false
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
private func scheduleIncomingPreviewTransitionFallbackClear() {
|
|
1118
|
+
self.previewTransitionClearWorkItem?.cancel()
|
|
1119
|
+
let workItem = DispatchWorkItem { [weak self] in
|
|
1120
|
+
self?.clearIncomingPreviewTransition()
|
|
1121
|
+
}
|
|
1122
|
+
self.previewTransitionClearWorkItem = workItem
|
|
1123
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(self.appReadyTimeout), execute: workItem)
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1063
1126
|
@objc func startPreviewSession(_ call: CAPPluginCall) {
|
|
1064
1127
|
guard self.allowPreview else {
|
|
1128
|
+
self.hidePreviewTransitionLoader(reason: "preview-session-not-allowed")
|
|
1065
1129
|
logger.error("startPreviewSession called without allowPreview")
|
|
1066
1130
|
call.reject("startPreviewSession not allowed. Set allowPreview to true in your config to enable it.")
|
|
1067
1131
|
return
|
|
@@ -1070,6 +1134,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1070
1134
|
let rawPayloadUrl = call.getString("payloadUrl")
|
|
1071
1135
|
let previewPayloadUrl = self.normalizedPreviewPayloadUrl(rawPayloadUrl)
|
|
1072
1136
|
if let rawPayloadUrl = rawPayloadUrl, !rawPayloadUrl.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, previewPayloadUrl == nil {
|
|
1137
|
+
self.hidePreviewTransitionLoader(reason: "preview-session-invalid-payload")
|
|
1073
1138
|
logger.error("startPreviewSession called with invalid payloadUrl")
|
|
1074
1139
|
call.reject("Invalid preview payloadUrl")
|
|
1075
1140
|
return
|
|
@@ -1078,6 +1143,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1078
1143
|
if !self.previewSessionEnabled {
|
|
1079
1144
|
let current = self.implementation.getCurrentBundle()
|
|
1080
1145
|
guard self.implementation.setPreviewFallbackBundle(fallback: current.getId()) else {
|
|
1146
|
+
self.hidePreviewTransitionLoader(reason: "preview-session-fallback-failed")
|
|
1081
1147
|
logger.error("Could not save current bundle as preview fallback")
|
|
1082
1148
|
call.reject("Could not save current bundle as preview fallback")
|
|
1083
1149
|
return
|
|
@@ -1117,11 +1183,12 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1117
1183
|
UserDefaults.standard.removeObject(forKey: self.previewPayloadUrlDefaultsKey)
|
|
1118
1184
|
}
|
|
1119
1185
|
|
|
1186
|
+
self.clearIncomingPreviewTransition()
|
|
1187
|
+
self.hidePreviewTransitionLoader(reason: "preview-session-started")
|
|
1120
1188
|
self.previewSessionEnabled = true
|
|
1121
1189
|
self.previewSessionAlertPending = true
|
|
1122
1190
|
self.implementation.previewSession = true
|
|
1123
1191
|
self.shakeMenuEnabled = true
|
|
1124
|
-
self.shakeChannelSelectorEnabled = false
|
|
1125
1192
|
UserDefaults.standard.set(true, forKey: self.previewSessionDefaultsKey)
|
|
1126
1193
|
UserDefaults.standard.set(true, forKey: self.previewSessionAlertPendingDefaultsKey)
|
|
1127
1194
|
UserDefaults.standard.synchronize()
|
|
@@ -1131,8 +1198,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1131
1198
|
func leavePreviewSessionFromShakeMenu() -> Bool {
|
|
1132
1199
|
let previewBundle = self.implementation.getCurrentBundle()
|
|
1133
1200
|
|
|
1201
|
+
self.showPreviewTransitionLoader(reason: "leave-preview-session")
|
|
1134
1202
|
let didReset = self.resetToPreviewFallbackBundle()
|
|
1135
1203
|
guard didReset else {
|
|
1204
|
+
self.hidePreviewTransitionLoader(reason: "leave-preview-session-failed")
|
|
1136
1205
|
return false
|
|
1137
1206
|
}
|
|
1138
1207
|
|
|
@@ -1152,14 +1221,16 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1152
1221
|
}
|
|
1153
1222
|
|
|
1154
1223
|
self.isLeavingPreviewForIncomingLink = true
|
|
1224
|
+
self.showPreviewTransitionLoader(reason: "preview-launch-deeplink")
|
|
1155
1225
|
logger.info("Preview deeplink launch detected while preview session is active; restoring fallback before initial load")
|
|
1156
1226
|
if !self.leavePreviewSessionWithoutReload() {
|
|
1157
1227
|
logger.error("Could not leave preview session before initial preview deeplink routing")
|
|
1158
1228
|
self.isLeavingPreviewForIncomingLink = false
|
|
1229
|
+
self.hidePreviewTransitionLoader(reason: "preview-launch-deeplink-failed")
|
|
1159
1230
|
}
|
|
1160
1231
|
}
|
|
1161
1232
|
|
|
1162
|
-
private func leavePreviewSessionWithoutReload() -> Bool {
|
|
1233
|
+
private func leavePreviewSessionWithoutReload(keepPreviewGuard: Bool = false) -> Bool {
|
|
1163
1234
|
let previewBundle = self.implementation.getCurrentBundle()
|
|
1164
1235
|
guard let previewFallbackBundle = self.implementation.getPreviewFallbackBundle(), !previewFallbackBundle.isErrorStatus() else {
|
|
1165
1236
|
logger.error("No preview fallback bundle available")
|
|
@@ -1174,12 +1245,55 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1174
1245
|
return false
|
|
1175
1246
|
}
|
|
1176
1247
|
|
|
1177
|
-
self.endPreviewSession()
|
|
1248
|
+
self.endPreviewSession(keepPreviewGuard: keepPreviewGuard)
|
|
1178
1249
|
let restoredNextBundle = self.implementation.getNextBundle()
|
|
1179
1250
|
self.deletePreviewBundleIfUnused(previewBundle, previewFallbackBundle: previewFallbackBundle, restoredNextBundle: restoredNextBundle)
|
|
1180
1251
|
return true
|
|
1181
1252
|
}
|
|
1182
1253
|
|
|
1254
|
+
private func leavePreviewSessionForIncomingPreviewLink() -> Bool {
|
|
1255
|
+
self.showPreviewTransitionLoader(reason: "incoming-preview-deeplink")
|
|
1256
|
+
let previewBundle = self.implementation.getCurrentBundle()
|
|
1257
|
+
guard let previewFallbackBundle = self.implementation.getPreviewFallbackBundle(), !previewFallbackBundle.isErrorStatus() else {
|
|
1258
|
+
logger.error("No preview fallback bundle available")
|
|
1259
|
+
self.clearIncomingPreviewTransition()
|
|
1260
|
+
self.hidePreviewTransitionLoader(reason: "incoming-preview-deeplink-failed")
|
|
1261
|
+
return false
|
|
1262
|
+
}
|
|
1263
|
+
guard self.implementation.canSet(bundle: previewFallbackBundle) else {
|
|
1264
|
+
logger.error("Preview fallback bundle is not installable")
|
|
1265
|
+
self.clearIncomingPreviewTransition()
|
|
1266
|
+
self.hidePreviewTransitionLoader(reason: "incoming-preview-deeplink-failed")
|
|
1267
|
+
return false
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
let previousState = self.implementation.captureResetState()
|
|
1271
|
+
guard self.implementation.stagePreviewFallbackReload(bundle: previewFallbackBundle) else {
|
|
1272
|
+
logger.error("Could not stage preview fallback bundle")
|
|
1273
|
+
self.clearIncomingPreviewTransition()
|
|
1274
|
+
self.hidePreviewTransitionLoader(reason: "incoming-preview-deeplink-failed")
|
|
1275
|
+
return false
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
let didReload = self._reload()
|
|
1279
|
+
if didReload {
|
|
1280
|
+
self.endPreviewSession(keepPreviewGuard: true)
|
|
1281
|
+
let restoredNextBundle = self.implementation.getNextBundle()
|
|
1282
|
+
self.deletePreviewBundleIfUnused(
|
|
1283
|
+
previewBundle,
|
|
1284
|
+
previewFallbackBundle: previewFallbackBundle,
|
|
1285
|
+
restoredNextBundle: restoredNextBundle
|
|
1286
|
+
)
|
|
1287
|
+
self.scheduleIncomingPreviewTransitionFallbackClear()
|
|
1288
|
+
} else {
|
|
1289
|
+
self.implementation.restoreResetState(previousState)
|
|
1290
|
+
self.restoreLiveBundleStateAfterFailedReload()
|
|
1291
|
+
self.clearIncomingPreviewTransition()
|
|
1292
|
+
self.hidePreviewTransitionLoader(reason: "incoming-preview-deeplink-reload-failed")
|
|
1293
|
+
}
|
|
1294
|
+
return didReload
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1183
1297
|
private func deletePreviewBundleIfUnused(
|
|
1184
1298
|
_ previewBundle: BundleInfo,
|
|
1185
1299
|
previewFallbackBundle: BundleInfo?,
|
|
@@ -1193,11 +1307,18 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1193
1307
|
}
|
|
1194
1308
|
|
|
1195
1309
|
func reloadPreviewSessionFromShakeMenu() -> Bool {
|
|
1310
|
+
self.showPreviewTransitionLoader(reason: "reload-preview-session")
|
|
1311
|
+
let didReload: Bool
|
|
1196
1312
|
if let payloadUrl = self.storedPreviewPayloadUrl() {
|
|
1197
|
-
|
|
1313
|
+
didReload = self.refreshPreviewSessionFromPayloadUrl(payloadUrl)
|
|
1314
|
+
} else {
|
|
1315
|
+
didReload = self._reload()
|
|
1198
1316
|
}
|
|
1199
1317
|
|
|
1200
|
-
|
|
1318
|
+
if !didReload {
|
|
1319
|
+
self.hidePreviewTransitionLoader(reason: "reload-preview-session-failed")
|
|
1320
|
+
}
|
|
1321
|
+
return didReload
|
|
1201
1322
|
}
|
|
1202
1323
|
|
|
1203
1324
|
func hasActivePreviewSession() -> Bool {
|
|
@@ -1228,7 +1349,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1228
1349
|
return false
|
|
1229
1350
|
}
|
|
1230
1351
|
|
|
1231
|
-
private func endPreviewSession() {
|
|
1352
|
+
private func endPreviewSession(keepPreviewGuard: Bool = false) {
|
|
1232
1353
|
let previousShakeMenuEnabled = UserDefaults.standard.object(forKey: self.previewPreviousShakeMenuDefaultsKey) as? Bool
|
|
1233
1354
|
?? getConfig().getBoolean("shakeMenu", false)
|
|
1234
1355
|
let previousShakeChannelSelectorEnabled = UserDefaults.standard.object(forKey: self.previewPreviousShakeChannelSelectorDefaultsKey) as? Bool
|
|
@@ -1239,8 +1360,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1239
1360
|
|
|
1240
1361
|
self.previewSessionEnabled = false
|
|
1241
1362
|
self.previewSessionAlertPending = false
|
|
1242
|
-
|
|
1243
|
-
|
|
1363
|
+
if keepPreviewGuard {
|
|
1364
|
+
self.implementation.previewSession = true
|
|
1365
|
+
} else {
|
|
1366
|
+
self.clearIncomingPreviewTransition()
|
|
1367
|
+
}
|
|
1244
1368
|
self.shakeMenuEnabled = previousShakeMenuEnabled
|
|
1245
1369
|
self.shakeChannelSelectorEnabled = previousShakeChannelSelectorEnabled
|
|
1246
1370
|
_ = self.implementation.setPreviewFallbackBundle(fallback: nil)
|
|
@@ -1271,6 +1395,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1271
1395
|
self.previewSessionAlertPending = false
|
|
1272
1396
|
self.isLeavingPreviewForIncomingLink = false
|
|
1273
1397
|
self.implementation.previewSession = false
|
|
1398
|
+
self.hidePreviewTransitionLoader(reason: "preview-session-disabled")
|
|
1274
1399
|
self.shakeMenuEnabled = getConfig().getBoolean("shakeMenu", false)
|
|
1275
1400
|
self.shakeChannelSelectorEnabled = getConfig().getBoolean("allowShakeChannelSelector", false)
|
|
1276
1401
|
self.clearPreviewSessionPreferences()
|
|
@@ -1393,12 +1518,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1393
1518
|
}
|
|
1394
1519
|
|
|
1395
1520
|
self.isLeavingPreviewForIncomingLink = true
|
|
1521
|
+
self.showPreviewTransitionLoader(reason: "incoming-preview-deeplink")
|
|
1396
1522
|
logger.info("Preview deeplink received while preview session is active; restoring fallback before routing")
|
|
1397
1523
|
DispatchQueue.global(qos: .userInitiated).async {
|
|
1398
|
-
let didLeave = self.
|
|
1524
|
+
let didLeave = self.leavePreviewSessionForIncomingPreviewLink()
|
|
1399
1525
|
if !didLeave {
|
|
1400
1526
|
self.logger.error("Could not leave preview session before routing incoming preview deeplink")
|
|
1401
1527
|
self.isLeavingPreviewForIncomingLink = false
|
|
1528
|
+
self.hidePreviewTransitionLoader(reason: "incoming-preview-deeplink-failed")
|
|
1402
1529
|
}
|
|
1403
1530
|
}
|
|
1404
1531
|
}
|
|
@@ -1701,6 +1828,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1701
1828
|
logger.error("Error no url or wrong format")
|
|
1702
1829
|
return "unavailable"
|
|
1703
1830
|
}
|
|
1831
|
+
if self.shouldBlockAutoUpdateForPreviewSession() {
|
|
1832
|
+
return "preview_session"
|
|
1833
|
+
}
|
|
1704
1834
|
if self.isDownloadStuckOrTimedOut() {
|
|
1705
1835
|
logger.info("Download already in progress, skipping duplicate download request")
|
|
1706
1836
|
return "already_running"
|
|
@@ -1937,6 +2067,8 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1937
2067
|
let bundle = self.implementation.getCurrentBundle()
|
|
1938
2068
|
self.implementation.setSuccess(bundle: bundle, autoDeletePrevious: self.autoDeletePrevious)
|
|
1939
2069
|
logger.info("Current bundle loaded successfully. [notifyAppReady was called] \(bundle.toString())")
|
|
2070
|
+
self.clearIncomingPreviewTransition()
|
|
2071
|
+
self.hidePreviewTransitionLoader(reason: "notify-app-ready")
|
|
1940
2072
|
|
|
1941
2073
|
call.resolve(["bundle": bundle.toJSON()])
|
|
1942
2074
|
}
|
|
@@ -1987,6 +2119,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1987
2119
|
// Note: _checkCancelDelay method has been moved to DelayUpdateUtils class
|
|
1988
2120
|
|
|
1989
2121
|
private func _isAutoUpdateEnabled() -> Bool {
|
|
2122
|
+
if self.isPreviewSessionStateActive() {
|
|
2123
|
+
return false
|
|
2124
|
+
}
|
|
1990
2125
|
let instanceDescriptor = (self.bridge?.viewController as? CAPBridgeViewController)?.instanceDescriptor()
|
|
1991
2126
|
if instanceDescriptor?.serverURL != nil {
|
|
1992
2127
|
logger.warn("AutoUpdate is automatic disabled when serverUrl is set.")
|
|
@@ -2024,6 +2159,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
2024
2159
|
logger.info("Built-in bundle is active. We skip the check for notifyAppReady.")
|
|
2025
2160
|
return
|
|
2026
2161
|
}
|
|
2162
|
+
if self.isPreviewSessionStateActive() {
|
|
2163
|
+
logger.info("Preview session is active. We skip the check for notifyAppReady.")
|
|
2164
|
+
return
|
|
2165
|
+
}
|
|
2027
2166
|
|
|
2028
2167
|
logger.info("Current bundle is: \(current.toString())")
|
|
2029
2168
|
|
|
@@ -2076,6 +2215,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
2076
2215
|
if self.autoSplashscreen {
|
|
2077
2216
|
self.hideSplashscreen()
|
|
2078
2217
|
}
|
|
2218
|
+
self.hidePreviewTransitionLoader(reason: "app-ready")
|
|
2079
2219
|
}
|
|
2080
2220
|
}
|
|
2081
2221
|
|
|
@@ -2243,6 +2383,51 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
2243
2383
|
}
|
|
2244
2384
|
}
|
|
2245
2385
|
|
|
2386
|
+
private func createLoaderOverlay(
|
|
2387
|
+
backgroundColor: UIColor,
|
|
2388
|
+
isUserInteractionEnabled: Bool,
|
|
2389
|
+
indicatorColor: UIColor?
|
|
2390
|
+
) -> (container: UIView, indicator: UIActivityIndicatorView) {
|
|
2391
|
+
let container = UIView()
|
|
2392
|
+
container.translatesAutoresizingMaskIntoConstraints = false
|
|
2393
|
+
container.backgroundColor = backgroundColor
|
|
2394
|
+
container.isUserInteractionEnabled = isUserInteractionEnabled
|
|
2395
|
+
|
|
2396
|
+
let indicatorStyle: UIActivityIndicatorView.Style
|
|
2397
|
+
if #available(iOS 13.0, *) {
|
|
2398
|
+
indicatorStyle = .large
|
|
2399
|
+
} else {
|
|
2400
|
+
indicatorStyle = .whiteLarge
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
let indicator = UIActivityIndicatorView(style: indicatorStyle)
|
|
2404
|
+
indicator.translatesAutoresizingMaskIntoConstraints = false
|
|
2405
|
+
indicator.hidesWhenStopped = false
|
|
2406
|
+
if let indicatorColor = indicatorColor {
|
|
2407
|
+
indicator.color = indicatorColor
|
|
2408
|
+
}
|
|
2409
|
+
indicator.startAnimating()
|
|
2410
|
+
|
|
2411
|
+
return (container, indicator)
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
private func attachLoaderOverlay(
|
|
2415
|
+
_ overlay: (container: UIView, indicator: UIActivityIndicatorView),
|
|
2416
|
+
to rootView: UIView
|
|
2417
|
+
) {
|
|
2418
|
+
overlay.container.addSubview(overlay.indicator)
|
|
2419
|
+
rootView.addSubview(overlay.container)
|
|
2420
|
+
|
|
2421
|
+
NSLayoutConstraint.activate([
|
|
2422
|
+
overlay.container.leadingAnchor.constraint(equalTo: rootView.leadingAnchor),
|
|
2423
|
+
overlay.container.trailingAnchor.constraint(equalTo: rootView.trailingAnchor),
|
|
2424
|
+
overlay.container.topAnchor.constraint(equalTo: rootView.topAnchor),
|
|
2425
|
+
overlay.container.bottomAnchor.constraint(equalTo: rootView.bottomAnchor),
|
|
2426
|
+
overlay.indicator.centerXAnchor.constraint(equalTo: overlay.container.centerXAnchor),
|
|
2427
|
+
overlay.indicator.centerYAnchor.constraint(equalTo: overlay.container.centerYAnchor)
|
|
2428
|
+
])
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2246
2431
|
private func addSplashscreenLoaderIfNeeded() {
|
|
2247
2432
|
guard self.autoSplashscreenLoader else {
|
|
2248
2433
|
return
|
|
@@ -2257,40 +2442,20 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
2257
2442
|
return
|
|
2258
2443
|
}
|
|
2259
2444
|
|
|
2260
|
-
let
|
|
2261
|
-
container.translatesAutoresizingMaskIntoConstraints = false
|
|
2262
|
-
container.backgroundColor = UIColor.clear
|
|
2263
|
-
container.isUserInteractionEnabled = false
|
|
2264
|
-
|
|
2265
|
-
let indicatorStyle: UIActivityIndicatorView.Style
|
|
2445
|
+
let indicatorColor: UIColor?
|
|
2266
2446
|
if #available(iOS 13.0, *) {
|
|
2267
|
-
|
|
2447
|
+
indicatorColor = UIColor.label
|
|
2268
2448
|
} else {
|
|
2269
|
-
|
|
2270
|
-
}
|
|
2271
|
-
|
|
2272
|
-
let indicator = UIActivityIndicatorView(style: indicatorStyle)
|
|
2273
|
-
indicator.translatesAutoresizingMaskIntoConstraints = false
|
|
2274
|
-
indicator.hidesWhenStopped = false
|
|
2275
|
-
if #available(iOS 13.0, *) {
|
|
2276
|
-
indicator.color = UIColor.label
|
|
2449
|
+
indicatorColor = nil
|
|
2277
2450
|
}
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
container.topAnchor.constraint(equalTo: rootView.topAnchor),
|
|
2287
|
-
container.bottomAnchor.constraint(equalTo: rootView.bottomAnchor),
|
|
2288
|
-
indicator.centerXAnchor.constraint(equalTo: container.centerXAnchor),
|
|
2289
|
-
indicator.centerYAnchor.constraint(equalTo: container.centerYAnchor)
|
|
2290
|
-
])
|
|
2291
|
-
|
|
2292
|
-
self.splashscreenLoaderContainer = container
|
|
2293
|
-
self.splashscreenLoaderView = indicator
|
|
2451
|
+
let overlay = self.createLoaderOverlay(
|
|
2452
|
+
backgroundColor: UIColor.clear,
|
|
2453
|
+
isUserInteractionEnabled: false,
|
|
2454
|
+
indicatorColor: indicatorColor
|
|
2455
|
+
)
|
|
2456
|
+
self.attachLoaderOverlay(overlay, to: rootView)
|
|
2457
|
+
self.splashscreenLoaderContainer = overlay.container
|
|
2458
|
+
self.splashscreenLoaderView = overlay.indicator
|
|
2294
2459
|
}
|
|
2295
2460
|
|
|
2296
2461
|
if Thread.isMainThread {
|
|
@@ -2319,6 +2484,97 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
2319
2484
|
}
|
|
2320
2485
|
}
|
|
2321
2486
|
|
|
2487
|
+
private func showPreviewTransitionLoader(reason: String) {
|
|
2488
|
+
self.previewTransitionLoaderRequested = true
|
|
2489
|
+
let showLoader = {
|
|
2490
|
+
guard self.previewTransitionLoaderRequested else {
|
|
2491
|
+
return
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
if let container = self.previewTransitionLoaderContainer {
|
|
2495
|
+
self.previewTransitionLoaderTimeoutWorkItem?.cancel()
|
|
2496
|
+
self.schedulePreviewTransitionLoaderTimeout()
|
|
2497
|
+
container.superview?.bringSubviewToFront(container)
|
|
2498
|
+
return
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
guard let rootView = self.bridge?.viewController?.view else {
|
|
2502
|
+
self.logger.warn("Preview transition loader unavailable: root view missing for \(reason)")
|
|
2503
|
+
self.previewTransitionLoaderRequested = false
|
|
2504
|
+
return
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
self.previewTransitionLoaderTimeoutWorkItem?.cancel()
|
|
2508
|
+
self.schedulePreviewTransitionLoaderTimeout()
|
|
2509
|
+
|
|
2510
|
+
let indicatorColor: UIColor?
|
|
2511
|
+
if #available(iOS 13.0, *) {
|
|
2512
|
+
indicatorColor = UIColor.white
|
|
2513
|
+
} else {
|
|
2514
|
+
indicatorColor = nil
|
|
2515
|
+
}
|
|
2516
|
+
let overlay = self.createLoaderOverlay(
|
|
2517
|
+
backgroundColor: UIColor.black.withAlphaComponent(0.18),
|
|
2518
|
+
isUserInteractionEnabled: true,
|
|
2519
|
+
indicatorColor: indicatorColor
|
|
2520
|
+
)
|
|
2521
|
+
self.attachLoaderOverlay(overlay, to: rootView)
|
|
2522
|
+
self.previewTransitionLoaderContainer = overlay.container
|
|
2523
|
+
self.previewTransitionLoaderView = overlay.indicator
|
|
2524
|
+
self.logger.info("Preview transition loader shown: \(reason)")
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
if Thread.isMainThread {
|
|
2528
|
+
showLoader()
|
|
2529
|
+
} else {
|
|
2530
|
+
DispatchQueue.main.async {
|
|
2531
|
+
showLoader()
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
private func hidePreviewTransitionLoader(reason: String) {
|
|
2537
|
+
if !self.previewTransitionLoaderRequested &&
|
|
2538
|
+
self.previewTransitionLoaderContainer == nil &&
|
|
2539
|
+
self.previewTransitionLoaderTimeoutWorkItem == nil {
|
|
2540
|
+
return
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
let hideLoader = {
|
|
2544
|
+
self.previewTransitionLoaderRequested = false
|
|
2545
|
+
self.previewTransitionLoaderTimeoutWorkItem?.cancel()
|
|
2546
|
+
self.previewTransitionLoaderTimeoutWorkItem = nil
|
|
2547
|
+
guard self.previewTransitionLoaderContainer != nil else {
|
|
2548
|
+
return
|
|
2549
|
+
}
|
|
2550
|
+
self.previewTransitionLoaderView?.stopAnimating()
|
|
2551
|
+
self.previewTransitionLoaderContainer?.removeFromSuperview()
|
|
2552
|
+
self.previewTransitionLoaderView = nil
|
|
2553
|
+
self.previewTransitionLoaderContainer = nil
|
|
2554
|
+
self.logger.info("Preview transition loader hidden: \(reason)")
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
if Thread.isMainThread {
|
|
2558
|
+
hideLoader()
|
|
2559
|
+
} else {
|
|
2560
|
+
DispatchQueue.main.async {
|
|
2561
|
+
hideLoader()
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
private func schedulePreviewTransitionLoaderTimeout() {
|
|
2567
|
+
self.previewTransitionLoaderTimeoutWorkItem?.cancel()
|
|
2568
|
+
let workItem = DispatchWorkItem { [weak self] in
|
|
2569
|
+
self?.hidePreviewTransitionLoader(reason: "preview-transition-timeout")
|
|
2570
|
+
}
|
|
2571
|
+
self.previewTransitionLoaderTimeoutWorkItem = workItem
|
|
2572
|
+
DispatchQueue.main.asyncAfter(
|
|
2573
|
+
deadline: .now() + .milliseconds(Self.previewLoaderTimeoutMs),
|
|
2574
|
+
execute: workItem
|
|
2575
|
+
)
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2322
2578
|
private func scheduleSplashscreenTimeout() {
|
|
2323
2579
|
guard self.autoSplashscreenTimeout > 0 else {
|
|
2324
2580
|
return
|
|
@@ -2693,6 +2949,13 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
2693
2949
|
return true
|
|
2694
2950
|
}
|
|
2695
2951
|
|
|
2952
|
+
private func clearDownloadInProgressState() {
|
|
2953
|
+
downloadLock.lock()
|
|
2954
|
+
defer { downloadLock.unlock() }
|
|
2955
|
+
downloadInProgress = false
|
|
2956
|
+
downloadStartTime = nil
|
|
2957
|
+
}
|
|
2958
|
+
|
|
2696
2959
|
func runBackgroundDownloadWork(_ work: @escaping () -> Void) {
|
|
2697
2960
|
// Live update checks/downloads are user-visible work. Using `.background`
|
|
2698
2961
|
// lets the scheduler starve them for minutes while the app is active.
|
|
@@ -2718,6 +2981,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
2718
2981
|
}
|
|
2719
2982
|
|
|
2720
2983
|
func backgroundDownload() {
|
|
2984
|
+
if self.shouldBlockAutoUpdateForPreviewSession() {
|
|
2985
|
+
return
|
|
2986
|
+
}
|
|
2721
2987
|
// Set download in progress flag (thread-safe)
|
|
2722
2988
|
downloadLock.lock()
|
|
2723
2989
|
downloadInProgress = true
|
|
@@ -2746,10 +3012,19 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
2746
3012
|
self.runBackgroundDownloadWork {
|
|
2747
3013
|
// Wait for cleanup to complete before starting download
|
|
2748
3014
|
self.waitForCleanupIfNeeded()
|
|
3015
|
+
if self.shouldBlockAutoUpdateForPreviewSession() {
|
|
3016
|
+
self.clearDownloadInProgressState()
|
|
3017
|
+
return
|
|
3018
|
+
}
|
|
2749
3019
|
self.beginDownloadBackgroundTask()
|
|
2750
3020
|
self.logger.info("Check for update via \(self.updateUrl)")
|
|
2751
3021
|
let res = self.implementation.getLatest(url: url, channel: nil)
|
|
2752
3022
|
let current = self.implementation.getCurrentBundle()
|
|
3023
|
+
if self.shouldBlockAutoUpdateForPreviewSession() {
|
|
3024
|
+
self.clearDownloadInProgressState()
|
|
3025
|
+
self.endBackGroundTask()
|
|
3026
|
+
return
|
|
3027
|
+
}
|
|
2753
3028
|
|
|
2754
3029
|
// Handle network errors and other failures first
|
|
2755
3030
|
let backendError = res.error ?? ""
|
|
@@ -2861,6 +3136,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
2861
3136
|
)
|
|
2862
3137
|
return
|
|
2863
3138
|
}
|
|
3139
|
+
if self.shouldBlockAutoUpdateForPreviewSession() {
|
|
3140
|
+
self.clearDownloadInProgressState()
|
|
3141
|
+
self.endBackGroundTask()
|
|
3142
|
+
return
|
|
3143
|
+
}
|
|
2864
3144
|
res.checksum = try CryptoCipher.decryptChecksum(checksum: res.checksum, publicKey: self.implementation.publicKey)
|
|
2865
3145
|
CryptoCipher.logChecksumInfo(label: "Bundle checksum", hexChecksum: next.getChecksum())
|
|
2866
3146
|
CryptoCipher.logChecksumInfo(label: "Expected checksum", hexChecksum: res.checksum)
|
|
@@ -2880,6 +3160,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
2880
3160
|
)
|
|
2881
3161
|
return
|
|
2882
3162
|
}
|
|
3163
|
+
if self.shouldBlockAutoUpdateForPreviewSession() {
|
|
3164
|
+
self.clearDownloadInProgressState()
|
|
3165
|
+
self.endBackGroundTask()
|
|
3166
|
+
return
|
|
3167
|
+
}
|
|
2883
3168
|
let directUpdateAllowed = plannedDirectUpdate && !self.autoSplashscreenTimedOut
|
|
2884
3169
|
if directUpdateAllowed {
|
|
2885
3170
|
let delayUpdatePreferences = UserDefaults.standard.string(forKey: DelayUpdateUtils.DELAY_CONDITION_PREFERENCES) ?? "[]"
|
|
@@ -2899,7 +3184,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
2899
3184
|
)
|
|
2900
3185
|
return
|
|
2901
3186
|
}
|
|
2902
|
-
if self.
|
|
3187
|
+
if self.applyDownloadedBundleForDirectUpdate(next) {
|
|
2903
3188
|
self.notifyBundleSet(next)
|
|
2904
3189
|
self.endBackGroundTaskWithNotif(
|
|
2905
3190
|
msg: "update installed",
|
|
@@ -2909,10 +3194,13 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
2909
3194
|
plannedDirectUpdate: plannedDirectUpdate
|
|
2910
3195
|
)
|
|
2911
3196
|
} else {
|
|
3197
|
+
_ = self.implementation.setNextBundle(next: next.getId())
|
|
3198
|
+
self.notifyListeners("updateAvailable", data: ["bundle": next.toJSON()])
|
|
2912
3199
|
self.endBackGroundTaskWithNotif(
|
|
2913
|
-
msg: "
|
|
3200
|
+
msg: "Direct update reload failed, update will install next background",
|
|
2914
3201
|
latestVersionName: latestVersionName,
|
|
2915
|
-
current:
|
|
3202
|
+
current: self.implementation.getCurrentBundle(),
|
|
3203
|
+
error: false,
|
|
2916
3204
|
plannedDirectUpdate: plannedDirectUpdate
|
|
2917
3205
|
)
|
|
2918
3206
|
}
|
|
@@ -2968,6 +3256,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
2968
3256
|
}
|
|
2969
3257
|
|
|
2970
3258
|
private func installNext() {
|
|
3259
|
+
if self.shouldBlockAutoUpdateForPreviewSession() {
|
|
3260
|
+
return
|
|
3261
|
+
}
|
|
2971
3262
|
let delayUpdatePreferences = UserDefaults.standard.string(forKey: DelayUpdateUtils.DELAY_CONDITION_PREFERENCES) ?? "[]"
|
|
2972
3263
|
let delayConditionList: [DelayCondition] = fromJsonArr(json: delayUpdatePreferences).map { obj -> DelayCondition in
|
|
2973
3264
|
let kind: String = obj.value(forKey: "kind") as! String
|
|
@@ -3059,8 +3350,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
3059
3350
|
return
|
|
3060
3351
|
}
|
|
3061
3352
|
DispatchQueue.global(qos: .utility).async {
|
|
3353
|
+
if self.shouldBlockAutoUpdateForPreviewSession() {
|
|
3354
|
+
return
|
|
3355
|
+
}
|
|
3062
3356
|
let res = self.implementation.getLatest(url: url, channel: nil)
|
|
3063
3357
|
let current = self.implementation.getCurrentBundle()
|
|
3358
|
+
if self.shouldBlockAutoUpdateForPreviewSession() {
|
|
3359
|
+
return
|
|
3360
|
+
}
|
|
3064
3361
|
|
|
3065
3362
|
if res.version != current.getVersionName() {
|
|
3066
3363
|
self.logger.info("New version found: \(res.version)")
|
|
@@ -2431,7 +2431,7 @@ import UIKit
|
|
|
2431
2431
|
if let channels = responseValue.channels {
|
|
2432
2432
|
listChannels.channels = channels.map { channel in
|
|
2433
2433
|
var channelDict: [String: Any] = [:]
|
|
2434
|
-
channelDict["id"] = channel.id
|
|
2434
|
+
channelDict["id"] = channel.id
|
|
2435
2435
|
channelDict["name"] = channel.name ?? ""
|
|
2436
2436
|
channelDict["public"] = channel.public ?? false
|
|
2437
2437
|
channelDict["allow_self_set"] = channel.allow_self_set ?? false
|