@capgo/capacitor-updater 7.43.3 → 7.45.10
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/Package.swift +5 -2
- package/README.md +149 -39
- package/android/build.gradle +3 -3
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +537 -172
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +170 -35
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayUpdateUtils.java +49 -13
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +38 -13
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeMenu.java +49 -9
- package/dist/docs.json +290 -10
- package/dist/esm/definitions.d.ts +134 -22
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +1 -2
- package/dist/esm/web.js +0 -4
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +0 -4
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +0 -4
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +557 -135
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +213 -50
- package/ios/Sources/CapacitorUpdaterPlugin/DelayUpdateUtils.swift +37 -16
- package/ios/Sources/CapacitorUpdaterPlugin/InternalUtils.swift +2 -0
- package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +20 -3
- package/package.json +11 -8
|
@@ -44,7 +44,6 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
44
44
|
CAPPluginMethod(name: "current", returnType: CAPPluginReturnPromise),
|
|
45
45
|
CAPPluginMethod(name: "reload", returnType: CAPPluginReturnPromise),
|
|
46
46
|
CAPPluginMethod(name: "notifyAppReady", returnType: CAPPluginReturnPromise),
|
|
47
|
-
CAPPluginMethod(name: "setDelay", returnType: CAPPluginReturnPromise),
|
|
48
47
|
CAPPluginMethod(name: "setMultiDelay", returnType: CAPPluginReturnPromise),
|
|
49
48
|
CAPPluginMethod(name: "cancelDelay", returnType: CAPPluginReturnPromise),
|
|
50
49
|
CAPPluginMethod(name: "getLatest", returnType: CAPPluginReturnPromise),
|
|
@@ -73,7 +72,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
73
72
|
CAPPluginMethod(name: "completeFlexibleUpdate", returnType: CAPPluginReturnPromise)
|
|
74
73
|
]
|
|
75
74
|
public var implementation = CapgoUpdater()
|
|
76
|
-
private let pluginVersion: String = "7.
|
|
75
|
+
private let pluginVersion: String = "7.45.10"
|
|
77
76
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
78
77
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
79
78
|
static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
|
|
@@ -103,7 +102,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
103
102
|
private var autoSplashscreenTimeoutWorkItem: DispatchWorkItem?
|
|
104
103
|
private var splashscreenLoaderView: UIActivityIndicatorView?
|
|
105
104
|
private var splashscreenLoaderContainer: UIView?
|
|
105
|
+
private let splashscreenPluginName = "SplashScreen"
|
|
106
|
+
private let splashscreenRetryDelayMilliseconds = 100
|
|
107
|
+
private let splashscreenMaxRetries = 20
|
|
106
108
|
private var autoSplashscreenTimedOut = false
|
|
109
|
+
private var splashscreenInvocationToken = 0
|
|
107
110
|
private var autoDeleteFailed = false
|
|
108
111
|
private var autoDeletePrevious = false
|
|
109
112
|
var allowSetDefaultChannel = true
|
|
@@ -112,6 +115,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
112
115
|
private var taskRunning = false
|
|
113
116
|
private var periodCheckDelay = 0
|
|
114
117
|
private let downloadLock = NSLock()
|
|
118
|
+
private let onLaunchDirectUpdateStateLock = NSLock()
|
|
115
119
|
private var downloadInProgress = false
|
|
116
120
|
private var downloadStartTime: Date?
|
|
117
121
|
private let downloadTimeout: TimeInterval = 3600 // 1 hour timeout
|
|
@@ -228,6 +232,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
228
232
|
|
|
229
233
|
implementation.setPublicKey(getConfig().getString("publicKey") ?? "")
|
|
230
234
|
implementation.notifyDownloadRaw = notifyDownload
|
|
235
|
+
implementation.notifyListeners = { [weak self] eventName, data in
|
|
236
|
+
self?.notifyListeners(eventName, data: data)
|
|
237
|
+
}
|
|
231
238
|
implementation.pluginVersion = self.pluginVersion
|
|
232
239
|
|
|
233
240
|
// Set logger for shared classes
|
|
@@ -272,11 +279,12 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
272
279
|
}
|
|
273
280
|
self.implementation.autoReset()
|
|
274
281
|
|
|
275
|
-
// Check if app was recently installed/updated BEFORE
|
|
282
|
+
// Check if app was recently installed/updated BEFORE cleanup updates the stored native build version.
|
|
276
283
|
self.wasRecentlyInstalledOrUpdated = self.checkIfRecentlyInstalledOrUpdated()
|
|
277
284
|
|
|
278
285
|
if resetWhenUpdate {
|
|
279
|
-
self.
|
|
286
|
+
let didResetCurrentBundle = self.resetCurrentBundleForNativeBuildChangeIfNeeded()
|
|
287
|
+
self.cleanupObsoleteVersions(didResetCurrentBundle: didResetCurrentBundle)
|
|
280
288
|
}
|
|
281
289
|
|
|
282
290
|
// Load the server
|
|
@@ -392,7 +400,29 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
392
400
|
semaphoreReady.signal()
|
|
393
401
|
}
|
|
394
402
|
|
|
395
|
-
|
|
403
|
+
func storedNativeBuildVersion() -> String {
|
|
404
|
+
UserDefaults.standard.string(forKey: "LatestNativeBuildVersion") ?? UserDefaults.standard.string(forKey: "LatestVersionNative") ?? "0"
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
func hasNativeBuildVersionChanged() -> Bool {
|
|
408
|
+
let previous = self.storedNativeBuildVersion()
|
|
409
|
+
return previous != "0" && self.currentBuildVersion != previous
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
@discardableResult
|
|
413
|
+
func resetCurrentBundleForNativeBuildChangeIfNeeded() -> Bool {
|
|
414
|
+
let previous = self.storedNativeBuildVersion()
|
|
415
|
+
guard previous != "0" && self.currentBuildVersion != previous else {
|
|
416
|
+
return false
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Reset startup state synchronously so initialLoad() boots from the builtin bundle.
|
|
420
|
+
self.logger.info("Native build version changed from \(previous) to \(self.currentBuildVersion). Resetting startup bundle to builtin.")
|
|
421
|
+
self.implementation.reset(isInternal: true)
|
|
422
|
+
return true
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private func cleanupObsoleteVersions(didResetCurrentBundle: Bool = false) {
|
|
396
426
|
cleanupThread = Thread {
|
|
397
427
|
self.cleanupLock.lock()
|
|
398
428
|
defer {
|
|
@@ -427,9 +457,12 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
427
457
|
// 1. Write "LatestVersionNative" - this fixes the part 1 of this bug
|
|
428
458
|
// 2. Compare both keys. If any is not equal to "currentBuildVersion", then revert to builtin version. This fixes the part 2 of this bug
|
|
429
459
|
|
|
430
|
-
let previous =
|
|
460
|
+
let previous = self.storedNativeBuildVersion()
|
|
431
461
|
if previous != "0" && self.currentBuildVersion != previous {
|
|
432
|
-
|
|
462
|
+
if !didResetCurrentBundle {
|
|
463
|
+
self.logger.info("Native build version changed from \(previous) to \(self.currentBuildVersion). Resetting current bundle to builtin.")
|
|
464
|
+
self.implementation.reset(isInternal: true)
|
|
465
|
+
}
|
|
433
466
|
let res = self.implementation.list()
|
|
434
467
|
for version in res {
|
|
435
468
|
// Check if thread was cancelled
|
|
@@ -648,47 +681,77 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
648
681
|
}
|
|
649
682
|
}
|
|
650
683
|
|
|
651
|
-
|
|
652
|
-
guard let bridge = self.bridge else { return false }
|
|
653
|
-
self.semaphoreUp()
|
|
684
|
+
private func currentReloadDestination() -> URL {
|
|
654
685
|
let id = self.implementation.getCurrentBundleId()
|
|
655
|
-
let dest: URL
|
|
656
686
|
if BundleInfo.ID_BUILTIN == id {
|
|
657
|
-
|
|
687
|
+
return Bundle.main.resourceURL!.appendingPathComponent("public")
|
|
658
688
|
} else {
|
|
659
|
-
|
|
689
|
+
return self.implementation.getBundleDirectory(id: id)
|
|
660
690
|
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
private func applyCurrentBundleToBridge(_ bridge: CAPBridgeProtocol) -> Bool {
|
|
694
|
+
let id = self.implementation.getCurrentBundleId()
|
|
695
|
+
let dest = self.currentReloadDestination()
|
|
661
696
|
logger.info("Reloading \(id)")
|
|
662
697
|
|
|
663
|
-
let
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
if
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
_ = vc.webView?.load(URLRequest(url: finalUrl))
|
|
681
|
-
} else {
|
|
682
|
-
self.logger.error("Unable to build final URL when keeping path after reload; falling back to base path")
|
|
683
|
-
vc.setServerBasePath(path: dest.path)
|
|
684
|
-
}
|
|
698
|
+
guard let vc = bridge.viewController as? CAPBridgeViewController else {
|
|
699
|
+
self.logger.error("Cannot get viewController")
|
|
700
|
+
return false
|
|
701
|
+
}
|
|
702
|
+
guard let capBridge = vc.bridge else {
|
|
703
|
+
self.logger.error("Cannot get capBridge")
|
|
704
|
+
return false
|
|
705
|
+
}
|
|
706
|
+
if self.keepUrlPathAfterReload {
|
|
707
|
+
if let currentURL = vc.webView?.url {
|
|
708
|
+
capBridge.setServerBasePath(dest.path)
|
|
709
|
+
var urlComponents = URLComponents(url: capBridge.config.serverURL, resolvingAgainstBaseURL: false)!
|
|
710
|
+
urlComponents.path = currentURL.path
|
|
711
|
+
urlComponents.query = currentURL.query
|
|
712
|
+
urlComponents.fragment = currentURL.fragment
|
|
713
|
+
if let finalUrl = urlComponents.url {
|
|
714
|
+
_ = vc.webView?.load(URLRequest(url: finalUrl))
|
|
685
715
|
} else {
|
|
686
|
-
self.logger.error("
|
|
716
|
+
self.logger.error("Unable to build final URL when keeping path after reload; falling back to base path")
|
|
687
717
|
vc.setServerBasePath(path: dest.path)
|
|
688
718
|
}
|
|
689
719
|
} else {
|
|
720
|
+
self.logger.error("vc.webView?.url is null? Falling back to base path reload.")
|
|
690
721
|
vc.setServerBasePath(path: dest.path)
|
|
691
722
|
}
|
|
723
|
+
} else {
|
|
724
|
+
vc.setServerBasePath(path: dest.path)
|
|
725
|
+
}
|
|
726
|
+
return true
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
func restoreLiveBundleStateAfterFailedReload() {
|
|
730
|
+
guard let bridge = self.bridge else {
|
|
731
|
+
return
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
let restoreLiveState = {
|
|
735
|
+
_ = self.applyCurrentBundleToBridge(bridge)
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if Thread.isMainThread {
|
|
739
|
+
restoreLiveState()
|
|
740
|
+
} else {
|
|
741
|
+
DispatchQueue.main.sync {
|
|
742
|
+
restoreLiveState()
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
public func _reload() -> Bool {
|
|
748
|
+
guard let bridge = self.bridge else { return false }
|
|
749
|
+
self.semaphoreUp()
|
|
750
|
+
|
|
751
|
+
let performReload: () -> Bool = {
|
|
752
|
+
guard self.applyCurrentBundleToBridge(bridge) else {
|
|
753
|
+
return false
|
|
754
|
+
}
|
|
692
755
|
self.checkAppReady()
|
|
693
756
|
self.notifyListeners("appReloaded", data: [:])
|
|
694
757
|
return true
|
|
@@ -706,6 +769,38 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
706
769
|
}
|
|
707
770
|
|
|
708
771
|
@objc func reload(_ call: CAPPluginCall) {
|
|
772
|
+
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
773
|
+
let next: BundleInfo? = self.implementation.getNextBundle()
|
|
774
|
+
|
|
775
|
+
if let next = next, !next.isErrorStatus(), next.getId() != current.getId() {
|
|
776
|
+
let previousState = self.implementation.captureResetState()
|
|
777
|
+
let previousBundleName = self.implementation.getCurrentBundle().getVersionName()
|
|
778
|
+
logger.info("Applying pending bundle before reload: \(next.toString())")
|
|
779
|
+
let didApplyPendingBundle: Bool
|
|
780
|
+
if next.isBuiltin() {
|
|
781
|
+
self.implementation.prepareResetStateForTransition()
|
|
782
|
+
didApplyPendingBundle = true
|
|
783
|
+
} else {
|
|
784
|
+
didApplyPendingBundle = self.implementation.stagePendingReload(bundle: next)
|
|
785
|
+
}
|
|
786
|
+
if didApplyPendingBundle && self._reload() {
|
|
787
|
+
if next.isBuiltin() {
|
|
788
|
+
self.implementation.finalizeResetTransition(previousBundleName: previousBundleName, isInternal: false)
|
|
789
|
+
} else {
|
|
790
|
+
self.implementation.finalizePendingReload(bundle: next, previousBundleName: previousBundleName)
|
|
791
|
+
}
|
|
792
|
+
self.notifyBundleSet(next)
|
|
793
|
+
_ = self.implementation.setNextBundle(next: Optional<String>.none)
|
|
794
|
+
call.resolve()
|
|
795
|
+
return
|
|
796
|
+
}
|
|
797
|
+
self.implementation.restoreResetState(previousState)
|
|
798
|
+
self.restoreLiveBundleStateAfterFailedReload()
|
|
799
|
+
logger.error("Reload failed after applying pending bundle: \(next.toString())")
|
|
800
|
+
call.reject("Reload failed after applying pending bundle")
|
|
801
|
+
return
|
|
802
|
+
}
|
|
803
|
+
|
|
709
804
|
if self._reload() {
|
|
710
805
|
call.resolve()
|
|
711
806
|
} else {
|
|
@@ -740,8 +835,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
740
835
|
if !res {
|
|
741
836
|
logger.info("Bundle successfully set to: \(id) ")
|
|
742
837
|
call.reject("Update failed, id \(id) doesn't exist")
|
|
838
|
+
} else if !self._reload() {
|
|
839
|
+
call.reject("Reload failed after setting bundle \(id)")
|
|
743
840
|
} else {
|
|
744
|
-
self.
|
|
841
|
+
self.notifyBundleSet(self.implementation.getBundleInfo(id: id))
|
|
842
|
+
call.resolve()
|
|
745
843
|
}
|
|
746
844
|
}
|
|
747
845
|
|
|
@@ -804,12 +902,32 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
804
902
|
|
|
805
903
|
@objc func getLatest(_ call: CAPPluginCall) {
|
|
806
904
|
let channel = call.getString("channel")
|
|
807
|
-
|
|
905
|
+
runGetLatestWork {
|
|
808
906
|
let res = self.implementation.getLatest(url: URL(string: self.updateUrl)!, channel: channel)
|
|
809
|
-
if res.error
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
907
|
+
if let error = res.error, !error.isEmpty {
|
|
908
|
+
let responseKind = self.updateResponseKind(kind: res.kind)
|
|
909
|
+
res.kind = responseKind
|
|
910
|
+
if responseKind == "failed" {
|
|
911
|
+
call.reject(error)
|
|
912
|
+
} else {
|
|
913
|
+
if res.version.isEmpty {
|
|
914
|
+
res.version = self.implementation.getCurrentBundle().getVersionName()
|
|
915
|
+
}
|
|
916
|
+
call.resolve(res.toDict())
|
|
917
|
+
}
|
|
918
|
+
} else if let kind = res.kind, !kind.isEmpty {
|
|
919
|
+
let responseKind = self.updateResponseKind(kind: kind)
|
|
920
|
+
res.kind = responseKind
|
|
921
|
+
if responseKind != "failed" {
|
|
922
|
+
if res.version.isEmpty {
|
|
923
|
+
res.version = self.implementation.getCurrentBundle().getVersionName()
|
|
924
|
+
}
|
|
925
|
+
call.resolve(res.toDict())
|
|
926
|
+
} else {
|
|
927
|
+
call.reject(res.message ?? "server did not provide a message")
|
|
928
|
+
}
|
|
929
|
+
} else if let message = res.message, !message.isEmpty {
|
|
930
|
+
call.reject(message)
|
|
813
931
|
} else {
|
|
814
932
|
call.resolve(res.toDict())
|
|
815
933
|
}
|
|
@@ -926,32 +1044,90 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
926
1044
|
call.resolve()
|
|
927
1045
|
}
|
|
928
1046
|
|
|
929
|
-
@objc func _reset(toLastSuccessful: Bool) -> Bool {
|
|
930
|
-
|
|
1047
|
+
@objc func _reset(toLastSuccessful: Bool, usePendingBundle: Bool) -> Bool {
|
|
1048
|
+
self.performReset(toLastSuccessful: toLastSuccessful, usePendingBundle: usePendingBundle, isInternal: false)
|
|
1049
|
+
}
|
|
931
1050
|
|
|
932
|
-
|
|
933
|
-
|
|
1051
|
+
func performReset(toLastSuccessful: Bool, usePendingBundle: Bool, isInternal: Bool) -> Bool {
|
|
1052
|
+
guard self.canPerformResetTransition() else { return false }
|
|
934
1053
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
return self.implementation.set(bundle: fallback) && self._reload()
|
|
940
|
-
}
|
|
1054
|
+
let fallback: BundleInfo = self.implementation.getFallbackBundle()
|
|
1055
|
+
let pending: BundleInfo? = self.implementation.getNextBundle()
|
|
1056
|
+
let previousState = self.implementation.captureResetState()
|
|
1057
|
+
let previousBundleName = self.implementation.getCurrentBundle().getVersionName()
|
|
941
1058
|
|
|
942
|
-
|
|
1059
|
+
if usePendingBundle {
|
|
1060
|
+
guard let pending = pending, !pending.isErrorStatus() else {
|
|
1061
|
+
logger.error("No pending bundle available to reset to")
|
|
1062
|
+
return false
|
|
1063
|
+
}
|
|
1064
|
+
guard self.implementation.canSet(bundle: pending) else {
|
|
1065
|
+
logger.error("Pending bundle is not installable")
|
|
1066
|
+
return false
|
|
1067
|
+
}
|
|
1068
|
+
self.implementation.prepareResetStateForTransition()
|
|
1069
|
+
logger.info("Resetting to pending bundle: \(pending.toString())")
|
|
1070
|
+
let didApplyPendingBundle: Bool
|
|
1071
|
+
if pending.isBuiltin() {
|
|
1072
|
+
didApplyPendingBundle = true
|
|
1073
|
+
} else {
|
|
1074
|
+
didApplyPendingBundle = self.implementation.set(bundle: pending)
|
|
1075
|
+
}
|
|
1076
|
+
if didApplyPendingBundle && self._reload() {
|
|
1077
|
+
self.implementation.finalizeResetTransition(previousBundleName: previousBundleName, isInternal: isInternal)
|
|
1078
|
+
self.notifyBundleSet(pending)
|
|
1079
|
+
_ = self.implementation.setNextBundle(next: Optional<String>.none)
|
|
1080
|
+
return true
|
|
1081
|
+
}
|
|
1082
|
+
self.implementation.restoreResetState(previousState)
|
|
1083
|
+
self.restoreLiveBundleStateAfterFailedReload()
|
|
1084
|
+
return false
|
|
1085
|
+
}
|
|
943
1086
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1087
|
+
// If developer wants to reset to the last successful bundle, and that bundle is not
|
|
1088
|
+
// the built-in bundle, set it as the bundle to use and reload.
|
|
1089
|
+
if toLastSuccessful && !fallback.isBuiltin() {
|
|
1090
|
+
if self.implementation.canSet(bundle: fallback) {
|
|
1091
|
+
self.implementation.prepareResetStateForTransition()
|
|
1092
|
+
logger.info("Resetting to: \(fallback.toString())")
|
|
1093
|
+
if self.implementation.set(bundle: fallback) && self._reload() {
|
|
1094
|
+
self.implementation.finalizeResetTransition(previousBundleName: previousBundleName, isInternal: isInternal)
|
|
1095
|
+
self.notifyBundleSet(fallback)
|
|
1096
|
+
return true
|
|
1097
|
+
}
|
|
1098
|
+
if !isInternal {
|
|
1099
|
+
self.implementation.restoreResetState(previousState)
|
|
1100
|
+
self.restoreLiveBundleStateAfterFailedReload()
|
|
1101
|
+
return false
|
|
1102
|
+
}
|
|
1103
|
+
logger.warn("Fallback reload failed during internal reset, resetting to builtin instead")
|
|
1104
|
+
} else {
|
|
1105
|
+
logger.warn("Fallback bundle is not installable, resetting to builtin instead")
|
|
1106
|
+
}
|
|
947
1107
|
}
|
|
948
1108
|
|
|
1109
|
+
self.implementation.prepareResetStateForTransition()
|
|
1110
|
+
logger.info("Resetting to builtin version")
|
|
1111
|
+
if self._reload() {
|
|
1112
|
+
self.implementation.finalizeResetTransition(previousBundleName: previousBundleName, isInternal: isInternal)
|
|
1113
|
+
return true
|
|
1114
|
+
}
|
|
1115
|
+
if !isInternal {
|
|
1116
|
+
self.implementation.restoreResetState(previousState)
|
|
1117
|
+
self.restoreLiveBundleStateAfterFailedReload()
|
|
1118
|
+
}
|
|
949
1119
|
return false
|
|
950
1120
|
}
|
|
951
1121
|
|
|
1122
|
+
func canPerformResetTransition() -> Bool {
|
|
1123
|
+
guard let bridge = self.bridge else { return false }
|
|
1124
|
+
return (bridge.viewController as? CAPBridgeViewController) != nil
|
|
1125
|
+
}
|
|
1126
|
+
|
|
952
1127
|
@objc func reset(_ call: CAPPluginCall) {
|
|
953
1128
|
let toLastSuccessful = call.getBool("toLastSuccessful") ?? false
|
|
954
|
-
|
|
1129
|
+
let usePendingBundle = call.getBool("usePendingBundle") ?? false
|
|
1130
|
+
if self._reset(toLastSuccessful: toLastSuccessful, usePendingBundle: usePendingBundle) {
|
|
955
1131
|
call.resolve()
|
|
956
1132
|
} else {
|
|
957
1133
|
logger.error("Reset failed")
|
|
@@ -1070,7 +1246,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1070
1246
|
self.persistLastFailedBundle(current)
|
|
1071
1247
|
self.implementation.sendStats(action: "update_fail", versionName: current.getVersionName())
|
|
1072
1248
|
self.implementation.setError(bundle: current)
|
|
1073
|
-
_ = self.
|
|
1249
|
+
_ = self.performReset(toLastSuccessful: true, usePendingBundle: false, isInternal: true)
|
|
1074
1250
|
if self.autoDeleteFailed && !current.isBuiltin() {
|
|
1075
1251
|
logger.info("Deleting failing bundle: \(current.toString())")
|
|
1076
1252
|
let res = self.implementation.delete(id: current.getId(), removeInfo: false)
|
|
@@ -1095,6 +1271,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1095
1271
|
self.backgroundTaskID = UIBackgroundTaskIdentifier.invalid
|
|
1096
1272
|
}
|
|
1097
1273
|
|
|
1274
|
+
private func notifyBundleSet(_ bundle: BundleInfo) {
|
|
1275
|
+
self.notifyListeners("set", data: ["bundle": bundle.toJSON()], retainUntilConsumed: true)
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1098
1278
|
func sendReadyToJs(current: BundleInfo, msg: String) {
|
|
1099
1279
|
logger.info("sendReadyToJs")
|
|
1100
1280
|
DispatchQueue.global().async {
|
|
@@ -1122,32 +1302,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1122
1302
|
private func performHideSplashscreen() {
|
|
1123
1303
|
self.cancelSplashscreenTimeout()
|
|
1124
1304
|
self.removeSplashscreenLoader()
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
self.logger.info("Splashscreen hidden automatically")
|
|
1134
|
-
}, error: { (_) in
|
|
1135
|
-
self.logger.error("Failed to auto-hide splashscreen")
|
|
1136
|
-
})
|
|
1137
|
-
|
|
1138
|
-
// Try to call the SplashScreen hide method directly through the bridge
|
|
1139
|
-
if let splashScreenPlugin = bridge.plugin(withName: "SplashScreen") {
|
|
1140
|
-
// Use runtime method invocation to call hide method
|
|
1141
|
-
let selector = NSSelectorFromString("hide:")
|
|
1142
|
-
if splashScreenPlugin.responds(to: selector) {
|
|
1143
|
-
_ = splashScreenPlugin.perform(selector, with: call)
|
|
1144
|
-
self.logger.info("Called SplashScreen hide method")
|
|
1145
|
-
} else {
|
|
1146
|
-
self.logger.warn("autoSplashscreen: SplashScreen plugin does not respond to hide: method. Make sure @capacitor/splash-screen plugin is properly installed.")
|
|
1147
|
-
}
|
|
1148
|
-
} else {
|
|
1149
|
-
self.logger.warn("autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin.")
|
|
1150
|
-
}
|
|
1305
|
+
self.splashscreenInvocationToken += 1
|
|
1306
|
+
self.invokeSplashscreenMethod(
|
|
1307
|
+
methodName: "hide",
|
|
1308
|
+
callbackId: "autoHideSplashscreen",
|
|
1309
|
+
options: self.splashscreenOptions(methodName: "hide"),
|
|
1310
|
+
retriesRemaining: self.splashscreenMaxRetries,
|
|
1311
|
+
requestToken: self.splashscreenInvocationToken
|
|
1312
|
+
)
|
|
1151
1313
|
}
|
|
1152
1314
|
|
|
1153
1315
|
private func showSplashscreen() {
|
|
@@ -1163,35 +1325,132 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1163
1325
|
private func performShowSplashscreen() {
|
|
1164
1326
|
self.cancelSplashscreenTimeout()
|
|
1165
1327
|
self.autoSplashscreenTimedOut = false
|
|
1328
|
+
self.splashscreenInvocationToken += 1
|
|
1329
|
+
self.invokeSplashscreenMethod(
|
|
1330
|
+
methodName: "show",
|
|
1331
|
+
callbackId: "autoShowSplashscreen",
|
|
1332
|
+
options: self.splashscreenOptions(methodName: "show"),
|
|
1333
|
+
retriesRemaining: self.splashscreenMaxRetries,
|
|
1334
|
+
requestToken: self.splashscreenInvocationToken
|
|
1335
|
+
)
|
|
1336
|
+
|
|
1337
|
+
self.addSplashscreenLoaderIfNeeded()
|
|
1338
|
+
self.scheduleSplashscreenTimeout()
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
private func splashscreenOptions(methodName: String) -> [String: Any] {
|
|
1342
|
+
methodName == "show" ? ["autoHide": false] : [:]
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
private func splashscreenCompletedMessage(methodName: String) -> String {
|
|
1346
|
+
methodName == "show" ? "Splashscreen shown automatically" : "Splashscreen hidden automatically"
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
func splashscreenOptionsForTesting(methodName: String) -> [String: Any] {
|
|
1350
|
+
self.splashscreenOptions(methodName: methodName)
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
func isCurrentSplashscreenInvocationTokenForTesting(_ requestToken: Int) -> Bool {
|
|
1354
|
+
requestToken == self.splashscreenInvocationToken
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
func advanceSplashscreenInvocationTokenForTesting() {
|
|
1358
|
+
self.splashscreenInvocationToken += 1
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
private func makeSplashscreenCall(callbackId: String, options: [String: Any], methodName: String) -> CAPPluginCall {
|
|
1362
|
+
CAPPluginCall(callbackId: callbackId, options: options, success: { [weak self] (_, _) in
|
|
1363
|
+
guard let self = self else { return }
|
|
1364
|
+
self.logger.info(self.splashscreenCompletedMessage(methodName: methodName))
|
|
1365
|
+
}, error: { [weak self] (_) in
|
|
1366
|
+
guard let self = self else { return }
|
|
1367
|
+
self.logger.error("Failed to auto-\(methodName) splashscreen")
|
|
1368
|
+
})
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
private func invokeSplashscreenMethod(
|
|
1372
|
+
methodName: String,
|
|
1373
|
+
callbackId: String,
|
|
1374
|
+
options: [String: Any],
|
|
1375
|
+
retriesRemaining: Int,
|
|
1376
|
+
requestToken: Int
|
|
1377
|
+
) {
|
|
1378
|
+
guard requestToken == self.splashscreenInvocationToken else {
|
|
1379
|
+
return
|
|
1380
|
+
}
|
|
1166
1381
|
|
|
1167
1382
|
guard let bridge = self.bridge else {
|
|
1168
|
-
self.
|
|
1383
|
+
self.retrySplashscreenMethod(
|
|
1384
|
+
methodName: methodName,
|
|
1385
|
+
callbackId: callbackId,
|
|
1386
|
+
options: options,
|
|
1387
|
+
retriesRemaining: retriesRemaining,
|
|
1388
|
+
requestToken: requestToken,
|
|
1389
|
+
message: "Bridge not available for \(methodName == "show" ? "showing" : "hiding") splashscreen with autoSplashscreen"
|
|
1390
|
+
)
|
|
1169
1391
|
return
|
|
1170
1392
|
}
|
|
1171
1393
|
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1394
|
+
guard let splashScreenPlugin = bridge.plugin(withName: self.splashscreenPluginName) else {
|
|
1395
|
+
self.retrySplashscreenMethod(
|
|
1396
|
+
methodName: methodName,
|
|
1397
|
+
callbackId: callbackId,
|
|
1398
|
+
options: options,
|
|
1399
|
+
retriesRemaining: retriesRemaining,
|
|
1400
|
+
requestToken: requestToken,
|
|
1401
|
+
message: "autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin."
|
|
1402
|
+
)
|
|
1403
|
+
return
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
let selector = NSSelectorFromString("\(methodName):")
|
|
1407
|
+
guard splashScreenPlugin.responds(to: selector) else {
|
|
1408
|
+
self.retrySplashscreenMethod(
|
|
1409
|
+
methodName: methodName,
|
|
1410
|
+
callbackId: callbackId,
|
|
1411
|
+
options: options,
|
|
1412
|
+
retriesRemaining: retriesRemaining,
|
|
1413
|
+
requestToken: requestToken,
|
|
1414
|
+
message: "autoSplashscreen: SplashScreen plugin does not respond to \(methodName): method. Make sure @capacitor/splash-screen plugin is properly installed."
|
|
1415
|
+
)
|
|
1416
|
+
return
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
let call = self.makeSplashscreenCall(callbackId: callbackId, options: options, methodName: methodName)
|
|
1420
|
+
_ = splashScreenPlugin.perform(selector, with: call)
|
|
1421
|
+
self.logger.info("Called SplashScreen \(methodName) method")
|
|
1422
|
+
}
|
|
1178
1423
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1424
|
+
private func retrySplashscreenMethod(
|
|
1425
|
+
methodName: String,
|
|
1426
|
+
callbackId: String,
|
|
1427
|
+
options: [String: Any],
|
|
1428
|
+
retriesRemaining: Int,
|
|
1429
|
+
requestToken: Int,
|
|
1430
|
+
message: String
|
|
1431
|
+
) {
|
|
1432
|
+
guard retriesRemaining > 0 else {
|
|
1433
|
+
if methodName == "show" {
|
|
1434
|
+
self.logger.warn(message)
|
|
1186
1435
|
} else {
|
|
1187
|
-
self.logger.
|
|
1436
|
+
self.logger.error(message)
|
|
1188
1437
|
}
|
|
1189
|
-
|
|
1190
|
-
self.logger.warn("autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin.")
|
|
1438
|
+
return
|
|
1191
1439
|
}
|
|
1192
1440
|
|
|
1193
|
-
self.
|
|
1194
|
-
|
|
1441
|
+
self.logger.info("\(message). Retrying.")
|
|
1442
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(self.splashscreenRetryDelayMilliseconds)) { [weak self] in
|
|
1443
|
+
guard let self = self, requestToken == self.splashscreenInvocationToken else {
|
|
1444
|
+
return
|
|
1445
|
+
}
|
|
1446
|
+
self.invokeSplashscreenMethod(
|
|
1447
|
+
methodName: methodName,
|
|
1448
|
+
callbackId: callbackId,
|
|
1449
|
+
options: options,
|
|
1450
|
+
retriesRemaining: retriesRemaining - 1,
|
|
1451
|
+
requestToken: requestToken
|
|
1452
|
+
)
|
|
1453
|
+
}
|
|
1195
1454
|
}
|
|
1196
1455
|
|
|
1197
1456
|
private func addSplashscreenLoaderIfNeeded() {
|
|
@@ -1345,7 +1604,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1345
1604
|
}
|
|
1346
1605
|
return false
|
|
1347
1606
|
case "onLaunch":
|
|
1348
|
-
if !self.
|
|
1607
|
+
if !self.getOnLaunchDirectUpdateUsed() {
|
|
1349
1608
|
return true
|
|
1350
1609
|
}
|
|
1351
1610
|
return false
|
|
@@ -1355,6 +1614,51 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1355
1614
|
}
|
|
1356
1615
|
}
|
|
1357
1616
|
|
|
1617
|
+
static func shouldConsumeOnLaunchDirectUpdate(directUpdateMode: String, plannedDirectUpdate: Bool) -> Bool {
|
|
1618
|
+
plannedDirectUpdate && directUpdateMode == "onLaunch"
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
private func getOnLaunchDirectUpdateUsed() -> Bool {
|
|
1622
|
+
self.onLaunchDirectUpdateStateLock.lock()
|
|
1623
|
+
defer { self.onLaunchDirectUpdateStateLock.unlock() }
|
|
1624
|
+
return self.onLaunchDirectUpdateUsed
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
private func setOnLaunchDirectUpdateUsed(_ used: Bool) {
|
|
1628
|
+
self.onLaunchDirectUpdateStateLock.lock()
|
|
1629
|
+
self.onLaunchDirectUpdateUsed = used
|
|
1630
|
+
self.onLaunchDirectUpdateStateLock.unlock()
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
private func consumeOnLaunchDirectUpdateAttempt(plannedDirectUpdate: Bool) {
|
|
1634
|
+
guard Self.shouldConsumeOnLaunchDirectUpdate(directUpdateMode: self.directUpdateMode, plannedDirectUpdate: plannedDirectUpdate) else {
|
|
1635
|
+
return
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
self.setOnLaunchDirectUpdateUsed(true)
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
func configureDirectUpdateModeForTesting(_ directUpdateMode: String, onLaunchDirectUpdateUsed: Bool = false) {
|
|
1642
|
+
self.directUpdateMode = directUpdateMode
|
|
1643
|
+
self.setOnLaunchDirectUpdateUsed(onLaunchDirectUpdateUsed)
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
func setUpdateUrlForTesting(_ updateUrl: String) {
|
|
1647
|
+
self.updateUrl = updateUrl
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
func setCurrentBuildVersionForTesting(_ currentBuildVersion: String) {
|
|
1651
|
+
self.currentBuildVersion = currentBuildVersion
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
func shouldUseDirectUpdateForTesting() -> Bool {
|
|
1655
|
+
self.shouldUseDirectUpdate()
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
var hasConsumedOnLaunchDirectUpdateForTesting: Bool {
|
|
1659
|
+
self.getOnLaunchDirectUpdateUsed()
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1358
1662
|
private func notifyBreakingEvents(version: String) {
|
|
1359
1663
|
guard !version.isEmpty else {
|
|
1360
1664
|
return
|
|
@@ -1364,11 +1668,58 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1364
1668
|
self.notifyListeners("majorAvailable", data: payload)
|
|
1365
1669
|
}
|
|
1366
1670
|
|
|
1671
|
+
private func updateResponseKind(kind: String?) -> String {
|
|
1672
|
+
if let kind, ["up_to_date", "blocked", "failed"].contains(kind) {
|
|
1673
|
+
return kind
|
|
1674
|
+
}
|
|
1675
|
+
return "failed"
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
private func endBackgroundDownloadAfterLatestError(
|
|
1679
|
+
backendError: String,
|
|
1680
|
+
res: AppVersion,
|
|
1681
|
+
current: BundleInfo,
|
|
1682
|
+
plannedDirectUpdate: Bool
|
|
1683
|
+
) {
|
|
1684
|
+
let statusCode = res.statusCode
|
|
1685
|
+
let responseKind = self.updateResponseKind(kind: res.kind)
|
|
1686
|
+
let responseMessage = res.message?.isEmpty == false ? res.message : nil
|
|
1687
|
+
let message = responseMessage ?? (backendError.isEmpty ? "server did not provide a message" : backendError)
|
|
1688
|
+
let latestVersionName = res.version.isEmpty ? current.getVersionName() : res.version
|
|
1689
|
+
self.notifyListeners("updateCheckResult", data: [
|
|
1690
|
+
"kind": responseKind,
|
|
1691
|
+
"error": backendError,
|
|
1692
|
+
"message": message,
|
|
1693
|
+
"statusCode": statusCode,
|
|
1694
|
+
"version": latestVersionName,
|
|
1695
|
+
"bundle": current.toJSON()
|
|
1696
|
+
])
|
|
1697
|
+
|
|
1698
|
+
if responseKind == "up_to_date" {
|
|
1699
|
+
self.logger.info("No new version available")
|
|
1700
|
+
} else if responseKind == "blocked" {
|
|
1701
|
+
self.logger.info("Update check blocked with error: \(backendError)")
|
|
1702
|
+
} else {
|
|
1703
|
+
self.logger.error("getLatest failed with error: \(backendError)")
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
let isFailure = responseKind == "failed"
|
|
1707
|
+
self.endBackGroundTaskWithNotif(
|
|
1708
|
+
msg: message,
|
|
1709
|
+
latestVersionName: latestVersionName,
|
|
1710
|
+
current: current,
|
|
1711
|
+
error: isFailure,
|
|
1712
|
+
plannedDirectUpdate: plannedDirectUpdate,
|
|
1713
|
+
sendStats: isFailure
|
|
1714
|
+
)
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1367
1717
|
func endBackGroundTaskWithNotif(
|
|
1368
1718
|
msg: String,
|
|
1369
1719
|
latestVersionName: String,
|
|
1370
1720
|
current: BundleInfo,
|
|
1371
1721
|
error: Bool = true,
|
|
1722
|
+
plannedDirectUpdate: Bool = false,
|
|
1372
1723
|
failureAction: String = "download_fail",
|
|
1373
1724
|
failureEvent: String = "downloadFailed",
|
|
1374
1725
|
sendStats: Bool = true
|
|
@@ -1380,6 +1731,8 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1380
1731
|
downloadInProgress = false
|
|
1381
1732
|
downloadStartTime = nil
|
|
1382
1733
|
|
|
1734
|
+
self.consumeOnLaunchDirectUpdateAttempt(plannedDirectUpdate: plannedDirectUpdate)
|
|
1735
|
+
|
|
1383
1736
|
if error {
|
|
1384
1737
|
if sendStats {
|
|
1385
1738
|
self.implementation.sendStats(action: failureAction, versionName: current.getVersionName())
|
|
@@ -1414,6 +1767,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1414
1767
|
return true
|
|
1415
1768
|
}
|
|
1416
1769
|
|
|
1770
|
+
func runBackgroundDownloadWork(_ work: @escaping () -> Void) {
|
|
1771
|
+
DispatchQueue.global(qos: .background).async(execute: work)
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
func runGetLatestWork(_ work: @escaping () -> Void) {
|
|
1775
|
+
DispatchQueue.global(qos: .background).async(execute: work)
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1417
1778
|
func backgroundDownload() {
|
|
1418
1779
|
// Set download in progress flag (thread-safe)
|
|
1419
1780
|
downloadLock.lock()
|
|
@@ -1433,7 +1794,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1433
1794
|
return
|
|
1434
1795
|
}
|
|
1435
1796
|
|
|
1436
|
-
|
|
1797
|
+
self.runBackgroundDownloadWork {
|
|
1437
1798
|
// Wait for cleanup to complete before starting download
|
|
1438
1799
|
self.waitForCleanupIfNeeded()
|
|
1439
1800
|
self.backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "Finish Download Tasks") {
|
|
@@ -1445,16 +1806,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1445
1806
|
let current = self.implementation.getCurrentBundle()
|
|
1446
1807
|
|
|
1447
1808
|
// Handle network errors and other failures first
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
latestVersionName: res.version,
|
|
1809
|
+
let backendError = res.error ?? ""
|
|
1810
|
+
let backendKind = res.kind ?? ""
|
|
1811
|
+
if !backendError.isEmpty || !backendKind.isEmpty {
|
|
1812
|
+
self.endBackgroundDownloadAfterLatestError(
|
|
1813
|
+
backendError: backendError,
|
|
1814
|
+
res: res,
|
|
1455
1815
|
current: current,
|
|
1456
|
-
|
|
1457
|
-
sendStats: !responseIsOk
|
|
1816
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1458
1817
|
)
|
|
1459
1818
|
return
|
|
1460
1819
|
}
|
|
@@ -1463,26 +1822,39 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1463
1822
|
let directUpdateAllowed = plannedDirectUpdate && !self.autoSplashscreenTimedOut
|
|
1464
1823
|
if directUpdateAllowed {
|
|
1465
1824
|
self.logger.info("Direct update to builtin version")
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1825
|
+
_ = self._reset(toLastSuccessful: false, usePendingBundle: false)
|
|
1826
|
+
self.endBackGroundTaskWithNotif(
|
|
1827
|
+
msg: "Updated to builtin version",
|
|
1828
|
+
latestVersionName: res.version,
|
|
1829
|
+
current: self.implementation.getCurrentBundle(),
|
|
1830
|
+
error: false,
|
|
1831
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1832
|
+
)
|
|
1472
1833
|
} else {
|
|
1473
1834
|
if plannedDirectUpdate && !directUpdateAllowed {
|
|
1474
1835
|
self.logger.info("Direct update skipped because splashscreen timeout occurred. Update will apply later.")
|
|
1475
1836
|
}
|
|
1476
1837
|
self.logger.info("Setting next bundle to builtin")
|
|
1477
1838
|
_ = self.implementation.setNextBundle(next: BundleInfo.ID_BUILTIN)
|
|
1478
|
-
self.endBackGroundTaskWithNotif(
|
|
1839
|
+
self.endBackGroundTaskWithNotif(
|
|
1840
|
+
msg: "Next update will be to builtin version",
|
|
1841
|
+
latestVersionName: res.version,
|
|
1842
|
+
current: current,
|
|
1843
|
+
error: false,
|
|
1844
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1845
|
+
)
|
|
1479
1846
|
}
|
|
1480
1847
|
return
|
|
1481
1848
|
}
|
|
1482
1849
|
let sessionKey = res.sessionKey ?? ""
|
|
1483
1850
|
guard let downloadUrl = URL(string: res.url) else {
|
|
1484
1851
|
self.logger.error("Error no url or wrong format")
|
|
1485
|
-
self.endBackGroundTaskWithNotif(
|
|
1852
|
+
self.endBackGroundTaskWithNotif(
|
|
1853
|
+
msg: "Error no url or wrong format",
|
|
1854
|
+
latestVersionName: res.version,
|
|
1855
|
+
current: current,
|
|
1856
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1857
|
+
)
|
|
1486
1858
|
return
|
|
1487
1859
|
}
|
|
1488
1860
|
let latestVersionName = res.version
|
|
@@ -1500,6 +1872,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1500
1872
|
self.logger.error("Failed to delete failed bundle: \(nextImpl!.toString())")
|
|
1501
1873
|
}
|
|
1502
1874
|
}
|
|
1875
|
+
self.consumeOnLaunchDirectUpdateAttempt(plannedDirectUpdate: plannedDirectUpdate)
|
|
1503
1876
|
if res.manifest != nil {
|
|
1504
1877
|
nextImpl = try self.implementation.downloadManifest(manifest: res.manifest!, version: latestVersionName, sessionKey: sessionKey, link: res.link, comment: res.comment)
|
|
1505
1878
|
} else {
|
|
@@ -1508,12 +1881,22 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1508
1881
|
}
|
|
1509
1882
|
guard let next = nextImpl else {
|
|
1510
1883
|
self.logger.error("Error downloading file")
|
|
1511
|
-
self.endBackGroundTaskWithNotif(
|
|
1884
|
+
self.endBackGroundTaskWithNotif(
|
|
1885
|
+
msg: "Error downloading file",
|
|
1886
|
+
latestVersionName: latestVersionName,
|
|
1887
|
+
current: current,
|
|
1888
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1889
|
+
)
|
|
1512
1890
|
return
|
|
1513
1891
|
}
|
|
1514
1892
|
if next.isErrorStatus() {
|
|
1515
1893
|
self.logger.error("Latest bundle already exists and is in error state. Aborting update.")
|
|
1516
|
-
self.endBackGroundTaskWithNotif(
|
|
1894
|
+
self.endBackGroundTaskWithNotif(
|
|
1895
|
+
msg: "Latest version is in error state. Aborting update.",
|
|
1896
|
+
latestVersionName: latestVersionName,
|
|
1897
|
+
current: current,
|
|
1898
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1899
|
+
)
|
|
1517
1900
|
return
|
|
1518
1901
|
}
|
|
1519
1902
|
res.checksum = try CryptoCipher.decryptChecksum(checksum: res.checksum, publicKey: self.implementation.publicKey)
|
|
@@ -1527,7 +1910,12 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1527
1910
|
if !resDel {
|
|
1528
1911
|
self.logger.error("Delete failed, id \(id) doesn't exist")
|
|
1529
1912
|
}
|
|
1530
|
-
self.endBackGroundTaskWithNotif(
|
|
1913
|
+
self.endBackGroundTaskWithNotif(
|
|
1914
|
+
msg: "Error checksum",
|
|
1915
|
+
latestVersionName: latestVersionName,
|
|
1916
|
+
current: current,
|
|
1917
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1918
|
+
)
|
|
1531
1919
|
return
|
|
1532
1920
|
}
|
|
1533
1921
|
let directUpdateAllowed = plannedDirectUpdate && !self.autoSplashscreenTimedOut
|
|
@@ -1540,34 +1928,67 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1540
1928
|
}
|
|
1541
1929
|
if !delayConditionList.isEmpty {
|
|
1542
1930
|
self.logger.info("Update delayed until delay conditions met")
|
|
1543
|
-
self.endBackGroundTaskWithNotif(
|
|
1931
|
+
self.endBackGroundTaskWithNotif(
|
|
1932
|
+
msg: "Update delayed until delay conditions met",
|
|
1933
|
+
latestVersionName: latestVersionName,
|
|
1934
|
+
current: next,
|
|
1935
|
+
error: false,
|
|
1936
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1937
|
+
)
|
|
1544
1938
|
return
|
|
1545
1939
|
}
|
|
1546
|
-
if self.
|
|
1547
|
-
self.
|
|
1548
|
-
self.
|
|
1940
|
+
if self.implementation.set(bundle: next) && self._reload() {
|
|
1941
|
+
self.notifyBundleSet(next)
|
|
1942
|
+
self.endBackGroundTaskWithNotif(
|
|
1943
|
+
msg: "update installed",
|
|
1944
|
+
latestVersionName: latestVersionName,
|
|
1945
|
+
current: next,
|
|
1946
|
+
error: false,
|
|
1947
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1948
|
+
)
|
|
1949
|
+
} else {
|
|
1950
|
+
self.endBackGroundTaskWithNotif(
|
|
1951
|
+
msg: "Update install failed",
|
|
1952
|
+
latestVersionName: latestVersionName,
|
|
1953
|
+
current: next,
|
|
1954
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1955
|
+
)
|
|
1549
1956
|
}
|
|
1550
|
-
_ = self.implementation.set(bundle: next)
|
|
1551
|
-
_ = self._reload()
|
|
1552
|
-
self.endBackGroundTaskWithNotif(msg: "update installed", latestVersionName: latestVersionName, current: next, error: false)
|
|
1553
1957
|
} else {
|
|
1554
1958
|
if plannedDirectUpdate && !directUpdateAllowed {
|
|
1555
1959
|
self.logger.info("Direct update skipped because splashscreen timeout occurred. Update will install on next app background.")
|
|
1556
1960
|
}
|
|
1557
1961
|
self.notifyListeners("updateAvailable", data: ["bundle": next.toJSON()])
|
|
1558
1962
|
_ = self.implementation.setNextBundle(next: next.getId())
|
|
1559
|
-
self.endBackGroundTaskWithNotif(
|
|
1963
|
+
self.endBackGroundTaskWithNotif(
|
|
1964
|
+
msg: "update downloaded, will install next background",
|
|
1965
|
+
latestVersionName: latestVersionName,
|
|
1966
|
+
current: current,
|
|
1967
|
+
error: false,
|
|
1968
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1969
|
+
)
|
|
1560
1970
|
}
|
|
1561
1971
|
return
|
|
1562
1972
|
} catch {
|
|
1563
1973
|
self.logger.error("Error downloading file \(error.localizedDescription)")
|
|
1564
1974
|
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
1565
|
-
self.endBackGroundTaskWithNotif(
|
|
1975
|
+
self.endBackGroundTaskWithNotif(
|
|
1976
|
+
msg: "Error downloading file",
|
|
1977
|
+
latestVersionName: latestVersionName,
|
|
1978
|
+
current: current,
|
|
1979
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1980
|
+
)
|
|
1566
1981
|
return
|
|
1567
1982
|
}
|
|
1568
1983
|
} else {
|
|
1569
1984
|
self.logger.info("No need to update, \(current.getId()) is the latest bundle.")
|
|
1570
|
-
self.endBackGroundTaskWithNotif(
|
|
1985
|
+
self.endBackGroundTaskWithNotif(
|
|
1986
|
+
msg: "No need to update, \(current.getId()) is the latest bundle.",
|
|
1987
|
+
latestVersionName: latestVersionName,
|
|
1988
|
+
current: current,
|
|
1989
|
+
error: false,
|
|
1990
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1991
|
+
)
|
|
1571
1992
|
return
|
|
1572
1993
|
}
|
|
1573
1994
|
}
|
|
@@ -1591,6 +2012,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1591
2012
|
logger.info("Next bundle is: \(next!.toString())")
|
|
1592
2013
|
if self.implementation.set(bundle: next!) && self._reload() {
|
|
1593
2014
|
logger.info("Updated to bundle: \(next!.toString())")
|
|
2015
|
+
self.notifyBundleSet(next!)
|
|
1594
2016
|
_ = self.implementation.setNextBundle(next: Optional<String>.none)
|
|
1595
2017
|
} else {
|
|
1596
2018
|
logger.error("Update to bundle: \(next!.toString()) Failed!")
|