@capgo/capacitor-updater 6.43.5 → 6.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 +1 -1
- package/README.md +149 -39
- package/android/build.gradle +3 -3
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +534 -170
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +151 -28
- 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/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +557 -134
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +213 -50
- package/ios/Sources/CapacitorUpdaterPlugin/InternalUtils.swift +2 -0
- package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +20 -3
- package/package.json +5 -2
|
@@ -72,7 +72,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
72
72
|
CAPPluginMethod(name: "completeFlexibleUpdate", returnType: CAPPluginReturnPromise)
|
|
73
73
|
]
|
|
74
74
|
public var implementation = CapgoUpdater()
|
|
75
|
-
private let pluginVersion: String = "
|
|
75
|
+
private let pluginVersion: String = "6.45.10"
|
|
76
76
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
77
77
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
78
78
|
static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
|
|
@@ -102,7 +102,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
102
102
|
private var autoSplashscreenTimeoutWorkItem: DispatchWorkItem?
|
|
103
103
|
private var splashscreenLoaderView: UIActivityIndicatorView?
|
|
104
104
|
private var splashscreenLoaderContainer: UIView?
|
|
105
|
+
private let splashscreenPluginName = "SplashScreen"
|
|
106
|
+
private let splashscreenRetryDelayMilliseconds = 100
|
|
107
|
+
private let splashscreenMaxRetries = 20
|
|
105
108
|
private var autoSplashscreenTimedOut = false
|
|
109
|
+
private var splashscreenInvocationToken = 0
|
|
106
110
|
private var autoDeleteFailed = false
|
|
107
111
|
private var autoDeletePrevious = false
|
|
108
112
|
var allowSetDefaultChannel = true
|
|
@@ -111,6 +115,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
111
115
|
private var taskRunning = false
|
|
112
116
|
private var periodCheckDelay = 0
|
|
113
117
|
private let downloadLock = NSLock()
|
|
118
|
+
private let onLaunchDirectUpdateStateLock = NSLock()
|
|
114
119
|
private var downloadInProgress = false
|
|
115
120
|
private var downloadStartTime: Date?
|
|
116
121
|
private let downloadTimeout: TimeInterval = 3600 // 1 hour timeout
|
|
@@ -227,6 +232,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
227
232
|
|
|
228
233
|
implementation.setPublicKey(getConfig().getString("publicKey") ?? "")
|
|
229
234
|
implementation.notifyDownloadRaw = notifyDownload
|
|
235
|
+
implementation.notifyListeners = { [weak self] eventName, data in
|
|
236
|
+
self?.notifyListeners(eventName, data: data)
|
|
237
|
+
}
|
|
230
238
|
implementation.pluginVersion = self.pluginVersion
|
|
231
239
|
|
|
232
240
|
// Set logger for shared classes
|
|
@@ -271,11 +279,12 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
271
279
|
}
|
|
272
280
|
self.implementation.autoReset()
|
|
273
281
|
|
|
274
|
-
// Check if app was recently installed/updated BEFORE
|
|
282
|
+
// Check if app was recently installed/updated BEFORE cleanup updates the stored native build version.
|
|
275
283
|
self.wasRecentlyInstalledOrUpdated = self.checkIfRecentlyInstalledOrUpdated()
|
|
276
284
|
|
|
277
285
|
if resetWhenUpdate {
|
|
278
|
-
self.
|
|
286
|
+
let didResetCurrentBundle = self.resetCurrentBundleForNativeBuildChangeIfNeeded()
|
|
287
|
+
self.cleanupObsoleteVersions(didResetCurrentBundle: didResetCurrentBundle)
|
|
279
288
|
}
|
|
280
289
|
|
|
281
290
|
// Load the server
|
|
@@ -391,7 +400,29 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
391
400
|
semaphoreReady.signal()
|
|
392
401
|
}
|
|
393
402
|
|
|
394
|
-
|
|
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) {
|
|
395
426
|
cleanupThread = Thread {
|
|
396
427
|
self.cleanupLock.lock()
|
|
397
428
|
defer {
|
|
@@ -426,9 +457,12 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
426
457
|
// 1. Write "LatestVersionNative" - this fixes the part 1 of this bug
|
|
427
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
|
|
428
459
|
|
|
429
|
-
let previous =
|
|
460
|
+
let previous = self.storedNativeBuildVersion()
|
|
430
461
|
if previous != "0" && self.currentBuildVersion != previous {
|
|
431
|
-
|
|
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
|
+
}
|
|
432
466
|
let res = self.implementation.list()
|
|
433
467
|
for version in res {
|
|
434
468
|
// Check if thread was cancelled
|
|
@@ -647,47 +681,77 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
647
681
|
}
|
|
648
682
|
}
|
|
649
683
|
|
|
650
|
-
|
|
651
|
-
guard let bridge = self.bridge else { return false }
|
|
652
|
-
self.semaphoreUp()
|
|
684
|
+
private func currentReloadDestination() -> URL {
|
|
653
685
|
let id = self.implementation.getCurrentBundleId()
|
|
654
|
-
let dest: URL
|
|
655
686
|
if BundleInfo.ID_BUILTIN == id {
|
|
656
|
-
|
|
687
|
+
return Bundle.main.resourceURL!.appendingPathComponent("public")
|
|
657
688
|
} else {
|
|
658
|
-
|
|
689
|
+
return self.implementation.getBundleDirectory(id: id)
|
|
659
690
|
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
private func applyCurrentBundleToBridge(_ bridge: CAPBridgeProtocol) -> Bool {
|
|
694
|
+
let id = self.implementation.getCurrentBundleId()
|
|
695
|
+
let dest = self.currentReloadDestination()
|
|
660
696
|
logger.info("Reloading \(id)")
|
|
661
697
|
|
|
662
|
-
let
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
if
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
_ = vc.webView?.load(URLRequest(url: finalUrl))
|
|
680
|
-
} else {
|
|
681
|
-
self.logger.error("Unable to build final URL when keeping path after reload; falling back to base path")
|
|
682
|
-
vc.setServerBasePath(path: dest.path)
|
|
683
|
-
}
|
|
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))
|
|
684
715
|
} else {
|
|
685
|
-
self.logger.error("
|
|
716
|
+
self.logger.error("Unable to build final URL when keeping path after reload; falling back to base path")
|
|
686
717
|
vc.setServerBasePath(path: dest.path)
|
|
687
718
|
}
|
|
688
719
|
} else {
|
|
720
|
+
self.logger.error("vc.webView?.url is null? Falling back to base path reload.")
|
|
689
721
|
vc.setServerBasePath(path: dest.path)
|
|
690
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
|
+
}
|
|
691
755
|
self.checkAppReady()
|
|
692
756
|
self.notifyListeners("appReloaded", data: [:])
|
|
693
757
|
return true
|
|
@@ -705,6 +769,38 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
705
769
|
}
|
|
706
770
|
|
|
707
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
|
+
|
|
708
804
|
if self._reload() {
|
|
709
805
|
call.resolve()
|
|
710
806
|
} else {
|
|
@@ -739,8 +835,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
739
835
|
if !res {
|
|
740
836
|
logger.info("Bundle successfully set to: \(id) ")
|
|
741
837
|
call.reject("Update failed, id \(id) doesn't exist")
|
|
838
|
+
} else if !self._reload() {
|
|
839
|
+
call.reject("Reload failed after setting bundle \(id)")
|
|
742
840
|
} else {
|
|
743
|
-
self.
|
|
841
|
+
self.notifyBundleSet(self.implementation.getBundleInfo(id: id))
|
|
842
|
+
call.resolve()
|
|
744
843
|
}
|
|
745
844
|
}
|
|
746
845
|
|
|
@@ -803,12 +902,32 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
803
902
|
|
|
804
903
|
@objc func getLatest(_ call: CAPPluginCall) {
|
|
805
904
|
let channel = call.getString("channel")
|
|
806
|
-
|
|
905
|
+
runGetLatestWork {
|
|
807
906
|
let res = self.implementation.getLatest(url: URL(string: self.updateUrl)!, channel: channel)
|
|
808
|
-
if res.error
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
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)
|
|
812
931
|
} else {
|
|
813
932
|
call.resolve(res.toDict())
|
|
814
933
|
}
|
|
@@ -925,32 +1044,90 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
925
1044
|
call.resolve()
|
|
926
1045
|
}
|
|
927
1046
|
|
|
928
|
-
@objc func _reset(toLastSuccessful: Bool) -> Bool {
|
|
929
|
-
|
|
1047
|
+
@objc func _reset(toLastSuccessful: Bool, usePendingBundle: Bool) -> Bool {
|
|
1048
|
+
self.performReset(toLastSuccessful: toLastSuccessful, usePendingBundle: usePendingBundle, isInternal: false)
|
|
1049
|
+
}
|
|
930
1050
|
|
|
931
|
-
|
|
932
|
-
|
|
1051
|
+
func performReset(toLastSuccessful: Bool, usePendingBundle: Bool, isInternal: Bool) -> Bool {
|
|
1052
|
+
guard self.canPerformResetTransition() else { return false }
|
|
933
1053
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
return self.implementation.set(bundle: fallback) && self._reload()
|
|
939
|
-
}
|
|
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()
|
|
940
1058
|
|
|
941
|
-
|
|
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
|
+
}
|
|
942
1086
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
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
|
+
}
|
|
946
1107
|
}
|
|
947
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
|
+
}
|
|
948
1119
|
return false
|
|
949
1120
|
}
|
|
950
1121
|
|
|
1122
|
+
func canPerformResetTransition() -> Bool {
|
|
1123
|
+
guard let bridge = self.bridge else { return false }
|
|
1124
|
+
return (bridge.viewController as? CAPBridgeViewController) != nil
|
|
1125
|
+
}
|
|
1126
|
+
|
|
951
1127
|
@objc func reset(_ call: CAPPluginCall) {
|
|
952
1128
|
let toLastSuccessful = call.getBool("toLastSuccessful") ?? false
|
|
953
|
-
|
|
1129
|
+
let usePendingBundle = call.getBool("usePendingBundle") ?? false
|
|
1130
|
+
if self._reset(toLastSuccessful: toLastSuccessful, usePendingBundle: usePendingBundle) {
|
|
954
1131
|
call.resolve()
|
|
955
1132
|
} else {
|
|
956
1133
|
logger.error("Reset failed")
|
|
@@ -1069,7 +1246,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1069
1246
|
self.persistLastFailedBundle(current)
|
|
1070
1247
|
self.implementation.sendStats(action: "update_fail", versionName: current.getVersionName())
|
|
1071
1248
|
self.implementation.setError(bundle: current)
|
|
1072
|
-
_ = self.
|
|
1249
|
+
_ = self.performReset(toLastSuccessful: true, usePendingBundle: false, isInternal: true)
|
|
1073
1250
|
if self.autoDeleteFailed && !current.isBuiltin() {
|
|
1074
1251
|
logger.info("Deleting failing bundle: \(current.toString())")
|
|
1075
1252
|
let res = self.implementation.delete(id: current.getId(), removeInfo: false)
|
|
@@ -1094,6 +1271,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1094
1271
|
self.backgroundTaskID = UIBackgroundTaskIdentifier.invalid
|
|
1095
1272
|
}
|
|
1096
1273
|
|
|
1274
|
+
private func notifyBundleSet(_ bundle: BundleInfo) {
|
|
1275
|
+
self.notifyListeners("set", data: ["bundle": bundle.toJSON()], retainUntilConsumed: true)
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1097
1278
|
func sendReadyToJs(current: BundleInfo, msg: String) {
|
|
1098
1279
|
logger.info("sendReadyToJs")
|
|
1099
1280
|
DispatchQueue.global().async {
|
|
@@ -1121,32 +1302,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1121
1302
|
private func performHideSplashscreen() {
|
|
1122
1303
|
self.cancelSplashscreenTimeout()
|
|
1123
1304
|
self.removeSplashscreenLoader()
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
self.logger.info("Splashscreen hidden automatically")
|
|
1133
|
-
}, error: { (_) in
|
|
1134
|
-
self.logger.error("Failed to auto-hide splashscreen")
|
|
1135
|
-
})
|
|
1136
|
-
|
|
1137
|
-
// Try to call the SplashScreen hide method directly through the bridge
|
|
1138
|
-
if let splashScreenPlugin = bridge.plugin(withName: "SplashScreen") {
|
|
1139
|
-
// Use runtime method invocation to call hide method
|
|
1140
|
-
let selector = NSSelectorFromString("hide:")
|
|
1141
|
-
if splashScreenPlugin.responds(to: selector) {
|
|
1142
|
-
_ = splashScreenPlugin.perform(selector, with: call)
|
|
1143
|
-
self.logger.info("Called SplashScreen hide method")
|
|
1144
|
-
} else {
|
|
1145
|
-
self.logger.warn("autoSplashscreen: SplashScreen plugin does not respond to hide: method. Make sure @capacitor/splash-screen plugin is properly installed.")
|
|
1146
|
-
}
|
|
1147
|
-
} else {
|
|
1148
|
-
self.logger.warn("autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin.")
|
|
1149
|
-
}
|
|
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
|
+
)
|
|
1150
1313
|
}
|
|
1151
1314
|
|
|
1152
1315
|
private func showSplashscreen() {
|
|
@@ -1162,35 +1325,132 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1162
1325
|
private func performShowSplashscreen() {
|
|
1163
1326
|
self.cancelSplashscreenTimeout()
|
|
1164
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
|
+
}
|
|
1165
1381
|
|
|
1166
1382
|
guard let bridge = self.bridge else {
|
|
1167
|
-
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
|
+
)
|
|
1168
1391
|
return
|
|
1169
1392
|
}
|
|
1170
1393
|
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
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
|
+
}
|
|
1177
1423
|
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
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)
|
|
1185
1435
|
} else {
|
|
1186
|
-
self.logger.
|
|
1436
|
+
self.logger.error(message)
|
|
1187
1437
|
}
|
|
1188
|
-
|
|
1189
|
-
self.logger.warn("autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin.")
|
|
1438
|
+
return
|
|
1190
1439
|
}
|
|
1191
1440
|
|
|
1192
|
-
self.
|
|
1193
|
-
|
|
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
|
+
}
|
|
1194
1454
|
}
|
|
1195
1455
|
|
|
1196
1456
|
private func addSplashscreenLoaderIfNeeded() {
|
|
@@ -1344,7 +1604,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1344
1604
|
}
|
|
1345
1605
|
return false
|
|
1346
1606
|
case "onLaunch":
|
|
1347
|
-
if !self.
|
|
1607
|
+
if !self.getOnLaunchDirectUpdateUsed() {
|
|
1348
1608
|
return true
|
|
1349
1609
|
}
|
|
1350
1610
|
return false
|
|
@@ -1354,6 +1614,51 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1354
1614
|
}
|
|
1355
1615
|
}
|
|
1356
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
|
+
|
|
1357
1662
|
private func notifyBreakingEvents(version: String) {
|
|
1358
1663
|
guard !version.isEmpty else {
|
|
1359
1664
|
return
|
|
@@ -1363,11 +1668,58 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1363
1668
|
self.notifyListeners("majorAvailable", data: payload)
|
|
1364
1669
|
}
|
|
1365
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
|
+
|
|
1366
1717
|
func endBackGroundTaskWithNotif(
|
|
1367
1718
|
msg: String,
|
|
1368
1719
|
latestVersionName: String,
|
|
1369
1720
|
current: BundleInfo,
|
|
1370
1721
|
error: Bool = true,
|
|
1722
|
+
plannedDirectUpdate: Bool = false,
|
|
1371
1723
|
failureAction: String = "download_fail",
|
|
1372
1724
|
failureEvent: String = "downloadFailed",
|
|
1373
1725
|
sendStats: Bool = true
|
|
@@ -1379,6 +1731,8 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1379
1731
|
downloadInProgress = false
|
|
1380
1732
|
downloadStartTime = nil
|
|
1381
1733
|
|
|
1734
|
+
self.consumeOnLaunchDirectUpdateAttempt(plannedDirectUpdate: plannedDirectUpdate)
|
|
1735
|
+
|
|
1382
1736
|
if error {
|
|
1383
1737
|
if sendStats {
|
|
1384
1738
|
self.implementation.sendStats(action: failureAction, versionName: current.getVersionName())
|
|
@@ -1413,6 +1767,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1413
1767
|
return true
|
|
1414
1768
|
}
|
|
1415
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
|
+
|
|
1416
1778
|
func backgroundDownload() {
|
|
1417
1779
|
// Set download in progress flag (thread-safe)
|
|
1418
1780
|
downloadLock.lock()
|
|
@@ -1432,7 +1794,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1432
1794
|
return
|
|
1433
1795
|
}
|
|
1434
1796
|
|
|
1435
|
-
|
|
1797
|
+
self.runBackgroundDownloadWork {
|
|
1436
1798
|
// Wait for cleanup to complete before starting download
|
|
1437
1799
|
self.waitForCleanupIfNeeded()
|
|
1438
1800
|
self.backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "Finish Download Tasks") {
|
|
@@ -1444,16 +1806,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1444
1806
|
let current = self.implementation.getCurrentBundle()
|
|
1445
1807
|
|
|
1446
1808
|
// Handle network errors and other failures first
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
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,
|
|
1454
1815
|
current: current,
|
|
1455
|
-
|
|
1456
|
-
sendStats: !responseIsOk
|
|
1816
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1457
1817
|
)
|
|
1458
1818
|
return
|
|
1459
1819
|
}
|
|
@@ -1462,26 +1822,39 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1462
1822
|
let directUpdateAllowed = plannedDirectUpdate && !self.autoSplashscreenTimedOut
|
|
1463
1823
|
if directUpdateAllowed {
|
|
1464
1824
|
self.logger.info("Direct update to builtin version")
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
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
|
+
)
|
|
1471
1833
|
} else {
|
|
1472
1834
|
if plannedDirectUpdate && !directUpdateAllowed {
|
|
1473
1835
|
self.logger.info("Direct update skipped because splashscreen timeout occurred. Update will apply later.")
|
|
1474
1836
|
}
|
|
1475
1837
|
self.logger.info("Setting next bundle to builtin")
|
|
1476
1838
|
_ = self.implementation.setNextBundle(next: BundleInfo.ID_BUILTIN)
|
|
1477
|
-
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
|
+
)
|
|
1478
1846
|
}
|
|
1479
1847
|
return
|
|
1480
1848
|
}
|
|
1481
1849
|
let sessionKey = res.sessionKey ?? ""
|
|
1482
1850
|
guard let downloadUrl = URL(string: res.url) else {
|
|
1483
1851
|
self.logger.error("Error no url or wrong format")
|
|
1484
|
-
self.endBackGroundTaskWithNotif(
|
|
1852
|
+
self.endBackGroundTaskWithNotif(
|
|
1853
|
+
msg: "Error no url or wrong format",
|
|
1854
|
+
latestVersionName: res.version,
|
|
1855
|
+
current: current,
|
|
1856
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1857
|
+
)
|
|
1485
1858
|
return
|
|
1486
1859
|
}
|
|
1487
1860
|
let latestVersionName = res.version
|
|
@@ -1499,6 +1872,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1499
1872
|
self.logger.error("Failed to delete failed bundle: \(nextImpl!.toString())")
|
|
1500
1873
|
}
|
|
1501
1874
|
}
|
|
1875
|
+
self.consumeOnLaunchDirectUpdateAttempt(plannedDirectUpdate: plannedDirectUpdate)
|
|
1502
1876
|
if res.manifest != nil {
|
|
1503
1877
|
nextImpl = try self.implementation.downloadManifest(manifest: res.manifest!, version: latestVersionName, sessionKey: sessionKey, link: res.link, comment: res.comment)
|
|
1504
1878
|
} else {
|
|
@@ -1507,12 +1881,22 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1507
1881
|
}
|
|
1508
1882
|
guard let next = nextImpl else {
|
|
1509
1883
|
self.logger.error("Error downloading file")
|
|
1510
|
-
self.endBackGroundTaskWithNotif(
|
|
1884
|
+
self.endBackGroundTaskWithNotif(
|
|
1885
|
+
msg: "Error downloading file",
|
|
1886
|
+
latestVersionName: latestVersionName,
|
|
1887
|
+
current: current,
|
|
1888
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1889
|
+
)
|
|
1511
1890
|
return
|
|
1512
1891
|
}
|
|
1513
1892
|
if next.isErrorStatus() {
|
|
1514
1893
|
self.logger.error("Latest bundle already exists and is in error state. Aborting update.")
|
|
1515
|
-
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
|
+
)
|
|
1516
1900
|
return
|
|
1517
1901
|
}
|
|
1518
1902
|
res.checksum = try CryptoCipher.decryptChecksum(checksum: res.checksum, publicKey: self.implementation.publicKey)
|
|
@@ -1526,7 +1910,12 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1526
1910
|
if !resDel {
|
|
1527
1911
|
self.logger.error("Delete failed, id \(id) doesn't exist")
|
|
1528
1912
|
}
|
|
1529
|
-
self.endBackGroundTaskWithNotif(
|
|
1913
|
+
self.endBackGroundTaskWithNotif(
|
|
1914
|
+
msg: "Error checksum",
|
|
1915
|
+
latestVersionName: latestVersionName,
|
|
1916
|
+
current: current,
|
|
1917
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1918
|
+
)
|
|
1530
1919
|
return
|
|
1531
1920
|
}
|
|
1532
1921
|
let directUpdateAllowed = plannedDirectUpdate && !self.autoSplashscreenTimedOut
|
|
@@ -1539,34 +1928,67 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1539
1928
|
}
|
|
1540
1929
|
if !delayConditionList.isEmpty {
|
|
1541
1930
|
self.logger.info("Update delayed until delay conditions met")
|
|
1542
|
-
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
|
+
)
|
|
1543
1938
|
return
|
|
1544
1939
|
}
|
|
1545
|
-
if self.
|
|
1546
|
-
self.
|
|
1547
|
-
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
|
+
)
|
|
1548
1956
|
}
|
|
1549
|
-
_ = self.implementation.set(bundle: next)
|
|
1550
|
-
_ = self._reload()
|
|
1551
|
-
self.endBackGroundTaskWithNotif(msg: "update installed", latestVersionName: latestVersionName, current: next, error: false)
|
|
1552
1957
|
} else {
|
|
1553
1958
|
if plannedDirectUpdate && !directUpdateAllowed {
|
|
1554
1959
|
self.logger.info("Direct update skipped because splashscreen timeout occurred. Update will install on next app background.")
|
|
1555
1960
|
}
|
|
1556
1961
|
self.notifyListeners("updateAvailable", data: ["bundle": next.toJSON()])
|
|
1557
1962
|
_ = self.implementation.setNextBundle(next: next.getId())
|
|
1558
|
-
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
|
+
)
|
|
1559
1970
|
}
|
|
1560
1971
|
return
|
|
1561
1972
|
} catch {
|
|
1562
1973
|
self.logger.error("Error downloading file \(error.localizedDescription)")
|
|
1563
1974
|
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
1564
|
-
self.endBackGroundTaskWithNotif(
|
|
1975
|
+
self.endBackGroundTaskWithNotif(
|
|
1976
|
+
msg: "Error downloading file",
|
|
1977
|
+
latestVersionName: latestVersionName,
|
|
1978
|
+
current: current,
|
|
1979
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1980
|
+
)
|
|
1565
1981
|
return
|
|
1566
1982
|
}
|
|
1567
1983
|
} else {
|
|
1568
1984
|
self.logger.info("No need to update, \(current.getId()) is the latest bundle.")
|
|
1569
|
-
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
|
+
)
|
|
1570
1992
|
return
|
|
1571
1993
|
}
|
|
1572
1994
|
}
|
|
@@ -1590,6 +2012,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1590
2012
|
logger.info("Next bundle is: \(next!.toString())")
|
|
1591
2013
|
if self.implementation.set(bundle: next!) && self._reload() {
|
|
1592
2014
|
logger.info("Updated to bundle: \(next!.toString())")
|
|
2015
|
+
self.notifyBundleSet(next!)
|
|
1593
2016
|
_ = self.implementation.setNextBundle(next: Optional<String>.none)
|
|
1594
2017
|
} else {
|
|
1595
2018
|
logger.error("Update to bundle: \(next!.toString()) Failed!")
|