@capgo/capacitor-updater 8.0.0 → 8.0.1
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/CapgoCapacitorUpdater.podspec +2 -2
- package/Package.swift +35 -0
- package/README.md +667 -206
- package/android/build.gradle +16 -11
- package/android/proguard-rules.pro +28 -0
- package/android/src/main/AndroidManifest.xml +0 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +134 -194
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleStatus.java +23 -23
- package/android/src/main/java/ee/forgr/capacitor_updater/Callback.java +13 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +967 -1027
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1283 -1180
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipherV2.java +276 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DataManager.java +28 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayCondition.java +45 -48
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayUntilNext.java +4 -4
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +440 -113
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +101 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/InternalUtils.java +32 -0
- package/dist/docs.json +1316 -473
- package/dist/esm/definitions.d.ts +518 -248
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +4 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +25 -41
- package/dist/esm/web.js +67 -35
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +67 -35
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +67 -35
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/CapacitorUpdater.swift +736 -361
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +436 -136
- package/ios/Plugin/CryptoCipherV2.swift +310 -0
- package/ios/Plugin/InternalUtils.swift +258 -0
- package/package.json +33 -29
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +0 -153
- package/ios/Plugin/CapacitorUpdaterPlugin.h +0 -10
- package/ios/Plugin/CapacitorUpdaterPlugin.m +0 -27
- package/ios/Plugin/CryptoCipher.swift +0 -240
|
@@ -13,60 +13,173 @@ import Version
|
|
|
13
13
|
* here: https://capacitorjs.com/docs/plugins/ios
|
|
14
14
|
*/
|
|
15
15
|
@objc(CapacitorUpdaterPlugin)
|
|
16
|
-
public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
17
|
+
public let identifier = "CapacitorUpdaterPlugin"
|
|
18
|
+
public let jsName = "CapacitorUpdater"
|
|
19
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
20
|
+
CAPPluginMethod(name: "download", returnType: CAPPluginReturnPromise),
|
|
21
|
+
CAPPluginMethod(name: "setUpdateUrl", returnType: CAPPluginReturnPromise),
|
|
22
|
+
CAPPluginMethod(name: "setStatsUrl", returnType: CAPPluginReturnPromise),
|
|
23
|
+
CAPPluginMethod(name: "setChannelUrl", returnType: CAPPluginReturnPromise),
|
|
24
|
+
CAPPluginMethod(name: "set", returnType: CAPPluginReturnPromise),
|
|
25
|
+
CAPPluginMethod(name: "list", returnType: CAPPluginReturnPromise),
|
|
26
|
+
CAPPluginMethod(name: "delete", returnType: CAPPluginReturnPromise),
|
|
27
|
+
CAPPluginMethod(name: "reset", returnType: CAPPluginReturnPromise),
|
|
28
|
+
CAPPluginMethod(name: "current", returnType: CAPPluginReturnPromise),
|
|
29
|
+
CAPPluginMethod(name: "reload", returnType: CAPPluginReturnPromise),
|
|
30
|
+
CAPPluginMethod(name: "notifyAppReady", returnType: CAPPluginReturnPromise),
|
|
31
|
+
CAPPluginMethod(name: "setDelay", returnType: CAPPluginReturnPromise),
|
|
32
|
+
CAPPluginMethod(name: "setMultiDelay", returnType: CAPPluginReturnPromise),
|
|
33
|
+
CAPPluginMethod(name: "cancelDelay", returnType: CAPPluginReturnPromise),
|
|
34
|
+
CAPPluginMethod(name: "getLatest", returnType: CAPPluginReturnPromise),
|
|
35
|
+
CAPPluginMethod(name: "setChannel", returnType: CAPPluginReturnPromise),
|
|
36
|
+
CAPPluginMethod(name: "unsetChannel", returnType: CAPPluginReturnPromise),
|
|
37
|
+
CAPPluginMethod(name: "getChannel", returnType: CAPPluginReturnPromise),
|
|
38
|
+
CAPPluginMethod(name: "setCustomId", returnType: CAPPluginReturnPromise),
|
|
39
|
+
CAPPluginMethod(name: "getDeviceId", returnType: CAPPluginReturnPromise),
|
|
40
|
+
CAPPluginMethod(name: "getPluginVersion", returnType: CAPPluginReturnPromise),
|
|
41
|
+
CAPPluginMethod(name: "next", returnType: CAPPluginReturnPromise),
|
|
42
|
+
CAPPluginMethod(name: "isAutoUpdateEnabled", returnType: CAPPluginReturnPromise),
|
|
43
|
+
CAPPluginMethod(name: "getBuiltinVersion", returnType: CAPPluginReturnPromise),
|
|
44
|
+
CAPPluginMethod(name: "isAutoUpdateAvailable", returnType: CAPPluginReturnPromise),
|
|
45
|
+
CAPPluginMethod(name: "getNextBundle", returnType: CAPPluginReturnPromise)
|
|
46
|
+
]
|
|
47
|
+
public var implementation = CapacitorUpdater()
|
|
48
|
+
private let PLUGIN_VERSION: String = "8.0.1"
|
|
49
|
+
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
50
|
+
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
51
|
+
static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
|
|
22
52
|
let DELAY_CONDITION_PREFERENCES = ""
|
|
23
53
|
private var updateUrl = ""
|
|
24
54
|
private var statsUrl = ""
|
|
25
|
-
private var defaultPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA4pW9olT0FBXXivRCzd3xcImlWZrqkwcF2xTkX/FwXmj9eh9H\nkBLrsQmfsC+PJisRXIOGq6a0z3bsGq6jBpp3/Jr9jiaW5VuPGaKeMaZZBRvi/N5f\nIMG3hZXSOcy0IYg+E1Q7RkYO1xq5GLHseqG+PXvJsNe4R8R/Bmd/ngq0xh/cvcrH\nHpXwO0Aj9tfprlb+rHaVV79EkVRWYPidOLnK1n0EFHFJ1d/MyDIp10TEGm2xHpf/\nBrlb1an8wXEuzoC0DgYaczgTjovwR+ewSGhSHJliQdM0Qa3o1iN87DldWtydImMs\nPjJ3DUwpsjAMRe5X8Et4+udFW2ciYnQo9H0CkwIDAQABAoIBAQCtjlMV/4qBxAU4\nu0ZcWA9yywwraX0aJ3v1xrfzQYV322Wk4Ea5dbSxA5UcqCE29DA1M824t1Wxv/6z\npWbcTP9xLuresnJMtmgTE7umfiubvTONy2sENT20hgDkIwcq1CfwOEm61zjQzPhQ\nkSB5AmEsyR/BZEsUNc+ygR6AWOUFB7tj4yMc32LOTWSbE/znnF2BkmlmnQykomG1\n2oVqM3lUFP7+m8ux1O7scO6IMts+Z/eFXjWfxpbebUSvSIR83GXPQZ34S/c0ehOg\nyHdmCSOel1r3VvInMe+30j54Jr+Ml/7Ee6axiwyE2e/bd85MsK9sVdp0OtelXaqA\nOZZqWvN5AoGBAP2Hn3lSq+a8GsDH726mHJw60xM0LPbVJTYbXsmQkg1tl3NKJTMM\nQqz41+5uys+phEgLHI9gVJ0r+HaGHXnJ4zewlFjsudstb/0nfctUvTqnhEhfNo9I\ny4kufVKPRF3sMEeo7CDVJs4GNBLycEyIBy6Mbv0VcO7VaZqggRwu4no9AoGBAOTK\n6NWYs1BWlkua2wmxexGOzehNGedInp0wGr2l4FDayWjkZLqvB+nNXUQ63NdHlSs4\nWB2Z1kQXZxVaI2tPYexGUKXEo2uFob63uflbuE029ovDXIIPFTPtGNdNXwhHT5a+\nPhmy3sMc+s2BSNM5qaNmfxQxhdd6gRU6oikE+c0PAoGAMn3cKNFqIt27hkFLUgIL\nGKIuf1iYy9/PNWNmEUaVj88PpopRtkTu0nwMpROzmH/uNFriKTvKHjMvnItBO4wV\nkHW+VadvrFL0Rrqituf9d7z8/1zXBNo+juePVe3qc7oiM2NVA4Tv4YAixtM5wkQl\nCgQ15nlqsGYYTg9BJ1e/CxECgYEAjEYPzO2reuUrjr0p8F59ev1YJ0YmTJRMk0ks\nC/yIdGo/tGzbiU3JB0LfHPcN8Xu07GPGOpfYM7U5gXDbaG6qNgfCaHAQVdr/mQPi\nJQ1kCQtay8QCkscWk9iZM1//lP7LwDtxraXqSCwbZSYP9VlUNZeg8EuQqNU2EUL6\nqzWexmcCgYEA0prUGNBacraTYEknB1CsbP36UPWsqFWOvevlz+uEC5JPxPuW5ZHh\nSQN7xl6+PHyjPBM7ttwPKyhgLOVTb3K7ex/PXnudojMUK5fh7vYfChVTSlx2p6r0\nDi58PdD+node08cJH+ie0Yphp7m+D4+R9XD0v0nEvnu4BtAW6DrJasw=\n-----END RSA PRIVATE KEY-----\n"
|
|
26
55
|
private var backgroundTaskID: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier.invalid
|
|
27
56
|
private var currentVersionNative: Version = "0.0.0"
|
|
28
57
|
private var autoUpdate = false
|
|
29
58
|
private var appReadyTimeout = 10000
|
|
30
59
|
private var appReadyCheck: DispatchWorkItem?
|
|
31
60
|
private var resetWhenUpdate = true
|
|
61
|
+
private var directUpdate = false
|
|
32
62
|
private var autoDeleteFailed = false
|
|
33
63
|
private var autoDeletePrevious = false
|
|
64
|
+
private var keepUrlPathAfterReload = false
|
|
34
65
|
private var backgroundWork: DispatchWorkItem?
|
|
35
66
|
private var taskRunning = false
|
|
67
|
+
private var periodCheckDelay = 0
|
|
68
|
+
let semaphoreReady = DispatchSemaphore(value: 0)
|
|
36
69
|
|
|
37
70
|
override public func load() {
|
|
38
|
-
|
|
71
|
+
#if targetEnvironment(simulator)
|
|
72
|
+
print("\(CapacitorUpdater.TAG) ::::: SIMULATOR :::::")
|
|
73
|
+
print("\(CapacitorUpdater.TAG) Application directory: \(NSHomeDirectory())")
|
|
74
|
+
#endif
|
|
75
|
+
|
|
76
|
+
self.semaphoreUp()
|
|
77
|
+
self.implementation.deviceID = (UserDefaults.standard.string(forKey: "appUUID") ?? UUID().uuidString).lowercased()
|
|
78
|
+
UserDefaults.standard.set( self.implementation.deviceID, forKey: "appUUID")
|
|
79
|
+
UserDefaults.standard.synchronize()
|
|
80
|
+
print("\(CapacitorUpdater.TAG) init for device \(self.implementation.deviceID)")
|
|
81
|
+
guard let versionName = getConfig().getString("version", Bundle.main.versionName) else {
|
|
82
|
+
print("\(CapacitorUpdater.TAG) Cannot get version name")
|
|
83
|
+
// crash the app
|
|
84
|
+
fatalError("Cannot get version name")
|
|
85
|
+
}
|
|
39
86
|
do {
|
|
40
|
-
currentVersionNative = try Version(
|
|
87
|
+
currentVersionNative = try Version(versionName)
|
|
41
88
|
} catch {
|
|
42
|
-
print("\(
|
|
89
|
+
print("\(CapacitorUpdater.TAG) Cannot parse versionName \(versionName)")
|
|
43
90
|
}
|
|
44
|
-
print("\(
|
|
45
|
-
implementation.
|
|
91
|
+
print("\(CapacitorUpdater.TAG) version native \(self.currentVersionNative.description)")
|
|
92
|
+
implementation.versionBuild = getConfig().getString("version", Bundle.main.versionName)!
|
|
46
93
|
autoDeleteFailed = getConfig().getBoolean("autoDeleteFailed", true)
|
|
47
94
|
autoDeletePrevious = getConfig().getBoolean("autoDeletePrevious", true)
|
|
95
|
+
keepUrlPathAfterReload = getConfig().getBoolean("keepUrlPathAfterReload", false)
|
|
96
|
+
directUpdate = getConfig().getBoolean("directUpdate", false)
|
|
48
97
|
updateUrl = getConfig().getString("updateUrl", CapacitorUpdaterPlugin.updateUrlDefault)!
|
|
49
98
|
autoUpdate = getConfig().getBoolean("autoUpdate", true)
|
|
50
99
|
appReadyTimeout = getConfig().getInt("appReadyTimeout", 10000)
|
|
100
|
+
implementation.timeout = Double(getConfig().getInt("responseTimeout", 20))
|
|
51
101
|
resetWhenUpdate = getConfig().getBoolean("resetWhenUpdate", true)
|
|
102
|
+
let periodCheckDelayValue = getConfig().getInt("periodCheckDelay", 0)
|
|
103
|
+
if periodCheckDelayValue >= 0 && periodCheckDelayValue > 600 {
|
|
104
|
+
periodCheckDelay = 600
|
|
105
|
+
} else {
|
|
106
|
+
periodCheckDelay = periodCheckDelayValue
|
|
107
|
+
}
|
|
52
108
|
|
|
53
|
-
implementation.
|
|
54
|
-
implementation.
|
|
109
|
+
implementation.publicKey = getConfig().getString("publicKey", "")!
|
|
110
|
+
implementation.notifyDownloadRaw = notifyDownload
|
|
55
111
|
implementation.PLUGIN_VERSION = self.PLUGIN_VERSION
|
|
56
112
|
let config = (self.bridge?.viewController as? CAPBridgeViewController)?.instanceDescriptor().legacyConfig
|
|
57
|
-
|
|
58
|
-
|
|
113
|
+
implementation.appId = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String ?? ""
|
|
114
|
+
implementation.appId = config?["appId"] as? String ?? implementation.appId
|
|
115
|
+
implementation.appId = getConfig().getString("appId", implementation.appId)!
|
|
116
|
+
if implementation.appId == "" {
|
|
117
|
+
fatalError("appId is missing in capacitor.config.json or plugin config, and cannot be retrieved from the native app, please add it globally or in the plugin config")
|
|
59
118
|
}
|
|
119
|
+
print("\(CapacitorUpdater.TAG) appId \(implementation.appId)")
|
|
60
120
|
implementation.statsUrl = getConfig().getString("statsUrl", CapacitorUpdaterPlugin.statsUrlDefault)!
|
|
61
121
|
implementation.channelUrl = getConfig().getString("channelUrl", CapacitorUpdaterPlugin.channelUrlDefault)!
|
|
122
|
+
implementation.defaultChannel = getConfig().getString("defaultChannel", "")!
|
|
123
|
+
self.implementation.autoReset()
|
|
124
|
+
|
|
62
125
|
if resetWhenUpdate {
|
|
63
126
|
self.cleanupObsoleteVersions()
|
|
64
127
|
}
|
|
128
|
+
|
|
129
|
+
// Load the server
|
|
130
|
+
// This is very much swift specific, android does not do that
|
|
131
|
+
// In android we depend on the serverBasePath capacitor property
|
|
132
|
+
// In IOS we do not. Instead during the plugin initialization we try to call setServerBasePath
|
|
133
|
+
// The idea is to prevent having to store the bundle in 2 locations for hot reload and persisten storage
|
|
134
|
+
// According to martin it is not possible to use serverBasePath on ios in a way that allows us to store the bundle once
|
|
135
|
+
|
|
136
|
+
if !self.initialLoad() {
|
|
137
|
+
print("\(CapacitorUpdater.TAG) unable to force reload, the plugin might fallback to the builtin version")
|
|
138
|
+
}
|
|
139
|
+
|
|
65
140
|
let nc = NotificationCenter.default
|
|
66
141
|
nc.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
|
|
67
142
|
nc.addObserver(self, selector: #selector(appMovedToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
|
|
68
143
|
nc.addObserver(self, selector: #selector(appKilled), name: UIApplication.willTerminateNotification, object: nil)
|
|
69
144
|
self.appMovedToForeground()
|
|
145
|
+
self.checkForUpdateAfterDelay()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private func initialLoad() -> Bool {
|
|
149
|
+
guard let bridge = self.bridge else { return false }
|
|
150
|
+
|
|
151
|
+
let id = self.implementation.getCurrentBundleId()
|
|
152
|
+
var dest: URL
|
|
153
|
+
if BundleInfo.ID_BUILTIN == id {
|
|
154
|
+
dest = Bundle.main.resourceURL!.appendingPathComponent("public")
|
|
155
|
+
} else {
|
|
156
|
+
dest = self.implementation.getBundleDirectory(id: id)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if !FileManager.default.fileExists(atPath: dest.path) {
|
|
160
|
+
print("\(CapacitorUpdater.TAG) Initial load fail - file at path \(dest.path) doesn't exist. Defaulting to buildin!! \(id)")
|
|
161
|
+
dest = Bundle.main.resourceURL!.appendingPathComponent("public")
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
print("\(CapacitorUpdater.TAG) Initial load \(id)")
|
|
165
|
+
// We don't use the viewcontroller here as it does not work during the initial load state
|
|
166
|
+
bridge.setServerBasePath(dest.path)
|
|
167
|
+
return true
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private func semaphoreWait(waitTime: Int) {
|
|
171
|
+
print("\(CapacitorUpdater.TAG) semaphoreWait \(waitTime)")
|
|
172
|
+
_ = semaphoreReady.wait(timeout: .now() + .milliseconds(waitTime))
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private func semaphoreUp() {
|
|
176
|
+
DispatchQueue.global().async {
|
|
177
|
+
self.semaphoreWait(waitTime: 0)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private func semaphoreDown() {
|
|
182
|
+
semaphoreReady.signal()
|
|
70
183
|
}
|
|
71
184
|
|
|
72
185
|
private func cleanupObsoleteVersions() {
|
|
@@ -74,16 +187,16 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
74
187
|
do {
|
|
75
188
|
LatestVersionNative = try Version(UserDefaults.standard.string(forKey: "LatestVersionNative") ?? "0.0.0")
|
|
76
189
|
} catch {
|
|
77
|
-
print("\(
|
|
190
|
+
print("\(CapacitorUpdater.TAG) Cannot get version native \(currentVersionNative)")
|
|
78
191
|
}
|
|
79
192
|
if LatestVersionNative != "0.0.0" && self.currentVersionNative.description != LatestVersionNative.description {
|
|
80
193
|
_ = self._reset(toLastSuccessful: false)
|
|
81
194
|
let res = implementation.list()
|
|
82
195
|
res.forEach { version in
|
|
83
|
-
print("\(
|
|
196
|
+
print("\(CapacitorUpdater.TAG) Deleting obsolete bundle: \(version.getId())")
|
|
84
197
|
let res = implementation.delete(id: version.getId())
|
|
85
198
|
if !res {
|
|
86
|
-
print("\(
|
|
199
|
+
print("\(CapacitorUpdater.TAG) Delete failed, id \(version.getId()) doesn't exist")
|
|
87
200
|
}
|
|
88
201
|
}
|
|
89
202
|
}
|
|
@@ -91,17 +204,66 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
91
204
|
UserDefaults.standard.synchronize()
|
|
92
205
|
}
|
|
93
206
|
|
|
94
|
-
@objc func notifyDownload(id: String, percent: Int) {
|
|
207
|
+
@objc func notifyDownload(id: String, percent: Int, ignoreMultipleOfTen: Bool = false) {
|
|
95
208
|
let bundle = self.implementation.getBundleInfo(id: id)
|
|
96
209
|
self.notifyListeners("download", data: ["percent": percent, "bundle": bundle.toJSON()])
|
|
97
210
|
if percent == 100 {
|
|
98
211
|
self.notifyListeners("downloadComplete", data: ["bundle": bundle.toJSON()])
|
|
99
212
|
self.implementation.sendStats(action: "download_complete", versionName: bundle.getVersionName())
|
|
100
|
-
} else if percent.isMultiple(of: 10) {
|
|
213
|
+
} else if percent.isMultiple(of: 10) || ignoreMultipleOfTen {
|
|
101
214
|
self.implementation.sendStats(action: "download_\(percent)", versionName: bundle.getVersionName())
|
|
102
215
|
}
|
|
103
216
|
}
|
|
104
217
|
|
|
218
|
+
@objc func setUpdateUrl(_ call: CAPPluginCall) {
|
|
219
|
+
if !getConfig().getBoolean("allowModifyUrl", false) {
|
|
220
|
+
print("\(CapacitorUpdater.TAG) setUpdateUrl called without allowModifyUrl")
|
|
221
|
+
call.reject("setUpdateUrl called without allowModifyUrl set allowModifyUrl in your config to true to allow it")
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
guard let url = call.getString("url") else {
|
|
225
|
+
print("\(CapacitorUpdater.TAG) setUpdateUrl called without url")
|
|
226
|
+
call.reject("setUpdateUrl called without url")
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
self.updateUrl = url
|
|
230
|
+
call.resolve()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
@objc func setStatsUrl(_ call: CAPPluginCall) {
|
|
234
|
+
if !getConfig().getBoolean("allowModifyUrl", false) {
|
|
235
|
+
print("\(CapacitorUpdater.TAG) setStatsUrl called without allowModifyUrl")
|
|
236
|
+
call.reject("setStatsUrl called without allowModifyUrl set allowModifyUrl in your config to true to allow it")
|
|
237
|
+
return
|
|
238
|
+
}
|
|
239
|
+
guard let url = call.getString("url") else {
|
|
240
|
+
print("\(CapacitorUpdater.TAG) setStatsUrl called without url")
|
|
241
|
+
call.reject("setStatsUrl called without url")
|
|
242
|
+
return
|
|
243
|
+
}
|
|
244
|
+
self.statsUrl = url
|
|
245
|
+
call.resolve()
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
@objc func setChannelUrl(_ call: CAPPluginCall) {
|
|
249
|
+
if !getConfig().getBoolean("allowModifyUrl", false) {
|
|
250
|
+
print("\(CapacitorUpdater.TAG) setChannelUrl called without allowModifyUrl")
|
|
251
|
+
call.reject("setChannelUrl called without allowModifyUrl set allowModifyUrl in your config to true to allow it")
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
guard let url = call.getString("url") else {
|
|
255
|
+
print("\(CapacitorUpdater.TAG) setChannelUrl called without url")
|
|
256
|
+
call.reject("setChannelUrl called without url")
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
self.implementation.channelUrl = url
|
|
260
|
+
call.resolve()
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
@objc func getBuiltinVersion(_ call: CAPPluginCall) {
|
|
264
|
+
call.resolve(["version": implementation.versionBuild])
|
|
265
|
+
}
|
|
266
|
+
|
|
105
267
|
@objc func getDeviceId(_ call: CAPPluginCall) {
|
|
106
268
|
call.resolve(["deviceId": implementation.deviceID])
|
|
107
269
|
}
|
|
@@ -112,51 +274,82 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
112
274
|
|
|
113
275
|
@objc func download(_ call: CAPPluginCall) {
|
|
114
276
|
guard let urlString = call.getString("url") else {
|
|
115
|
-
print("\(
|
|
277
|
+
print("\(CapacitorUpdater.TAG) Download called without url")
|
|
116
278
|
call.reject("Download called without url")
|
|
117
279
|
return
|
|
118
280
|
}
|
|
119
281
|
guard let version = call.getString("version") else {
|
|
120
|
-
print("\(
|
|
282
|
+
print("\(CapacitorUpdater.TAG) Download called without version")
|
|
121
283
|
call.reject("Download called without version")
|
|
122
284
|
return
|
|
123
285
|
}
|
|
286
|
+
|
|
124
287
|
let sessionKey = call.getString("sessionKey", "")
|
|
125
|
-
|
|
288
|
+
var checksum = call.getString("checksum", "")
|
|
126
289
|
let url = URL(string: urlString)
|
|
127
|
-
print("\(
|
|
290
|
+
print("\(CapacitorUpdater.TAG) Downloading \(String(describing: url))")
|
|
128
291
|
DispatchQueue.global(qos: .background).async {
|
|
129
292
|
do {
|
|
130
293
|
let next = try self.implementation.download(url: url!, version: version, sessionKey: sessionKey)
|
|
131
|
-
|
|
132
|
-
|
|
294
|
+
checksum = try self.implementation.decryptChecksum(checksum: checksum, version: version)
|
|
295
|
+
if (checksum != "" || self.implementation.publicKey != "") && next.getChecksum() != checksum {
|
|
296
|
+
print("\(CapacitorUpdater.TAG) Error checksum", next.getChecksum(), checksum)
|
|
133
297
|
self.implementation.sendStats(action: "checksum_fail", versionName: next.getVersionName())
|
|
134
298
|
let id = next.getId()
|
|
135
299
|
let resDel = self.implementation.delete(id: id)
|
|
136
300
|
if !resDel {
|
|
137
|
-
print("\(
|
|
301
|
+
print("\(CapacitorUpdater.TAG) Delete failed, id \(id) doesn't exist")
|
|
138
302
|
}
|
|
139
303
|
throw ObjectSavableError.checksum
|
|
304
|
+
} else {
|
|
305
|
+
print("\(CapacitorUpdater.TAG) Good checksum", next.getChecksum(), checksum)
|
|
140
306
|
}
|
|
141
307
|
self.notifyListeners("updateAvailable", data: ["bundle": next.toJSON()])
|
|
142
308
|
call.resolve(next.toJSON())
|
|
143
309
|
} catch {
|
|
144
|
-
print("\(
|
|
310
|
+
print("\(CapacitorUpdater.TAG) Failed to download from: \(String(describing: url)) \(error.localizedDescription)")
|
|
145
311
|
self.notifyListeners("downloadFailed", data: ["version": version])
|
|
146
|
-
|
|
147
|
-
self.implementation.sendStats(action: "download_fail", versionName: current.getVersionName())
|
|
312
|
+
self.implementation.sendStats(action: "download_fail")
|
|
148
313
|
call.reject("Failed to download from: \(url!)", error.localizedDescription)
|
|
149
314
|
}
|
|
150
315
|
}
|
|
151
316
|
}
|
|
152
317
|
|
|
153
|
-
|
|
318
|
+
public func _reload() -> Bool {
|
|
154
319
|
guard let bridge = self.bridge else { return false }
|
|
320
|
+
self.semaphoreUp()
|
|
155
321
|
let id = self.implementation.getCurrentBundleId()
|
|
156
|
-
let
|
|
157
|
-
|
|
322
|
+
let dest: URL
|
|
323
|
+
if BundleInfo.ID_BUILTIN == id {
|
|
324
|
+
dest = Bundle.main.resourceURL!.appendingPathComponent("public")
|
|
325
|
+
} else {
|
|
326
|
+
dest = self.implementation.getBundleDirectory(id: id)
|
|
327
|
+
}
|
|
328
|
+
print("\(CapacitorUpdater.TAG) Reloading \(id)")
|
|
158
329
|
if let vc = bridge.viewController as? CAPBridgeViewController {
|
|
159
|
-
vc.
|
|
330
|
+
guard let capBridge = vc.bridge else {
|
|
331
|
+
print("\(CapacitorUpdater.TAG) Cannot get capBridge")
|
|
332
|
+
return false
|
|
333
|
+
}
|
|
334
|
+
if keepUrlPathAfterReload {
|
|
335
|
+
DispatchQueue.main.async {
|
|
336
|
+
guard let url = vc.webView?.url else {
|
|
337
|
+
print("\(CapacitorUpdater.TAG) vc.webView?.url is null?")
|
|
338
|
+
return
|
|
339
|
+
}
|
|
340
|
+
capBridge.setServerBasePath(dest.path)
|
|
341
|
+
var urlComponents = URLComponents(url: capBridge.config.serverURL, resolvingAgainstBaseURL: false)!
|
|
342
|
+
urlComponents.path = url.path
|
|
343
|
+
if let finalUrl = urlComponents.url {
|
|
344
|
+
_ = vc.webView?.load(URLRequest(url: finalUrl))
|
|
345
|
+
vc.webView?.backForwardList.perform(Selector(("_removeAllItems")))
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
} else {
|
|
349
|
+
vc.setServerBasePath(path: dest.path)
|
|
350
|
+
|
|
351
|
+
}
|
|
352
|
+
|
|
160
353
|
self.checkAppReady()
|
|
161
354
|
self.notifyListeners("appReloaded", data: [:])
|
|
162
355
|
return true
|
|
@@ -168,20 +361,20 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
168
361
|
if self._reload() {
|
|
169
362
|
call.resolve()
|
|
170
363
|
} else {
|
|
171
|
-
print("\(
|
|
364
|
+
print("\(CapacitorUpdater.TAG) Reload failed")
|
|
172
365
|
call.reject("Reload failed")
|
|
173
366
|
}
|
|
174
367
|
}
|
|
175
368
|
|
|
176
369
|
@objc func next(_ call: CAPPluginCall) {
|
|
177
370
|
guard let id = call.getString("id") else {
|
|
178
|
-
print("\(
|
|
371
|
+
print("\(CapacitorUpdater.TAG) Next called without id")
|
|
179
372
|
call.reject("Next called without id")
|
|
180
373
|
return
|
|
181
374
|
}
|
|
182
|
-
print("\(
|
|
375
|
+
print("\(CapacitorUpdater.TAG) Setting next active id \(id)")
|
|
183
376
|
if !self.implementation.setNextBundle(next: id) {
|
|
184
|
-
print("\(
|
|
377
|
+
print("\(CapacitorUpdater.TAG) Set next version failed. id \(id) does not exist.")
|
|
185
378
|
call.reject("Set next version failed. id \(id) does not exist.")
|
|
186
379
|
} else {
|
|
187
380
|
call.resolve(self.implementation.getBundleInfo(id: id).toJSON())
|
|
@@ -190,14 +383,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
190
383
|
|
|
191
384
|
@objc func set(_ call: CAPPluginCall) {
|
|
192
385
|
guard let id = call.getString("id") else {
|
|
193
|
-
print("\(
|
|
386
|
+
print("\(CapacitorUpdater.TAG) Set called without id")
|
|
194
387
|
call.reject("Set called without id")
|
|
195
388
|
return
|
|
196
389
|
}
|
|
197
390
|
let res = implementation.set(id: id)
|
|
198
|
-
print("\(
|
|
391
|
+
print("\(CapacitorUpdater.TAG) Set active bundle: \(id)")
|
|
199
392
|
if !res {
|
|
200
|
-
print("\(
|
|
393
|
+
print("\(CapacitorUpdater.TAG) Bundle successfully set to: \(id) ")
|
|
201
394
|
call.reject("Update failed, id \(id) doesn't exist")
|
|
202
395
|
} else {
|
|
203
396
|
self.reload(call)
|
|
@@ -206,7 +399,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
206
399
|
|
|
207
400
|
@objc func delete(_ call: CAPPluginCall) {
|
|
208
401
|
guard let id = call.getString("id") else {
|
|
209
|
-
print("\(
|
|
402
|
+
print("\(CapacitorUpdater.TAG) Delete called without version")
|
|
210
403
|
call.reject("Delete called without id")
|
|
211
404
|
return
|
|
212
405
|
}
|
|
@@ -214,13 +407,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
214
407
|
if res {
|
|
215
408
|
call.resolve()
|
|
216
409
|
} else {
|
|
217
|
-
print("\(
|
|
218
|
-
call.reject("Delete failed, id \(id)
|
|
410
|
+
print("\(CapacitorUpdater.TAG) Delete failed, id \(id) doesn't exist or it cannot be deleted (perhaps it is the 'next' bundle)")
|
|
411
|
+
call.reject("Delete failed, id \(id) does not exist or it cannot be deleted (perhaps it is the 'next' bundle)")
|
|
219
412
|
}
|
|
220
413
|
}
|
|
221
414
|
|
|
222
415
|
@objc func list(_ call: CAPPluginCall) {
|
|
223
|
-
let
|
|
416
|
+
let raw = call.getBool("raw", false)
|
|
417
|
+
let res = implementation.list(raw: raw)
|
|
224
418
|
var resArr: [[String: String]] = []
|
|
225
419
|
for v in res {
|
|
226
420
|
resArr.append(v.toJSON())
|
|
@@ -231,27 +425,51 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
231
425
|
}
|
|
232
426
|
|
|
233
427
|
@objc func getLatest(_ call: CAPPluginCall) {
|
|
428
|
+
let channel = call.getString("channel")
|
|
234
429
|
DispatchQueue.global(qos: .background).async {
|
|
235
|
-
let res = self.implementation.getLatest(url: URL(string: self.updateUrl)
|
|
430
|
+
let res = self.implementation.getLatest(url: URL(string: self.updateUrl)!, channel: channel)
|
|
236
431
|
if res.error != nil {
|
|
237
432
|
call.reject( res.error!)
|
|
433
|
+
} else if res.message != nil {
|
|
434
|
+
call.reject( res.message!)
|
|
238
435
|
} else {
|
|
239
436
|
call.resolve(res.toDict())
|
|
240
437
|
}
|
|
241
438
|
}
|
|
242
439
|
}
|
|
243
440
|
|
|
441
|
+
@objc func unsetChannel(_ call: CAPPluginCall) {
|
|
442
|
+
let triggerAutoUpdate = call.getBool("triggerAutoUpdate", false)
|
|
443
|
+
DispatchQueue.global(qos: .background).async {
|
|
444
|
+
let res = self.implementation.unsetChannel()
|
|
445
|
+
if res.error != "" {
|
|
446
|
+
call.reject(res.error)
|
|
447
|
+
} else {
|
|
448
|
+
if self._isAutoUpdateEnabled() && triggerAutoUpdate {
|
|
449
|
+
print("\(CapacitorUpdater.TAG) Calling autoupdater after channel change!")
|
|
450
|
+
self.backgroundDownload()
|
|
451
|
+
}
|
|
452
|
+
call.resolve(res.toDict())
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
244
457
|
@objc func setChannel(_ call: CAPPluginCall) {
|
|
245
458
|
guard let channel = call.getString("channel") else {
|
|
246
|
-
print("\(
|
|
459
|
+
print("\(CapacitorUpdater.TAG) setChannel called without channel")
|
|
247
460
|
call.reject("setChannel called without channel")
|
|
248
461
|
return
|
|
249
462
|
}
|
|
463
|
+
let triggerAutoUpdate = call.getBool("triggerAutoUpdate") ?? false
|
|
250
464
|
DispatchQueue.global(qos: .background).async {
|
|
251
465
|
let res = self.implementation.setChannel(channel: channel)
|
|
252
466
|
if res.error != "" {
|
|
253
467
|
call.reject(res.error)
|
|
254
468
|
} else {
|
|
469
|
+
if self._isAutoUpdateEnabled() && triggerAutoUpdate {
|
|
470
|
+
print("\(CapacitorUpdater.TAG) Calling autoupdater after channel change!")
|
|
471
|
+
self.backgroundDownload()
|
|
472
|
+
}
|
|
255
473
|
call.resolve(res.toDict())
|
|
256
474
|
}
|
|
257
475
|
}
|
|
@@ -269,7 +487,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
269
487
|
}
|
|
270
488
|
@objc func setCustomId(_ call: CAPPluginCall) {
|
|
271
489
|
guard let customId = call.getString("customId") else {
|
|
272
|
-
print("\(
|
|
490
|
+
print("\(CapacitorUpdater.TAG) setCustomId called without customId")
|
|
273
491
|
call.reject("setCustomId called without customId")
|
|
274
492
|
return
|
|
275
493
|
}
|
|
@@ -285,11 +503,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
285
503
|
// If developer wants to reset to the last successful bundle, and that bundle is not
|
|
286
504
|
// the built-in bundle, set it as the bundle to use and reload.
|
|
287
505
|
if toLastSuccessful && !fallback.isBuiltin() {
|
|
288
|
-
print("\(
|
|
506
|
+
print("\(CapacitorUpdater.TAG) Resetting to: \(fallback.toString())")
|
|
289
507
|
return self.implementation.set(bundle: fallback) && self._reload()
|
|
290
508
|
}
|
|
291
509
|
|
|
292
|
-
print("\(
|
|
510
|
+
print("\(CapacitorUpdater.TAG) Resetting to builtin version")
|
|
293
511
|
|
|
294
512
|
// Otherwise, reset back to the built-in bundle and reload.
|
|
295
513
|
self.implementation.reset()
|
|
@@ -304,8 +522,8 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
304
522
|
if self._reset(toLastSuccessful: toLastSuccessful) {
|
|
305
523
|
call.resolve()
|
|
306
524
|
} else {
|
|
307
|
-
print("\(
|
|
308
|
-
call.reject("\(
|
|
525
|
+
print("\(CapacitorUpdater.TAG) Reset failed")
|
|
526
|
+
call.reject("\(CapacitorUpdater.TAG) Reset failed")
|
|
309
527
|
}
|
|
310
528
|
}
|
|
311
529
|
|
|
@@ -318,15 +536,16 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
318
536
|
}
|
|
319
537
|
|
|
320
538
|
@objc func notifyAppReady(_ call: CAPPluginCall) {
|
|
321
|
-
|
|
322
|
-
self.implementation.
|
|
323
|
-
|
|
324
|
-
|
|
539
|
+
self.semaphoreDown()
|
|
540
|
+
let bundle = self.implementation.getCurrentBundle()
|
|
541
|
+
self.implementation.setSuccess(bundle: bundle, autoDeletePrevious: self.autoDeletePrevious)
|
|
542
|
+
print("\(CapacitorUpdater.TAG) Current bundle loaded successfully. ['notifyAppReady()' was called] \(bundle.toString())")
|
|
543
|
+
call.resolve(["bundle": bundle.toJSON()])
|
|
325
544
|
}
|
|
326
545
|
|
|
327
546
|
@objc func setMultiDelay(_ call: CAPPluginCall) {
|
|
328
547
|
guard let delayConditionList = call.getValue("delayConditions") else {
|
|
329
|
-
print("\(
|
|
548
|
+
print("\(CapacitorUpdater.TAG) setMultiDelay called without delayCondition")
|
|
330
549
|
call.reject("setMultiDelay called without delayCondition")
|
|
331
550
|
return
|
|
332
551
|
}
|
|
@@ -342,16 +561,16 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
342
561
|
if delayConditions != nil && "" != delayConditions {
|
|
343
562
|
UserDefaults.standard.set(delayConditions, forKey: DELAY_CONDITION_PREFERENCES)
|
|
344
563
|
UserDefaults.standard.synchronize()
|
|
345
|
-
print("\(
|
|
564
|
+
print("\(CapacitorUpdater.TAG) Delay update saved.")
|
|
346
565
|
return true
|
|
347
566
|
} else {
|
|
348
|
-
print("\(
|
|
567
|
+
print("\(CapacitorUpdater.TAG) Failed to delay update, [Error calling '_setMultiDelay()']")
|
|
349
568
|
return false
|
|
350
569
|
}
|
|
351
570
|
}
|
|
352
571
|
|
|
353
572
|
private func _cancelDelay(source: String) {
|
|
354
|
-
print("\(
|
|
573
|
+
print("\(CapacitorUpdater.TAG) delay Canceled from \(source)")
|
|
355
574
|
UserDefaults.standard.removeObject(forKey: DELAY_CONDITION_PREFERENCES)
|
|
356
575
|
UserDefaults.standard.synchronize()
|
|
357
576
|
}
|
|
@@ -377,14 +596,12 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
377
596
|
if !killed {
|
|
378
597
|
self._cancelDelay(source: "background check")
|
|
379
598
|
}
|
|
380
|
-
break
|
|
381
599
|
case "kill":
|
|
382
600
|
if killed {
|
|
383
601
|
self._cancelDelay(source: "kill check")
|
|
384
602
|
// instant install for kill action
|
|
385
603
|
self.installNext()
|
|
386
604
|
}
|
|
387
|
-
break
|
|
388
605
|
case "date":
|
|
389
606
|
if value != nil && value != "" {
|
|
390
607
|
let dateFormatter = ISO8601DateFormatter()
|
|
@@ -399,7 +616,6 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
399
616
|
} else {
|
|
400
617
|
self._cancelDelay(source: "delayVal absent")
|
|
401
618
|
}
|
|
402
|
-
break
|
|
403
619
|
case "nativeVersion":
|
|
404
620
|
if value != nil && value != "" {
|
|
405
621
|
do {
|
|
@@ -413,11 +629,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
413
629
|
} else {
|
|
414
630
|
self._cancelDelay(source: "delayVal absent")
|
|
415
631
|
}
|
|
416
|
-
break
|
|
417
632
|
case .none:
|
|
418
|
-
print("\(
|
|
633
|
+
print("\(CapacitorUpdater.TAG) _checkCancelDelay switch case none error")
|
|
419
634
|
case .some:
|
|
420
|
-
print("\(
|
|
635
|
+
print("\(CapacitorUpdater.TAG) _checkCancelDelay switch case some error")
|
|
421
636
|
}
|
|
422
637
|
}
|
|
423
638
|
}
|
|
@@ -427,7 +642,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
427
642
|
private func _isAutoUpdateEnabled() -> Bool {
|
|
428
643
|
let instanceDescriptor = (self.bridge?.viewController as? CAPBridgeViewController)?.instanceDescriptor()
|
|
429
644
|
if instanceDescriptor?.serverURL != nil {
|
|
430
|
-
print("⚠️ \(
|
|
645
|
+
print("⚠️ \(CapacitorUpdater.TAG) AutoUpdate is automatic disabled when serverUrl is set.")
|
|
431
646
|
}
|
|
432
647
|
return self.autoUpdate && self.updateUrl != "" && instanceDescriptor?.serverURL == nil
|
|
433
648
|
}
|
|
@@ -438,12 +653,20 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
438
653
|
])
|
|
439
654
|
}
|
|
440
655
|
|
|
656
|
+
@objc func isAutoUpdateAvailable(_ call: CAPPluginCall) {
|
|
657
|
+
let instanceDescriptor = (self.bridge?.viewController as? CAPBridgeViewController)?.instanceDescriptor()
|
|
658
|
+
let isAvailable = instanceDescriptor?.serverURL == nil
|
|
659
|
+
call.resolve([
|
|
660
|
+
"available": isAvailable
|
|
661
|
+
])
|
|
662
|
+
}
|
|
663
|
+
|
|
441
664
|
func checkAppReady() {
|
|
442
665
|
self.appReadyCheck?.cancel()
|
|
443
666
|
self.appReadyCheck = DispatchWorkItem(block: {
|
|
444
667
|
self.DeferredNotifyAppReadyCheck()
|
|
445
668
|
})
|
|
446
|
-
print("\(
|
|
669
|
+
print("\(CapacitorUpdater.TAG) Wait for \(self.appReadyTimeout) ms, then check for notifyAppReady")
|
|
447
670
|
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(self.appReadyTimeout), execute: self.appReadyCheck!)
|
|
448
671
|
}
|
|
449
672
|
|
|
@@ -451,15 +674,15 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
451
674
|
// Automatically roll back to fallback version if notifyAppReady has not been called yet
|
|
452
675
|
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
453
676
|
if current.isBuiltin() {
|
|
454
|
-
print("\(
|
|
677
|
+
print("\(CapacitorUpdater.TAG) Built-in bundle is active. We skip the check for notifyAppReady.")
|
|
455
678
|
return
|
|
456
679
|
}
|
|
457
680
|
|
|
458
|
-
print("\(
|
|
681
|
+
print("\(CapacitorUpdater.TAG) Current bundle is: \(current.toString())")
|
|
459
682
|
|
|
460
683
|
if BundleStatus.SUCCESS.localizedString != current.getStatus() {
|
|
461
|
-
print("\(
|
|
462
|
-
print("\(
|
|
684
|
+
print("\(CapacitorUpdater.TAG) notifyAppReady was not called, roll back current bundle: \(current.toString())")
|
|
685
|
+
print("\(CapacitorUpdater.TAG) Did you forget to call 'notifyAppReady()' in your Capacitor App code?")
|
|
463
686
|
self.notifyListeners("updateFailed", data: [
|
|
464
687
|
"bundle": current.toJSON()
|
|
465
688
|
])
|
|
@@ -467,16 +690,16 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
467
690
|
self.implementation.setError(bundle: current)
|
|
468
691
|
_ = self._reset(toLastSuccessful: true)
|
|
469
692
|
if self.autoDeleteFailed && !current.isBuiltin() {
|
|
470
|
-
print("\(
|
|
693
|
+
print("\(CapacitorUpdater.TAG) Deleting failing bundle: \(current.toString())")
|
|
471
694
|
let res = self.implementation.delete(id: current.getId(), removeInfo: false)
|
|
472
695
|
if !res {
|
|
473
|
-
print("\(
|
|
696
|
+
print("\(CapacitorUpdater.TAG) Delete version deleted: \(current.toString())")
|
|
474
697
|
} else {
|
|
475
|
-
print("\(
|
|
698
|
+
print("\(CapacitorUpdater.TAG) Failed to delete failed bundle: \(current.toString())")
|
|
476
699
|
}
|
|
477
700
|
}
|
|
478
701
|
} else {
|
|
479
|
-
print("\(
|
|
702
|
+
print("\(CapacitorUpdater.TAG) notifyAppReady was called. This is fine: \(current.toString())")
|
|
480
703
|
}
|
|
481
704
|
}
|
|
482
705
|
|
|
@@ -490,94 +713,137 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
490
713
|
self.backgroundTaskID = UIBackgroundTaskIdentifier.invalid
|
|
491
714
|
}
|
|
492
715
|
|
|
716
|
+
func sendReadyToJs(current: BundleInfo, msg: String) {
|
|
717
|
+
print("\(CapacitorUpdater.TAG) sendReadyToJs")
|
|
718
|
+
DispatchQueue.global().async {
|
|
719
|
+
self.semaphoreWait(waitTime: self.appReadyTimeout)
|
|
720
|
+
self.notifyListeners("appReady", data: ["bundle": current.toJSON(), "status": msg])
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
func endBackGroundTaskWithNotif(msg: String, latestVersionName: String, current: BundleInfo, error: Bool = true) {
|
|
725
|
+
if error {
|
|
726
|
+
self.implementation.sendStats(action: "download_fail", versionName: current.getVersionName())
|
|
727
|
+
self.notifyListeners("downloadFailed", data: ["version": latestVersionName])
|
|
728
|
+
}
|
|
729
|
+
self.notifyListeners("noNeedUpdate", data: ["bundle": current.toJSON()])
|
|
730
|
+
self.sendReadyToJs(current: current, msg: msg)
|
|
731
|
+
print("\(CapacitorUpdater.TAG) endBackGroundTaskWithNotif \(msg) current: \(current.getVersionName()) latestVersionName: \(latestVersionName)")
|
|
732
|
+
self.endBackGroundTask()
|
|
733
|
+
}
|
|
734
|
+
|
|
493
735
|
func backgroundDownload() {
|
|
736
|
+
let messageUpdate = self.directUpdate ? "Update will occur now." : "Update will occur next time app moves to background."
|
|
737
|
+
guard let url = URL(string: self.updateUrl) else {
|
|
738
|
+
print("\(CapacitorUpdater.TAG) Error no url or wrong format")
|
|
739
|
+
return
|
|
740
|
+
}
|
|
494
741
|
DispatchQueue.global(qos: .background).async {
|
|
495
742
|
self.backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "Finish Download Tasks") {
|
|
496
743
|
// End the task if time expires.
|
|
497
744
|
self.endBackGroundTask()
|
|
498
745
|
}
|
|
499
|
-
print("\(
|
|
500
|
-
let
|
|
501
|
-
let res = self.implementation.getLatest(url: url)
|
|
746
|
+
print("\(CapacitorUpdater.TAG) Check for update via \(self.updateUrl)")
|
|
747
|
+
let res = self.implementation.getLatest(url: url, channel: nil)
|
|
502
748
|
let current = self.implementation.getCurrentBundle()
|
|
503
749
|
|
|
504
750
|
if (res.message) != nil {
|
|
505
|
-
print("\(
|
|
751
|
+
print("\(CapacitorUpdater.TAG) API message: \(res.message ?? "")")
|
|
506
752
|
if res.major == true {
|
|
507
753
|
self.notifyListeners("majorAvailable", data: ["version": res.version])
|
|
508
754
|
}
|
|
509
|
-
self.
|
|
510
|
-
|
|
755
|
+
self.endBackGroundTaskWithNotif(msg: res.message ?? "", latestVersionName: res.version, current: current, error: true)
|
|
756
|
+
return
|
|
757
|
+
}
|
|
758
|
+
if res.version == "builtin" {
|
|
759
|
+
print("\(CapacitorUpdater.TAG) Latest version is builtin")
|
|
760
|
+
if self.directUpdate {
|
|
761
|
+
print("\(CapacitorUpdater.TAG) Direct update to builtin version")
|
|
762
|
+
_ = self._reset(toLastSuccessful: false)
|
|
763
|
+
self.endBackGroundTaskWithNotif(msg: "Updated to builtin version", latestVersionName: res.version, current: self.implementation.getCurrentBundle(), error: false)
|
|
764
|
+
} else {
|
|
765
|
+
print("\(CapacitorUpdater.TAG) Setting next bundle to builtin")
|
|
766
|
+
_ = self.implementation.setNextBundle(next: BundleInfo.ID_BUILTIN)
|
|
767
|
+
self.endBackGroundTaskWithNotif(msg: "Next update will be to builtin version", latestVersionName: res.version, current: current, error: false)
|
|
768
|
+
}
|
|
511
769
|
return
|
|
512
770
|
}
|
|
513
771
|
let sessionKey = res.sessionKey ?? ""
|
|
514
772
|
guard let downloadUrl = URL(string: res.url) else {
|
|
515
|
-
print("\(
|
|
516
|
-
self.
|
|
517
|
-
self.endBackGroundTask()
|
|
773
|
+
print("\(CapacitorUpdater.TAG) Error no url or wrong format")
|
|
774
|
+
self.endBackGroundTaskWithNotif(msg: "Error no url or wrong format", latestVersionName: res.version, current: current)
|
|
518
775
|
return
|
|
519
776
|
}
|
|
520
777
|
let latestVersionName = res.version
|
|
521
778
|
if latestVersionName != "" && current.getVersionName() != latestVersionName {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
779
|
+
do {
|
|
780
|
+
print("\(CapacitorUpdater.TAG) New bundle: \(latestVersionName) found. Current is: \(current.getVersionName()). \(messageUpdate)")
|
|
781
|
+
var nextImpl = self.implementation.getBundleInfoByVersionName(version: latestVersionName)
|
|
782
|
+
if nextImpl == nil || nextImpl?.isDeleted() == true {
|
|
783
|
+
if nextImpl?.isDeleted() == true {
|
|
784
|
+
print("\(CapacitorUpdater.TAG) Latest bundle already exists and will be deleted, download will overwrite it.")
|
|
785
|
+
let res = self.implementation.delete(id: nextImpl!.getId(), removeInfo: true)
|
|
786
|
+
if res {
|
|
787
|
+
print("\(CapacitorUpdater.TAG) Failed bundle deleted: \(nextImpl!.toString())")
|
|
788
|
+
} else {
|
|
789
|
+
print("\(CapacitorUpdater.TAG) Failed to delete failed bundle: \(nextImpl!.toString())")
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
if res.manifest != nil {
|
|
793
|
+
nextImpl = try self.implementation.downloadManifest(manifest: res.manifest!, version: latestVersionName, sessionKey: sessionKey)
|
|
794
|
+
} else {
|
|
795
|
+
nextImpl = try self.implementation.download(url: downloadUrl, version: latestVersionName, sessionKey: sessionKey)
|
|
796
|
+
}
|
|
529
797
|
}
|
|
530
|
-
|
|
531
|
-
print("\(
|
|
532
|
-
self.
|
|
533
|
-
_ = self.implementation.setNextBundle(next: latest!.getId())
|
|
534
|
-
self.endBackGroundTask()
|
|
798
|
+
guard let next = nextImpl else {
|
|
799
|
+
print("\(CapacitorUpdater.TAG) Error downloading file")
|
|
800
|
+
self.endBackGroundTaskWithNotif(msg: "Error downloading file", latestVersionName: latestVersionName, current: current)
|
|
535
801
|
return
|
|
536
802
|
}
|
|
537
|
-
if
|
|
538
|
-
print("\(
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
print("\(self.implementation.TAG) Delete version deleted: \(latest!.toString())")
|
|
542
|
-
} else {
|
|
543
|
-
print("\(self.implementation.TAG) Failed to delete failed bundle: \(latest!.toString())")
|
|
544
|
-
}
|
|
803
|
+
if next.isErrorStatus() {
|
|
804
|
+
print("\(CapacitorUpdater.TAG) Latest bundle already exists and is in error state. Aborting update.")
|
|
805
|
+
self.endBackGroundTaskWithNotif(msg: "Latest version is in error state. Aborting update.", latestVersionName: latestVersionName, current: current)
|
|
806
|
+
return
|
|
545
807
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
print("\(self.implementation.TAG) New bundle: \(latestVersionName) found. Current is: \(current.getVersionName()). Update will occur next time app moves to background.")
|
|
550
|
-
let next = try self.implementation.download(url: downloadUrl, version: latestVersionName, sessionKey: sessionKey)
|
|
551
|
-
if res.checksum != "" && next.getChecksum() != res.checksum {
|
|
552
|
-
print("\(self.implementation.TAG) Error checksum", next.getChecksum(), res.checksum)
|
|
808
|
+
res.checksum = try self.implementation.decryptChecksum(checksum: res.checksum, version: latestVersionName)
|
|
809
|
+
if res.checksum != "" && next.getChecksum() != res.checksum && res.manifest == nil {
|
|
810
|
+
print("\(CapacitorUpdater.TAG) Error checksum", next.getChecksum(), res.checksum)
|
|
553
811
|
self.implementation.sendStats(action: "checksum_fail", versionName: next.getVersionName())
|
|
554
812
|
let id = next.getId()
|
|
555
813
|
let resDel = self.implementation.delete(id: id)
|
|
556
814
|
if !resDel {
|
|
557
|
-
print("\(
|
|
815
|
+
print("\(CapacitorUpdater.TAG) Delete failed, id \(id) doesn't exist")
|
|
558
816
|
}
|
|
559
|
-
self.
|
|
560
|
-
|
|
817
|
+
self.endBackGroundTaskWithNotif(msg: "Error checksum", latestVersionName: latestVersionName, current: current)
|
|
818
|
+
return
|
|
819
|
+
}
|
|
820
|
+
if self.directUpdate {
|
|
821
|
+
_ = self.implementation.set(bundle: next)
|
|
822
|
+
_ = self._reload()
|
|
823
|
+
self.directUpdate = false
|
|
824
|
+
self.endBackGroundTaskWithNotif(msg: "update installed", latestVersionName: latestVersionName, current: current, error: false)
|
|
825
|
+
} else {
|
|
826
|
+
self.notifyListeners("updateAvailable", data: ["bundle": next.toJSON()])
|
|
827
|
+
_ = self.implementation.setNextBundle(next: next.getId())
|
|
828
|
+
self.endBackGroundTaskWithNotif(msg: "update downloaded, will install next background", latestVersionName: latestVersionName, current: current, error: false)
|
|
561
829
|
}
|
|
562
|
-
|
|
563
|
-
_ = self.implementation.setNextBundle(next: next.getId())
|
|
830
|
+
return
|
|
564
831
|
} catch {
|
|
565
|
-
print("\(
|
|
832
|
+
print("\(CapacitorUpdater.TAG) Error downloading file", error.localizedDescription)
|
|
566
833
|
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
567
|
-
self.
|
|
568
|
-
|
|
569
|
-
self.notifyListeners("noNeedUpdate", data: ["bundle": current.toJSON()])
|
|
834
|
+
self.endBackGroundTaskWithNotif(msg: "Error downloading file", latestVersionName: latestVersionName, current: current)
|
|
835
|
+
return
|
|
570
836
|
}
|
|
571
837
|
} else {
|
|
572
|
-
print("\(
|
|
573
|
-
self.
|
|
838
|
+
print("\(CapacitorUpdater.TAG) No need to update, \(current.getId()) is the latest bundle.")
|
|
839
|
+
self.endBackGroundTaskWithNotif(msg: "No need to update, \(current.getId()) is the latest bundle.", latestVersionName: latestVersionName, current: current, error: false)
|
|
840
|
+
return
|
|
574
841
|
}
|
|
575
|
-
self.endBackGroundTask()
|
|
576
842
|
}
|
|
577
843
|
}
|
|
578
844
|
|
|
579
845
|
@objc func appKilled() {
|
|
580
|
-
print("\(
|
|
846
|
+
print("\(CapacitorUpdater.TAG) onActivityDestroyed: all activity destroyed")
|
|
581
847
|
self._checkCancelDelay(killed: true)
|
|
582
848
|
}
|
|
583
849
|
|
|
@@ -589,19 +855,19 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
589
855
|
return DelayCondition(kind: kind, value: value)
|
|
590
856
|
}
|
|
591
857
|
if delayConditionList != nil && delayConditionList?.capacity != 0 {
|
|
592
|
-
print("\(
|
|
858
|
+
print("\(CapacitorUpdater.TAG) Update delayed until delay conditions met")
|
|
593
859
|
return
|
|
594
860
|
}
|
|
595
861
|
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
596
862
|
let next: BundleInfo? = self.implementation.getNextBundle()
|
|
597
863
|
|
|
598
864
|
if next != nil && !next!.isErrorStatus() && next!.getVersionName() != current.getVersionName() {
|
|
599
|
-
print("\(
|
|
865
|
+
print("\(CapacitorUpdater.TAG) Next bundle is: \(next!.toString())")
|
|
600
866
|
if self.implementation.set(bundle: next!) && self._reload() {
|
|
601
|
-
print("\(
|
|
867
|
+
print("\(CapacitorUpdater.TAG) Updated to bundle: \(next!.toString())")
|
|
602
868
|
_ = self.implementation.setNextBundle(next: Optional<String>.none)
|
|
603
869
|
} else {
|
|
604
|
-
print("\(
|
|
870
|
+
print("\(CapacitorUpdater.TAG) Update to bundle: \(next!.toString()) Failed!")
|
|
605
871
|
}
|
|
606
872
|
}
|
|
607
873
|
}
|
|
@@ -614,7 +880,9 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
614
880
|
}
|
|
615
881
|
|
|
616
882
|
@objc private func fromJsonArr(json: String) -> [NSObject] {
|
|
617
|
-
let jsonData = json.data(using: .utf8)
|
|
883
|
+
guard let jsonData = json.data(using: .utf8) else {
|
|
884
|
+
return []
|
|
885
|
+
}
|
|
618
886
|
let object = try? JSONSerialization.jsonObject(
|
|
619
887
|
with: jsonData,
|
|
620
888
|
options: .mutableContainers
|
|
@@ -627,20 +895,42 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
627
895
|
self.implementation.sendStats(action: "app_moved_to_foreground", versionName: current.getVersionName())
|
|
628
896
|
if backgroundWork != nil && taskRunning {
|
|
629
897
|
backgroundWork!.cancel()
|
|
630
|
-
print("\(
|
|
898
|
+
print("\(CapacitorUpdater.TAG) Background Timer Task canceled, Activity resumed before timer completes")
|
|
631
899
|
}
|
|
632
900
|
if self._isAutoUpdateEnabled() {
|
|
633
901
|
self.backgroundDownload()
|
|
634
902
|
} else {
|
|
635
|
-
print("\(
|
|
903
|
+
print("\(CapacitorUpdater.TAG) Auto update is disabled")
|
|
904
|
+
self.sendReadyToJs(current: current, msg: "disabled")
|
|
636
905
|
}
|
|
637
906
|
self.checkAppReady()
|
|
638
907
|
}
|
|
639
908
|
|
|
909
|
+
@objc func checkForUpdateAfterDelay() {
|
|
910
|
+
if periodCheckDelay == 0 || !self._isAutoUpdateEnabled() {
|
|
911
|
+
return
|
|
912
|
+
}
|
|
913
|
+
guard let url = URL(string: self.updateUrl) else {
|
|
914
|
+
print("\(CapacitorUpdater.TAG) Error no url or wrong format")
|
|
915
|
+
return
|
|
916
|
+
}
|
|
917
|
+
let timer = Timer.scheduledTimer(withTimeInterval: TimeInterval(periodCheckDelay), repeats: true) { _ in
|
|
918
|
+
DispatchQueue.global(qos: .background).async {
|
|
919
|
+
let res = self.implementation.getLatest(url: url, channel: nil)
|
|
920
|
+
let current = self.implementation.getCurrentBundle()
|
|
921
|
+
|
|
922
|
+
if res.version != current.getVersionName() {
|
|
923
|
+
print("\(CapacitorUpdater.TAG) New version found: \(res.version)")
|
|
924
|
+
self.backgroundDownload()
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
RunLoop.current.add(timer, forMode: .default)
|
|
929
|
+
}
|
|
930
|
+
|
|
640
931
|
@objc func appMovedToBackground() {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
print("\(self.implementation.TAG) Check for pending update")
|
|
932
|
+
self.implementation.sendStats(action: "app_moved_to_background")
|
|
933
|
+
print("\(CapacitorUpdater.TAG) Check for pending update")
|
|
644
934
|
let delayUpdatePreferences = UserDefaults.standard.string(forKey: DELAY_CONDITION_PREFERENCES) ?? "[]"
|
|
645
935
|
|
|
646
936
|
let delayConditionList: [DelayCondition] = fromJsonArr(json: delayUpdatePreferences).map { obj -> DelayCondition in
|
|
@@ -672,4 +962,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
672
962
|
}
|
|
673
963
|
|
|
674
964
|
}
|
|
965
|
+
|
|
966
|
+
@objc func getNextBundle(_ call: CAPPluginCall) {
|
|
967
|
+
let bundle = self.implementation.getNextBundle()
|
|
968
|
+
if bundle == nil || bundle?.isUnknown() == true {
|
|
969
|
+
call.resolve()
|
|
970
|
+
return
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
call.resolve(bundle!.toJSON())
|
|
974
|
+
}
|
|
675
975
|
}
|