@capgo/capacitor-updater 6.43.4 → 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 +5 -2
- package/README.md +151 -41
- 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 +173 -43
- 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 +558 -140
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +213 -55
- package/ios/Sources/CapacitorUpdaterPlugin/CryptoCipher.swift +30 -36
- 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 +12 -9
|
@@ -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 = "6.
|
|
75
|
+
private let pluginVersion: String = "6.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
|
|
@@ -226,12 +230,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
226
230
|
periodCheckDelay = periodCheckDelayValue
|
|
227
231
|
}
|
|
228
232
|
|
|
229
|
-
implementation.
|
|
230
|
-
implementation.publicKey = getConfig().getString("publicKey", "")!
|
|
231
|
-
if !implementation.privateKey.isEmpty {
|
|
232
|
-
implementation.hasOldPrivateKeyPropertyInConfig = true
|
|
233
|
-
}
|
|
233
|
+
implementation.setPublicKey(getConfig().getString("publicKey") ?? "")
|
|
234
234
|
implementation.notifyDownloadRaw = notifyDownload
|
|
235
|
+
implementation.notifyListeners = { [weak self] eventName, data in
|
|
236
|
+
self?.notifyListeners(eventName, data: data)
|
|
237
|
+
}
|
|
235
238
|
implementation.pluginVersion = self.pluginVersion
|
|
236
239
|
|
|
237
240
|
// Set logger for shared classes
|
|
@@ -276,11 +279,12 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
276
279
|
}
|
|
277
280
|
self.implementation.autoReset()
|
|
278
281
|
|
|
279
|
-
// Check if app was recently installed/updated BEFORE
|
|
282
|
+
// Check if app was recently installed/updated BEFORE cleanup updates the stored native build version.
|
|
280
283
|
self.wasRecentlyInstalledOrUpdated = self.checkIfRecentlyInstalledOrUpdated()
|
|
281
284
|
|
|
282
285
|
if resetWhenUpdate {
|
|
283
|
-
self.
|
|
286
|
+
let didResetCurrentBundle = self.resetCurrentBundleForNativeBuildChangeIfNeeded()
|
|
287
|
+
self.cleanupObsoleteVersions(didResetCurrentBundle: didResetCurrentBundle)
|
|
284
288
|
}
|
|
285
289
|
|
|
286
290
|
// Load the server
|
|
@@ -396,7 +400,29 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
396
400
|
semaphoreReady.signal()
|
|
397
401
|
}
|
|
398
402
|
|
|
399
|
-
|
|
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) {
|
|
400
426
|
cleanupThread = Thread {
|
|
401
427
|
self.cleanupLock.lock()
|
|
402
428
|
defer {
|
|
@@ -431,9 +457,12 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
431
457
|
// 1. Write "LatestVersionNative" - this fixes the part 1 of this bug
|
|
432
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
|
|
433
459
|
|
|
434
|
-
let previous =
|
|
460
|
+
let previous = self.storedNativeBuildVersion()
|
|
435
461
|
if previous != "0" && self.currentBuildVersion != previous {
|
|
436
|
-
|
|
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
|
+
}
|
|
437
466
|
let res = self.implementation.list()
|
|
438
467
|
for version in res {
|
|
439
468
|
// Check if thread was cancelled
|
|
@@ -652,47 +681,77 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
652
681
|
}
|
|
653
682
|
}
|
|
654
683
|
|
|
655
|
-
|
|
656
|
-
guard let bridge = self.bridge else { return false }
|
|
657
|
-
self.semaphoreUp()
|
|
684
|
+
private func currentReloadDestination() -> URL {
|
|
658
685
|
let id = self.implementation.getCurrentBundleId()
|
|
659
|
-
let dest: URL
|
|
660
686
|
if BundleInfo.ID_BUILTIN == id {
|
|
661
|
-
|
|
687
|
+
return Bundle.main.resourceURL!.appendingPathComponent("public")
|
|
662
688
|
} else {
|
|
663
|
-
|
|
689
|
+
return self.implementation.getBundleDirectory(id: id)
|
|
664
690
|
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
private func applyCurrentBundleToBridge(_ bridge: CAPBridgeProtocol) -> Bool {
|
|
694
|
+
let id = self.implementation.getCurrentBundleId()
|
|
695
|
+
let dest = self.currentReloadDestination()
|
|
665
696
|
logger.info("Reloading \(id)")
|
|
666
697
|
|
|
667
|
-
let
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
if
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
_ = vc.webView?.load(URLRequest(url: finalUrl))
|
|
685
|
-
} else {
|
|
686
|
-
self.logger.error("Unable to build final URL when keeping path after reload; falling back to base path")
|
|
687
|
-
vc.setServerBasePath(path: dest.path)
|
|
688
|
-
}
|
|
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))
|
|
689
715
|
} else {
|
|
690
|
-
self.logger.error("
|
|
716
|
+
self.logger.error("Unable to build final URL when keeping path after reload; falling back to base path")
|
|
691
717
|
vc.setServerBasePath(path: dest.path)
|
|
692
718
|
}
|
|
693
719
|
} else {
|
|
720
|
+
self.logger.error("vc.webView?.url is null? Falling back to base path reload.")
|
|
694
721
|
vc.setServerBasePath(path: dest.path)
|
|
695
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
|
+
}
|
|
696
755
|
self.checkAppReady()
|
|
697
756
|
self.notifyListeners("appReloaded", data: [:])
|
|
698
757
|
return true
|
|
@@ -710,6 +769,38 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
710
769
|
}
|
|
711
770
|
|
|
712
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
|
+
|
|
713
804
|
if self._reload() {
|
|
714
805
|
call.resolve()
|
|
715
806
|
} else {
|
|
@@ -744,8 +835,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
744
835
|
if !res {
|
|
745
836
|
logger.info("Bundle successfully set to: \(id) ")
|
|
746
837
|
call.reject("Update failed, id \(id) doesn't exist")
|
|
838
|
+
} else if !self._reload() {
|
|
839
|
+
call.reject("Reload failed after setting bundle \(id)")
|
|
747
840
|
} else {
|
|
748
|
-
self.
|
|
841
|
+
self.notifyBundleSet(self.implementation.getBundleInfo(id: id))
|
|
842
|
+
call.resolve()
|
|
749
843
|
}
|
|
750
844
|
}
|
|
751
845
|
|
|
@@ -808,12 +902,32 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
808
902
|
|
|
809
903
|
@objc func getLatest(_ call: CAPPluginCall) {
|
|
810
904
|
let channel = call.getString("channel")
|
|
811
|
-
|
|
905
|
+
runGetLatestWork {
|
|
812
906
|
let res = self.implementation.getLatest(url: URL(string: self.updateUrl)!, channel: channel)
|
|
813
|
-
if res.error
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
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)
|
|
817
931
|
} else {
|
|
818
932
|
call.resolve(res.toDict())
|
|
819
933
|
}
|
|
@@ -930,32 +1044,90 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
930
1044
|
call.resolve()
|
|
931
1045
|
}
|
|
932
1046
|
|
|
933
|
-
@objc func _reset(toLastSuccessful: Bool) -> Bool {
|
|
934
|
-
|
|
1047
|
+
@objc func _reset(toLastSuccessful: Bool, usePendingBundle: Bool) -> Bool {
|
|
1048
|
+
self.performReset(toLastSuccessful: toLastSuccessful, usePendingBundle: usePendingBundle, isInternal: false)
|
|
1049
|
+
}
|
|
935
1050
|
|
|
936
|
-
|
|
937
|
-
|
|
1051
|
+
func performReset(toLastSuccessful: Bool, usePendingBundle: Bool, isInternal: Bool) -> Bool {
|
|
1052
|
+
guard self.canPerformResetTransition() else { return false }
|
|
938
1053
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
return self.implementation.set(bundle: fallback) && self._reload()
|
|
944
|
-
}
|
|
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()
|
|
945
1058
|
|
|
946
|
-
|
|
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
|
+
}
|
|
947
1086
|
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
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
|
+
}
|
|
951
1107
|
}
|
|
952
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
|
+
}
|
|
953
1119
|
return false
|
|
954
1120
|
}
|
|
955
1121
|
|
|
1122
|
+
func canPerformResetTransition() -> Bool {
|
|
1123
|
+
guard let bridge = self.bridge else { return false }
|
|
1124
|
+
return (bridge.viewController as? CAPBridgeViewController) != nil
|
|
1125
|
+
}
|
|
1126
|
+
|
|
956
1127
|
@objc func reset(_ call: CAPPluginCall) {
|
|
957
1128
|
let toLastSuccessful = call.getBool("toLastSuccessful") ?? false
|
|
958
|
-
|
|
1129
|
+
let usePendingBundle = call.getBool("usePendingBundle") ?? false
|
|
1130
|
+
if self._reset(toLastSuccessful: toLastSuccessful, usePendingBundle: usePendingBundle) {
|
|
959
1131
|
call.resolve()
|
|
960
1132
|
} else {
|
|
961
1133
|
logger.error("Reset failed")
|
|
@@ -1074,7 +1246,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1074
1246
|
self.persistLastFailedBundle(current)
|
|
1075
1247
|
self.implementation.sendStats(action: "update_fail", versionName: current.getVersionName())
|
|
1076
1248
|
self.implementation.setError(bundle: current)
|
|
1077
|
-
_ = self.
|
|
1249
|
+
_ = self.performReset(toLastSuccessful: true, usePendingBundle: false, isInternal: true)
|
|
1078
1250
|
if self.autoDeleteFailed && !current.isBuiltin() {
|
|
1079
1251
|
logger.info("Deleting failing bundle: \(current.toString())")
|
|
1080
1252
|
let res = self.implementation.delete(id: current.getId(), removeInfo: false)
|
|
@@ -1099,6 +1271,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1099
1271
|
self.backgroundTaskID = UIBackgroundTaskIdentifier.invalid
|
|
1100
1272
|
}
|
|
1101
1273
|
|
|
1274
|
+
private func notifyBundleSet(_ bundle: BundleInfo) {
|
|
1275
|
+
self.notifyListeners("set", data: ["bundle": bundle.toJSON()], retainUntilConsumed: true)
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1102
1278
|
func sendReadyToJs(current: BundleInfo, msg: String) {
|
|
1103
1279
|
logger.info("sendReadyToJs")
|
|
1104
1280
|
DispatchQueue.global().async {
|
|
@@ -1126,32 +1302,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1126
1302
|
private func performHideSplashscreen() {
|
|
1127
1303
|
self.cancelSplashscreenTimeout()
|
|
1128
1304
|
self.removeSplashscreenLoader()
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
self.logger.info("Splashscreen hidden automatically")
|
|
1138
|
-
}, error: { (_) in
|
|
1139
|
-
self.logger.error("Failed to auto-hide splashscreen")
|
|
1140
|
-
})
|
|
1141
|
-
|
|
1142
|
-
// Try to call the SplashScreen hide method directly through the bridge
|
|
1143
|
-
if let splashScreenPlugin = bridge.plugin(withName: "SplashScreen") {
|
|
1144
|
-
// Use runtime method invocation to call hide method
|
|
1145
|
-
let selector = NSSelectorFromString("hide:")
|
|
1146
|
-
if splashScreenPlugin.responds(to: selector) {
|
|
1147
|
-
_ = splashScreenPlugin.perform(selector, with: call)
|
|
1148
|
-
self.logger.info("Called SplashScreen hide method")
|
|
1149
|
-
} else {
|
|
1150
|
-
self.logger.warn("autoSplashscreen: SplashScreen plugin does not respond to hide: method. Make sure @capacitor/splash-screen plugin is properly installed.")
|
|
1151
|
-
}
|
|
1152
|
-
} else {
|
|
1153
|
-
self.logger.warn("autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin.")
|
|
1154
|
-
}
|
|
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
|
+
)
|
|
1155
1313
|
}
|
|
1156
1314
|
|
|
1157
1315
|
private func showSplashscreen() {
|
|
@@ -1167,35 +1325,132 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1167
1325
|
private func performShowSplashscreen() {
|
|
1168
1326
|
self.cancelSplashscreenTimeout()
|
|
1169
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
|
+
}
|
|
1170
1381
|
|
|
1171
1382
|
guard let bridge = self.bridge else {
|
|
1172
|
-
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
|
+
)
|
|
1173
1391
|
return
|
|
1174
1392
|
}
|
|
1175
1393
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
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
|
+
}
|
|
1182
1423
|
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
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)
|
|
1190
1435
|
} else {
|
|
1191
|
-
self.logger.
|
|
1436
|
+
self.logger.error(message)
|
|
1192
1437
|
}
|
|
1193
|
-
|
|
1194
|
-
self.logger.warn("autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin.")
|
|
1438
|
+
return
|
|
1195
1439
|
}
|
|
1196
1440
|
|
|
1197
|
-
self.
|
|
1198
|
-
|
|
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
|
+
}
|
|
1199
1454
|
}
|
|
1200
1455
|
|
|
1201
1456
|
private func addSplashscreenLoaderIfNeeded() {
|
|
@@ -1349,7 +1604,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1349
1604
|
}
|
|
1350
1605
|
return false
|
|
1351
1606
|
case "onLaunch":
|
|
1352
|
-
if !self.
|
|
1607
|
+
if !self.getOnLaunchDirectUpdateUsed() {
|
|
1353
1608
|
return true
|
|
1354
1609
|
}
|
|
1355
1610
|
return false
|
|
@@ -1359,6 +1614,51 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1359
1614
|
}
|
|
1360
1615
|
}
|
|
1361
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
|
+
|
|
1362
1662
|
private func notifyBreakingEvents(version: String) {
|
|
1363
1663
|
guard !version.isEmpty else {
|
|
1364
1664
|
return
|
|
@@ -1368,11 +1668,58 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1368
1668
|
self.notifyListeners("majorAvailable", data: payload)
|
|
1369
1669
|
}
|
|
1370
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
|
+
|
|
1371
1717
|
func endBackGroundTaskWithNotif(
|
|
1372
1718
|
msg: String,
|
|
1373
1719
|
latestVersionName: String,
|
|
1374
1720
|
current: BundleInfo,
|
|
1375
1721
|
error: Bool = true,
|
|
1722
|
+
plannedDirectUpdate: Bool = false,
|
|
1376
1723
|
failureAction: String = "download_fail",
|
|
1377
1724
|
failureEvent: String = "downloadFailed",
|
|
1378
1725
|
sendStats: Bool = true
|
|
@@ -1384,6 +1731,8 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1384
1731
|
downloadInProgress = false
|
|
1385
1732
|
downloadStartTime = nil
|
|
1386
1733
|
|
|
1734
|
+
self.consumeOnLaunchDirectUpdateAttempt(plannedDirectUpdate: plannedDirectUpdate)
|
|
1735
|
+
|
|
1387
1736
|
if error {
|
|
1388
1737
|
if sendStats {
|
|
1389
1738
|
self.implementation.sendStats(action: failureAction, versionName: current.getVersionName())
|
|
@@ -1418,6 +1767,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1418
1767
|
return true
|
|
1419
1768
|
}
|
|
1420
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
|
+
|
|
1421
1778
|
func backgroundDownload() {
|
|
1422
1779
|
// Set download in progress flag (thread-safe)
|
|
1423
1780
|
downloadLock.lock()
|
|
@@ -1437,7 +1794,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1437
1794
|
return
|
|
1438
1795
|
}
|
|
1439
1796
|
|
|
1440
|
-
|
|
1797
|
+
self.runBackgroundDownloadWork {
|
|
1441
1798
|
// Wait for cleanup to complete before starting download
|
|
1442
1799
|
self.waitForCleanupIfNeeded()
|
|
1443
1800
|
self.backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "Finish Download Tasks") {
|
|
@@ -1449,16 +1806,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1449
1806
|
let current = self.implementation.getCurrentBundle()
|
|
1450
1807
|
|
|
1451
1808
|
// Handle network errors and other failures first
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
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,
|
|
1459
1815
|
current: current,
|
|
1460
|
-
|
|
1461
|
-
sendStats: !responseIsOk
|
|
1816
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1462
1817
|
)
|
|
1463
1818
|
return
|
|
1464
1819
|
}
|
|
@@ -1467,26 +1822,39 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1467
1822
|
let directUpdateAllowed = plannedDirectUpdate && !self.autoSplashscreenTimedOut
|
|
1468
1823
|
if directUpdateAllowed {
|
|
1469
1824
|
self.logger.info("Direct update to builtin version")
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
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
|
+
)
|
|
1476
1833
|
} else {
|
|
1477
1834
|
if plannedDirectUpdate && !directUpdateAllowed {
|
|
1478
1835
|
self.logger.info("Direct update skipped because splashscreen timeout occurred. Update will apply later.")
|
|
1479
1836
|
}
|
|
1480
1837
|
self.logger.info("Setting next bundle to builtin")
|
|
1481
1838
|
_ = self.implementation.setNextBundle(next: BundleInfo.ID_BUILTIN)
|
|
1482
|
-
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
|
+
)
|
|
1483
1846
|
}
|
|
1484
1847
|
return
|
|
1485
1848
|
}
|
|
1486
1849
|
let sessionKey = res.sessionKey ?? ""
|
|
1487
1850
|
guard let downloadUrl = URL(string: res.url) else {
|
|
1488
1851
|
self.logger.error("Error no url or wrong format")
|
|
1489
|
-
self.endBackGroundTaskWithNotif(
|
|
1852
|
+
self.endBackGroundTaskWithNotif(
|
|
1853
|
+
msg: "Error no url or wrong format",
|
|
1854
|
+
latestVersionName: res.version,
|
|
1855
|
+
current: current,
|
|
1856
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1857
|
+
)
|
|
1490
1858
|
return
|
|
1491
1859
|
}
|
|
1492
1860
|
let latestVersionName = res.version
|
|
@@ -1504,6 +1872,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1504
1872
|
self.logger.error("Failed to delete failed bundle: \(nextImpl!.toString())")
|
|
1505
1873
|
}
|
|
1506
1874
|
}
|
|
1875
|
+
self.consumeOnLaunchDirectUpdateAttempt(plannedDirectUpdate: plannedDirectUpdate)
|
|
1507
1876
|
if res.manifest != nil {
|
|
1508
1877
|
nextImpl = try self.implementation.downloadManifest(manifest: res.manifest!, version: latestVersionName, sessionKey: sessionKey, link: res.link, comment: res.comment)
|
|
1509
1878
|
} else {
|
|
@@ -1512,12 +1881,22 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1512
1881
|
}
|
|
1513
1882
|
guard let next = nextImpl else {
|
|
1514
1883
|
self.logger.error("Error downloading file")
|
|
1515
|
-
self.endBackGroundTaskWithNotif(
|
|
1884
|
+
self.endBackGroundTaskWithNotif(
|
|
1885
|
+
msg: "Error downloading file",
|
|
1886
|
+
latestVersionName: latestVersionName,
|
|
1887
|
+
current: current,
|
|
1888
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1889
|
+
)
|
|
1516
1890
|
return
|
|
1517
1891
|
}
|
|
1518
1892
|
if next.isErrorStatus() {
|
|
1519
1893
|
self.logger.error("Latest bundle already exists and is in error state. Aborting update.")
|
|
1520
|
-
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
|
+
)
|
|
1521
1900
|
return
|
|
1522
1901
|
}
|
|
1523
1902
|
res.checksum = try CryptoCipher.decryptChecksum(checksum: res.checksum, publicKey: self.implementation.publicKey)
|
|
@@ -1531,7 +1910,12 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1531
1910
|
if !resDel {
|
|
1532
1911
|
self.logger.error("Delete failed, id \(id) doesn't exist")
|
|
1533
1912
|
}
|
|
1534
|
-
self.endBackGroundTaskWithNotif(
|
|
1913
|
+
self.endBackGroundTaskWithNotif(
|
|
1914
|
+
msg: "Error checksum",
|
|
1915
|
+
latestVersionName: latestVersionName,
|
|
1916
|
+
current: current,
|
|
1917
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1918
|
+
)
|
|
1535
1919
|
return
|
|
1536
1920
|
}
|
|
1537
1921
|
let directUpdateAllowed = plannedDirectUpdate && !self.autoSplashscreenTimedOut
|
|
@@ -1544,34 +1928,67 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1544
1928
|
}
|
|
1545
1929
|
if !delayConditionList.isEmpty {
|
|
1546
1930
|
self.logger.info("Update delayed until delay conditions met")
|
|
1547
|
-
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
|
+
)
|
|
1548
1938
|
return
|
|
1549
1939
|
}
|
|
1550
|
-
if self.
|
|
1551
|
-
self.
|
|
1552
|
-
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
|
+
)
|
|
1553
1956
|
}
|
|
1554
|
-
_ = self.implementation.set(bundle: next)
|
|
1555
|
-
_ = self._reload()
|
|
1556
|
-
self.endBackGroundTaskWithNotif(msg: "update installed", latestVersionName: latestVersionName, current: next, error: false)
|
|
1557
1957
|
} else {
|
|
1558
1958
|
if plannedDirectUpdate && !directUpdateAllowed {
|
|
1559
1959
|
self.logger.info("Direct update skipped because splashscreen timeout occurred. Update will install on next app background.")
|
|
1560
1960
|
}
|
|
1561
1961
|
self.notifyListeners("updateAvailable", data: ["bundle": next.toJSON()])
|
|
1562
1962
|
_ = self.implementation.setNextBundle(next: next.getId())
|
|
1563
|
-
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
|
+
)
|
|
1564
1970
|
}
|
|
1565
1971
|
return
|
|
1566
1972
|
} catch {
|
|
1567
1973
|
self.logger.error("Error downloading file \(error.localizedDescription)")
|
|
1568
1974
|
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
1569
|
-
self.endBackGroundTaskWithNotif(
|
|
1975
|
+
self.endBackGroundTaskWithNotif(
|
|
1976
|
+
msg: "Error downloading file",
|
|
1977
|
+
latestVersionName: latestVersionName,
|
|
1978
|
+
current: current,
|
|
1979
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
1980
|
+
)
|
|
1570
1981
|
return
|
|
1571
1982
|
}
|
|
1572
1983
|
} else {
|
|
1573
1984
|
self.logger.info("No need to update, \(current.getId()) is the latest bundle.")
|
|
1574
|
-
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
|
+
)
|
|
1575
1992
|
return
|
|
1576
1993
|
}
|
|
1577
1994
|
}
|
|
@@ -1595,6 +2012,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1595
2012
|
logger.info("Next bundle is: \(next!.toString())")
|
|
1596
2013
|
if self.implementation.set(bundle: next!) && self._reload() {
|
|
1597
2014
|
logger.info("Updated to bundle: \(next!.toString())")
|
|
2015
|
+
self.notifyBundleSet(next!)
|
|
1598
2016
|
_ = self.implementation.setNextBundle(next: Optional<String>.none)
|
|
1599
2017
|
} else {
|
|
1600
2018
|
logger.error("Update to bundle: \(next!.toString()) Failed!")
|