@capgo/capacitor-updater 5.0.0-alpha.0 → 7.0.0
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 +1 -1
- package/LICENCE +373 -661
- package/README.md +339 -75
- package/android/build.gradle +13 -12
- package/android/src/main/AndroidManifest.xml +4 -2
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +205 -121
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleStatus.java +32 -24
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +1041 -441
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1217 -536
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +153 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayCondition.java +62 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayUntilNext.java +14 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +126 -0
- package/dist/docs.json +727 -171
- package/dist/esm/definitions.d.ts +234 -45
- package/dist/esm/definitions.js +5 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +9 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +12 -6
- package/dist/esm/web.js +64 -20
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +70 -23
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +70 -23
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/BundleInfo.swift +38 -19
- package/ios/Plugin/BundleStatus.swift +11 -4
- package/ios/Plugin/CapacitorUpdater.swift +520 -192
- package/ios/Plugin/CapacitorUpdaterPlugin.m +8 -1
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +447 -190
- package/ios/Plugin/CryptoCipher.swift +240 -0
- package/ios/Plugin/DelayCondition.swift +74 -0
- package/ios/Plugin/DelayUntilNext.swift +30 -0
- package/ios/Plugin/UserDefaultsExtension.swift +48 -0
- package/package.json +26 -20
- package/ios/Plugin/ObjectPreferences.swift +0 -97
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import Foundation
|
|
2
8
|
import Capacitor
|
|
3
9
|
import Version
|
|
@@ -9,11 +15,15 @@ import Version
|
|
|
9
15
|
@objc(CapacitorUpdaterPlugin)
|
|
10
16
|
public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
11
17
|
private var implementation = CapacitorUpdater()
|
|
12
|
-
|
|
13
|
-
static let
|
|
14
|
-
static let
|
|
15
|
-
|
|
18
|
+
private let PLUGIN_VERSION: String = "7.0.0"
|
|
19
|
+
static let updateUrlDefault = "https://api.capgo.app/updates"
|
|
20
|
+
static let statsUrlDefault = "https://api.capgo.app/stats"
|
|
21
|
+
static let channelUrlDefault = "https://api.capgo.app/channel_self"
|
|
22
|
+
let DELAY_CONDITION_PREFERENCES = ""
|
|
23
|
+
private var updateUrl = ""
|
|
16
24
|
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
|
+
private var backgroundTaskID: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier.invalid
|
|
17
27
|
private var currentVersionNative: Version = "0.0.0"
|
|
18
28
|
private var autoUpdate = false
|
|
19
29
|
private var appReadyTimeout = 10000
|
|
@@ -21,34 +31,41 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
21
31
|
private var resetWhenUpdate = true
|
|
22
32
|
private var autoDeleteFailed = false
|
|
23
33
|
private var autoDeletePrevious = false
|
|
24
|
-
|
|
34
|
+
private var backgroundWork: DispatchWorkItem?
|
|
35
|
+
private var taskRunning = false
|
|
36
|
+
|
|
25
37
|
override public func load() {
|
|
38
|
+
print("\(self.implementation.TAG) init for device \(self.implementation.deviceID)")
|
|
26
39
|
do {
|
|
27
|
-
currentVersionNative = try Version(Bundle.main.
|
|
40
|
+
currentVersionNative = try Version(getConfig().getString("version", Bundle.main.versionName ?? "0.0.0")!)
|
|
28
41
|
} catch {
|
|
29
42
|
print("\(self.implementation.TAG) Cannot get version native \(currentVersionNative)")
|
|
30
43
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
print("\(self.implementation.TAG) version native \(self.currentVersionNative.description)")
|
|
45
|
+
implementation.versionName = getConfig().getString("version", Bundle.main.versionName)!
|
|
46
|
+
autoDeleteFailed = getConfig().getBoolean("autoDeleteFailed", true)
|
|
47
|
+
autoDeletePrevious = getConfig().getBoolean("autoDeletePrevious", true)
|
|
48
|
+
updateUrl = getConfig().getString("updateUrl", CapacitorUpdaterPlugin.updateUrlDefault)!
|
|
49
|
+
autoUpdate = getConfig().getBoolean("autoUpdate", true)
|
|
50
|
+
appReadyTimeout = getConfig().getInt("appReadyTimeout", 10000)
|
|
51
|
+
resetWhenUpdate = getConfig().getBoolean("resetWhenUpdate", true)
|
|
52
|
+
|
|
53
|
+
implementation.privateKey = getConfig().getString("privateKey", self.defaultPrivateKey)!
|
|
39
54
|
implementation.notifyDownload = notifyDownload
|
|
55
|
+
implementation.PLUGIN_VERSION = self.PLUGIN_VERSION
|
|
40
56
|
let config = (self.bridge?.viewController as? CAPBridgeViewController)?.instanceDescriptor().legacyConfig
|
|
41
|
-
if
|
|
57
|
+
if config?["appId"] != nil {
|
|
42
58
|
implementation.appId = config?["appId"] as! String
|
|
43
59
|
}
|
|
44
|
-
implementation.statsUrl =
|
|
45
|
-
|
|
46
|
-
if
|
|
60
|
+
implementation.statsUrl = getConfig().getString("statsUrl", CapacitorUpdaterPlugin.statsUrlDefault)!
|
|
61
|
+
implementation.channelUrl = getConfig().getString("channelUrl", CapacitorUpdaterPlugin.channelUrlDefault)!
|
|
62
|
+
if resetWhenUpdate {
|
|
47
63
|
self.cleanupObsoleteVersions()
|
|
48
64
|
}
|
|
49
65
|
let nc = NotificationCenter.default
|
|
50
66
|
nc.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
|
|
51
67
|
nc.addObserver(self, selector: #selector(appMovedToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
|
|
68
|
+
nc.addObserver(self, selector: #selector(appKilled), name: UIApplication.willTerminateNotification, object: nil)
|
|
52
69
|
self.appMovedToForeground()
|
|
53
70
|
}
|
|
54
71
|
|
|
@@ -59,14 +76,15 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
59
76
|
} catch {
|
|
60
77
|
print("\(self.implementation.TAG) Cannot get version native \(currentVersionNative)")
|
|
61
78
|
}
|
|
62
|
-
if
|
|
63
|
-
_ = self._reset(
|
|
64
|
-
UserDefaults.standard.set("", forKey: "LatestVersionAutoUpdate")
|
|
65
|
-
UserDefaults.standard.set("", forKey: "LatestVersionNameAutoUpdate")
|
|
79
|
+
if LatestVersionNative != "0.0.0" && self.currentVersionNative.description != LatestVersionNative.description {
|
|
80
|
+
_ = self._reset(toLastSuccessful: false)
|
|
66
81
|
let res = implementation.list()
|
|
67
82
|
res.forEach { version in
|
|
68
83
|
print("\(self.implementation.TAG) Deleting obsolete bundle: \(version)")
|
|
69
|
-
|
|
84
|
+
let res = implementation.delete(id: version.getId())
|
|
85
|
+
if !res {
|
|
86
|
+
print("\(self.implementation.TAG) Delete failed, id \(version.getId()) doesn't exist")
|
|
87
|
+
}
|
|
70
88
|
}
|
|
71
89
|
}
|
|
72
90
|
UserDefaults.standard.set( self.currentVersionNative.description, forKey: "LatestVersionNative")
|
|
@@ -76,19 +94,22 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
76
94
|
@objc func notifyDownload(id: String, percent: Int) {
|
|
77
95
|
let bundle = self.implementation.getBundleInfo(id: id)
|
|
78
96
|
self.notifyListeners("download", data: ["percent": percent, "bundle": bundle.toJSON()])
|
|
79
|
-
if
|
|
97
|
+
if percent == 100 {
|
|
80
98
|
self.notifyListeners("downloadComplete", data: ["bundle": bundle.toJSON()])
|
|
99
|
+
self.implementation.sendStats(action: "download_complete", versionName: bundle.getVersionName())
|
|
100
|
+
} else if percent.isMultiple(of: 10) {
|
|
101
|
+
self.implementation.sendStats(action: "download_\(percent)", versionName: bundle.getVersionName())
|
|
81
102
|
}
|
|
82
103
|
}
|
|
83
104
|
|
|
84
|
-
@objc func
|
|
85
|
-
call.resolve(["
|
|
105
|
+
@objc func getDeviceId(_ call: CAPPluginCall) {
|
|
106
|
+
call.resolve(["deviceId": implementation.deviceID])
|
|
86
107
|
}
|
|
87
108
|
|
|
88
109
|
@objc func getPluginVersion(_ call: CAPPluginCall) {
|
|
89
|
-
call.resolve(["version":
|
|
110
|
+
call.resolve(["version": self.PLUGIN_VERSION])
|
|
90
111
|
}
|
|
91
|
-
|
|
112
|
+
|
|
92
113
|
@objc func download(_ call: CAPPluginCall) {
|
|
93
114
|
guard let urlString = call.getString("url") else {
|
|
94
115
|
print("\(self.implementation.TAG) Download called without url")
|
|
@@ -100,13 +121,32 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
100
121
|
call.reject("Download called without version")
|
|
101
122
|
return
|
|
102
123
|
}
|
|
124
|
+
let sessionKey = call.getString("sessionKey", "")
|
|
125
|
+
let checksum = call.getString("checksum", "")
|
|
103
126
|
let url = URL(string: urlString)
|
|
104
|
-
print("\(self.implementation.TAG) Downloading \(url
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
127
|
+
print("\(self.implementation.TAG) Downloading \(String(describing: url))")
|
|
128
|
+
DispatchQueue.global(qos: .background).async {
|
|
129
|
+
do {
|
|
130
|
+
let next = try self.implementation.download(url: url!, version: version, sessionKey: sessionKey)
|
|
131
|
+
if checksum != "" && next.getChecksum() != checksum {
|
|
132
|
+
print("\(self.implementation.TAG) Error checksum", next.getChecksum(), checksum)
|
|
133
|
+
self.implementation.sendStats(action: "checksum_fail", versionName: next.getVersionName())
|
|
134
|
+
let id = next.getId()
|
|
135
|
+
let resDel = self.implementation.delete(id: id)
|
|
136
|
+
if !resDel {
|
|
137
|
+
print("\(self.implementation.TAG) Delete failed, id \(id) doesn't exist")
|
|
138
|
+
}
|
|
139
|
+
throw ObjectSavableError.checksum
|
|
140
|
+
}
|
|
141
|
+
self.notifyListeners("updateAvailable", data: ["bundle": next.toJSON()])
|
|
142
|
+
call.resolve(next.toJSON())
|
|
143
|
+
} catch {
|
|
144
|
+
print("\(self.implementation.TAG) Failed to download from: \(String(describing: url)) \(error.localizedDescription)")
|
|
145
|
+
self.notifyListeners("downloadFailed", data: ["version": version])
|
|
146
|
+
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
147
|
+
self.implementation.sendStats(action: "download_fail", versionName: current.getVersionName())
|
|
148
|
+
call.reject("Failed to download from: \(url!)", error.localizedDescription)
|
|
149
|
+
}
|
|
110
150
|
}
|
|
111
151
|
}
|
|
112
152
|
|
|
@@ -118,17 +158,18 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
118
158
|
if let vc = bridge.viewController as? CAPBridgeViewController {
|
|
119
159
|
vc.setServerBasePath(path: destHot.path)
|
|
120
160
|
self.checkAppReady()
|
|
161
|
+
self.notifyListeners("appReloaded", data: [:])
|
|
121
162
|
return true
|
|
122
163
|
}
|
|
123
164
|
return false
|
|
124
165
|
}
|
|
125
|
-
|
|
166
|
+
|
|
126
167
|
@objc func reload(_ call: CAPPluginCall) {
|
|
127
|
-
if
|
|
168
|
+
if self._reload() {
|
|
128
169
|
call.resolve()
|
|
129
170
|
} else {
|
|
130
|
-
call.reject("Reload failed")
|
|
131
171
|
print("\(self.implementation.TAG) Reload failed")
|
|
172
|
+
call.reject("Reload failed")
|
|
132
173
|
}
|
|
133
174
|
}
|
|
134
175
|
|
|
@@ -138,15 +179,15 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
138
179
|
call.reject("Next called without id")
|
|
139
180
|
return
|
|
140
181
|
}
|
|
141
|
-
|
|
142
182
|
print("\(self.implementation.TAG) Setting next active id \(id)")
|
|
143
|
-
if
|
|
183
|
+
if !self.implementation.setNextBundle(next: id) {
|
|
184
|
+
print("\(self.implementation.TAG) Set next version failed. id \(id) does not exist.")
|
|
144
185
|
call.reject("Set next version failed. id \(id) does not exist.")
|
|
145
186
|
} else {
|
|
146
187
|
call.resolve(self.implementation.getBundleInfo(id: id).toJSON())
|
|
147
188
|
}
|
|
148
189
|
}
|
|
149
|
-
|
|
190
|
+
|
|
150
191
|
@objc func set(_ call: CAPPluginCall) {
|
|
151
192
|
guard let id = call.getString("id") else {
|
|
152
193
|
print("\(self.implementation.TAG) Set called without id")
|
|
@@ -155,7 +196,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
155
196
|
}
|
|
156
197
|
let res = implementation.set(id: id)
|
|
157
198
|
print("\(self.implementation.TAG) Set active bundle: \(id)")
|
|
158
|
-
if
|
|
199
|
+
if !res {
|
|
159
200
|
print("\(self.implementation.TAG) Bundle successfully set to: \(id) ")
|
|
160
201
|
call.reject("Update failed, id \(id) doesn't exist")
|
|
161
202
|
} else {
|
|
@@ -170,9 +211,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
170
211
|
return
|
|
171
212
|
}
|
|
172
213
|
let res = implementation.delete(id: id)
|
|
173
|
-
if
|
|
214
|
+
if res {
|
|
174
215
|
call.resolve()
|
|
175
216
|
} else {
|
|
217
|
+
print("\(self.implementation.TAG) Delete failed, id \(id) doesn't exist")
|
|
176
218
|
call.reject("Delete failed, id \(id) doesn't exist")
|
|
177
219
|
}
|
|
178
220
|
}
|
|
@@ -184,68 +226,210 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
184
226
|
resArr.append(v.toJSON())
|
|
185
227
|
}
|
|
186
228
|
call.resolve([
|
|
187
|
-
"
|
|
229
|
+
"bundles": resArr
|
|
188
230
|
])
|
|
189
231
|
}
|
|
190
232
|
|
|
191
|
-
@objc func
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if(toAutoUpdate && LatestVersionAutoUpdate != "" && LatestVersionNameAutoUpdate != "") {
|
|
199
|
-
let res = implementation.set(id: LatestVersionNameAutoUpdate)
|
|
200
|
-
return res && self._reload()
|
|
233
|
+
@objc func getLatest(_ call: CAPPluginCall) {
|
|
234
|
+
DispatchQueue.global(qos: .background).async {
|
|
235
|
+
let res = self.implementation.getLatest(url: URL(string: self.updateUrl)!)
|
|
236
|
+
if res.error != nil {
|
|
237
|
+
call.reject( res.error!)
|
|
238
|
+
} else {
|
|
239
|
+
call.resolve(res.toDict())
|
|
201
240
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
@objc func setChannel(_ call: CAPPluginCall) {
|
|
245
|
+
guard let channel = call.getString("channel") else {
|
|
246
|
+
print("\(self.implementation.TAG) setChannel called without channel")
|
|
247
|
+
call.reject("setChannel called without channel")
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
DispatchQueue.global(qos: .background).async {
|
|
251
|
+
let res = self.implementation.setChannel(channel: channel)
|
|
252
|
+
if res.error != "" {
|
|
253
|
+
call.reject(res.error)
|
|
254
|
+
} else {
|
|
255
|
+
call.resolve(res.toDict())
|
|
208
256
|
}
|
|
209
|
-
return true
|
|
210
257
|
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
@objc func getChannel(_ call: CAPPluginCall) {
|
|
261
|
+
DispatchQueue.global(qos: .background).async {
|
|
262
|
+
let res = self.implementation.getChannel()
|
|
263
|
+
if res.error != "" {
|
|
264
|
+
call.reject(res.error)
|
|
265
|
+
} else {
|
|
266
|
+
call.resolve(res.toDict())
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
@objc func setCustomId(_ call: CAPPluginCall) {
|
|
271
|
+
guard let customId = call.getString("customId") else {
|
|
272
|
+
print("\(self.implementation.TAG) setCustomId called without customId")
|
|
273
|
+
call.reject("setCustomId called without customId")
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
self.implementation.customId = customId
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
@objc func _reset(toLastSuccessful: Bool) -> Bool {
|
|
280
|
+
guard let bridge = self.bridge else { return false }
|
|
281
|
+
|
|
282
|
+
if (bridge.viewController as? CAPBridgeViewController) != nil {
|
|
283
|
+
let fallback: BundleInfo = self.implementation.getFallbackBundle()
|
|
284
|
+
|
|
285
|
+
// If developer wants to reset to the last successful bundle, and that bundle is not
|
|
286
|
+
// the built-in bundle, set it as the bundle to use and reload.
|
|
287
|
+
if toLastSuccessful && !fallback.isBuiltin() {
|
|
288
|
+
print("\(self.implementation.TAG) Resetting to: \(fallback.toString())")
|
|
289
|
+
return self.implementation.set(bundle: fallback) && self._reload()
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
print("\(self.implementation.TAG) Resetting to builtin version")
|
|
293
|
+
|
|
294
|
+
// Otherwise, reset back to the built-in bundle and reload.
|
|
295
|
+
self.implementation.reset()
|
|
296
|
+
return self._reload()
|
|
297
|
+
}
|
|
298
|
+
|
|
211
299
|
return false
|
|
212
300
|
}
|
|
213
301
|
|
|
214
302
|
@objc func reset(_ call: CAPPluginCall) {
|
|
215
|
-
let
|
|
216
|
-
if
|
|
217
|
-
|
|
303
|
+
let toLastSuccessful = call.getBool("toLastSuccessful") ?? false
|
|
304
|
+
if self._reset(toLastSuccessful: toLastSuccessful) {
|
|
305
|
+
call.resolve()
|
|
306
|
+
} else {
|
|
307
|
+
print("\(self.implementation.TAG) Reset failed")
|
|
308
|
+
call.reject("\(self.implementation.TAG) Reset failed")
|
|
218
309
|
}
|
|
219
|
-
call.reject("\(self.implementation.TAG) Reset failed")
|
|
220
310
|
}
|
|
221
|
-
|
|
311
|
+
|
|
222
312
|
@objc func current(_ call: CAPPluginCall) {
|
|
223
313
|
let bundle: BundleInfo = self.implementation.getCurrentBundle()
|
|
224
314
|
call.resolve([
|
|
225
315
|
"bundle": bundle.toJSON(),
|
|
226
|
-
"native": self.currentVersionNative
|
|
316
|
+
"native": self.currentVersionNative.description
|
|
227
317
|
])
|
|
228
318
|
}
|
|
229
319
|
|
|
230
320
|
@objc func notifyAppReady(_ call: CAPPluginCall) {
|
|
231
|
-
print("\(self.implementation.TAG) Current bundle loaded successfully. ['notifyAppReady()' was called]")
|
|
232
321
|
let version = self.implementation.getCurrentBundle()
|
|
233
|
-
self.implementation.
|
|
322
|
+
self.implementation.setSuccess(bundle: version, autoDeletePrevious: self.autoDeletePrevious)
|
|
323
|
+
print("\(self.implementation.TAG) Current bundle loaded successfully. ['notifyAppReady()' was called] \(version.toString())")
|
|
234
324
|
call.resolve()
|
|
235
325
|
}
|
|
236
|
-
|
|
237
|
-
@objc func
|
|
238
|
-
guard let
|
|
239
|
-
print("\(self.implementation.TAG)
|
|
240
|
-
call.reject("
|
|
326
|
+
|
|
327
|
+
@objc func setMultiDelay(_ call: CAPPluginCall) {
|
|
328
|
+
guard let delayConditionList = call.getValue("delayConditions") else {
|
|
329
|
+
print("\(self.implementation.TAG) setMultiDelay called without delayCondition")
|
|
330
|
+
call.reject("setMultiDelay called without delayCondition")
|
|
241
331
|
return
|
|
242
332
|
}
|
|
243
|
-
|
|
333
|
+
let delayConditions: String = toJson(object: delayConditionList)
|
|
334
|
+
if _setMultiDelay(delayConditions: delayConditions) {
|
|
335
|
+
call.resolve()
|
|
336
|
+
} else {
|
|
337
|
+
call.reject("Failed to delay update")
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private func _setMultiDelay(delayConditions: String?) -> Bool {
|
|
342
|
+
if delayConditions != nil && "" != delayConditions {
|
|
343
|
+
UserDefaults.standard.set(delayConditions, forKey: DELAY_CONDITION_PREFERENCES)
|
|
344
|
+
UserDefaults.standard.synchronize()
|
|
345
|
+
print("\(self.implementation.TAG) Delay update saved.")
|
|
346
|
+
return true
|
|
347
|
+
} else {
|
|
348
|
+
print("\(self.implementation.TAG) Failed to delay update, [Error calling '_setMultiDelay()']")
|
|
349
|
+
return false
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private func _cancelDelay(source: String) {
|
|
354
|
+
print("\(self.implementation.TAG) delay Canceled from \(source)")
|
|
355
|
+
UserDefaults.standard.removeObject(forKey: DELAY_CONDITION_PREFERENCES)
|
|
356
|
+
UserDefaults.standard.synchronize()
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
@objc func cancelDelay(_ call: CAPPluginCall) {
|
|
360
|
+
self._cancelDelay(source: "JS")
|
|
244
361
|
call.resolve()
|
|
245
362
|
}
|
|
246
|
-
|
|
363
|
+
|
|
364
|
+
private func _checkCancelDelay(killed: Bool) {
|
|
365
|
+
let delayUpdatePreferences = UserDefaults.standard.string(forKey: DELAY_CONDITION_PREFERENCES) ?? "[]"
|
|
366
|
+
let delayConditionList: [DelayCondition] = fromJsonArr(json: delayUpdatePreferences).map { obj -> DelayCondition in
|
|
367
|
+
let kind: String = obj.value(forKey: "kind") as! String
|
|
368
|
+
let value: String? = obj.value(forKey: "value") as? String
|
|
369
|
+
return DelayCondition(kind: kind, value: value)
|
|
370
|
+
}
|
|
371
|
+
for condition in delayConditionList {
|
|
372
|
+
let kind: String? = condition.getKind()
|
|
373
|
+
let value: String? = condition.getValue()
|
|
374
|
+
if kind != nil {
|
|
375
|
+
switch kind {
|
|
376
|
+
case "background":
|
|
377
|
+
if !killed {
|
|
378
|
+
self._cancelDelay(source: "background check")
|
|
379
|
+
}
|
|
380
|
+
break
|
|
381
|
+
case "kill":
|
|
382
|
+
if killed {
|
|
383
|
+
self._cancelDelay(source: "kill check")
|
|
384
|
+
// instant install for kill action
|
|
385
|
+
self.installNext()
|
|
386
|
+
}
|
|
387
|
+
break
|
|
388
|
+
case "date":
|
|
389
|
+
if value != nil && value != "" {
|
|
390
|
+
let dateFormatter = ISO8601DateFormatter()
|
|
391
|
+
dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
392
|
+
guard let ExpireDate = dateFormatter.date(from: value!) else {
|
|
393
|
+
self._cancelDelay(source: "date parsing issue")
|
|
394
|
+
return
|
|
395
|
+
}
|
|
396
|
+
if ExpireDate < Date() {
|
|
397
|
+
self._cancelDelay(source: "date expired")
|
|
398
|
+
}
|
|
399
|
+
} else {
|
|
400
|
+
self._cancelDelay(source: "delayVal absent")
|
|
401
|
+
}
|
|
402
|
+
break
|
|
403
|
+
case "nativeVersion":
|
|
404
|
+
if value != nil && value != "" {
|
|
405
|
+
do {
|
|
406
|
+
let versionLimit = try Version(value!)
|
|
407
|
+
if self.currentVersionNative >= versionLimit {
|
|
408
|
+
self._cancelDelay(source: "nativeVersion above limit")
|
|
409
|
+
}
|
|
410
|
+
} catch {
|
|
411
|
+
self._cancelDelay(source: "nativeVersion parsing issue")
|
|
412
|
+
}
|
|
413
|
+
} else {
|
|
414
|
+
self._cancelDelay(source: "delayVal absent")
|
|
415
|
+
}
|
|
416
|
+
break
|
|
417
|
+
case .none:
|
|
418
|
+
print("\(self.implementation.TAG) _checkCancelDelay switch case none error")
|
|
419
|
+
case .some:
|
|
420
|
+
print("\(self.implementation.TAG) _checkCancelDelay switch case some error")
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
// self.checkAppReady() why this here?
|
|
425
|
+
}
|
|
426
|
+
|
|
247
427
|
private func _isAutoUpdateEnabled() -> Bool {
|
|
248
|
-
|
|
428
|
+
let instanceDescriptor = (self.bridge?.viewController as? CAPBridgeViewController)?.instanceDescriptor()
|
|
429
|
+
if instanceDescriptor?.serverURL != nil {
|
|
430
|
+
print("⚠️ \(self.implementation.TAG) AutoUpdate is automatic disabled when serverUrl is set.")
|
|
431
|
+
}
|
|
432
|
+
return self.autoUpdate && self.updateUrl != "" && instanceDescriptor?.serverURL == nil
|
|
249
433
|
}
|
|
250
434
|
|
|
251
435
|
@objc func isAutoUpdateEnabled(_ call: CAPPluginCall) {
|
|
@@ -263,156 +447,229 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
|
|
|
263
447
|
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(self.appReadyTimeout), execute: self.appReadyCheck!)
|
|
264
448
|
}
|
|
265
449
|
|
|
266
|
-
func
|
|
450
|
+
func checkRevert() {
|
|
267
451
|
// Automatically roll back to fallback version if notifyAppReady has not been called yet
|
|
268
452
|
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
269
|
-
if
|
|
453
|
+
if current.isBuiltin() {
|
|
270
454
|
print("\(self.implementation.TAG) Built-in bundle is active. Nothing to do.")
|
|
271
455
|
return
|
|
272
456
|
}
|
|
273
457
|
|
|
274
|
-
|
|
458
|
+
print("\(self.implementation.TAG) Current bundle is: \(current.toString())")
|
|
459
|
+
|
|
460
|
+
if BundleStatus.SUCCESS.localizedString != current.getStatus() {
|
|
275
461
|
print("\(self.implementation.TAG) notifyAppReady was not called, roll back current bundle: \(current.toString())")
|
|
276
|
-
self.implementation.
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
462
|
+
print("\(self.implementation.TAG) Did you forget to call 'notifyAppReady()' in your Capacitor App code?")
|
|
463
|
+
self.notifyListeners("updateFailed", data: [
|
|
464
|
+
"bundle": current.toJSON()
|
|
465
|
+
])
|
|
466
|
+
self.implementation.sendStats(action: "update_fail", versionName: current.getVersionName())
|
|
467
|
+
self.implementation.setError(bundle: current)
|
|
468
|
+
_ = self._reset(toLastSuccessful: true)
|
|
469
|
+
if self.autoDeleteFailed && !current.isBuiltin() {
|
|
470
|
+
print("\(self.implementation.TAG) Deleting failing bundle: \(current.toString())")
|
|
471
|
+
let res = self.implementation.delete(id: current.getId(), removeInfo: false)
|
|
472
|
+
if !res {
|
|
473
|
+
print("\(self.implementation.TAG) Delete version deleted: \(current.toString())")
|
|
474
|
+
} else {
|
|
475
|
+
print("\(self.implementation.TAG) Failed to delete failed bundle: \(current.toString())")
|
|
476
|
+
}
|
|
280
477
|
}
|
|
281
478
|
} else {
|
|
282
479
|
print("\(self.implementation.TAG) notifyAppReady was called. This is fine: \(current.toString())")
|
|
283
480
|
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
func DeferredNotifyAppReadyCheck() {
|
|
484
|
+
self.checkRevert()
|
|
284
485
|
self.appReadyCheck = nil
|
|
285
486
|
}
|
|
286
487
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
488
|
+
func endBackGroundTask() {
|
|
489
|
+
UIApplication.shared.endBackgroundTask(self.backgroundTaskID)
|
|
490
|
+
self.backgroundTaskID = UIBackgroundTaskIdentifier.invalid
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
func backgroundDownload() {
|
|
494
|
+
DispatchQueue.global(qos: .background).async {
|
|
495
|
+
self.backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "Finish Download Tasks") {
|
|
496
|
+
// End the task if time expires.
|
|
497
|
+
self.endBackGroundTask()
|
|
498
|
+
}
|
|
499
|
+
print("\(self.implementation.TAG) Check for update via \(self.updateUrl)")
|
|
500
|
+
let url = URL(string: self.updateUrl)!
|
|
501
|
+
let res = self.implementation.getLatest(url: url)
|
|
502
|
+
let current = self.implementation.getCurrentBundle()
|
|
503
|
+
|
|
504
|
+
if (res.message) != nil {
|
|
505
|
+
print("\(self.implementation.TAG) message \(res.message ?? "")")
|
|
506
|
+
if res.major == true {
|
|
507
|
+
self.notifyListeners("majorAvailable", data: ["version": res.version])
|
|
296
508
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
509
|
+
self.notifyListeners("noNeedUpdate", data: ["bundle": current.toJSON()])
|
|
510
|
+
self.endBackGroundTask()
|
|
511
|
+
return
|
|
512
|
+
}
|
|
513
|
+
let sessionKey = res.sessionKey ?? ""
|
|
514
|
+
guard let downloadUrl = URL(string: res.url) else {
|
|
515
|
+
print("\(self.implementation.TAG) Error no url or wrong format")
|
|
516
|
+
self.notifyListeners("noNeedUpdate", data: ["bundle": current.toJSON()])
|
|
517
|
+
self.endBackGroundTask()
|
|
518
|
+
return
|
|
519
|
+
}
|
|
520
|
+
let latestVersionName = res.version
|
|
521
|
+
if latestVersionName != "" && current.getVersionName() != latestVersionName {
|
|
522
|
+
let latest = self.implementation.getBundleInfoByVersionName(version: latestVersionName)
|
|
523
|
+
if latest != nil {
|
|
524
|
+
if latest!.isErrorStatus() {
|
|
525
|
+
print("\(self.implementation.TAG) Latest version already exists, and is in error state. Aborting update.")
|
|
526
|
+
self.notifyListeners("noNeedUpdate", data: ["bundle": current.toJSON()])
|
|
527
|
+
self.endBackGroundTask()
|
|
528
|
+
return
|
|
301
529
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return
|
|
530
|
+
if latest!.isDownloaded() {
|
|
531
|
+
print("\(self.implementation.TAG) Latest version already exists and download is NOT required. Update will occur next time app moves to background.")
|
|
532
|
+
self.notifyListeners("updateAvailable", data: ["bundle": current.toJSON()])
|
|
533
|
+
_ = self.implementation.setNextBundle(next: latest!.getId())
|
|
534
|
+
self.endBackGroundTask()
|
|
535
|
+
return
|
|
536
|
+
}
|
|
537
|
+
if latest!.isDeleted() {
|
|
538
|
+
print("\(self.implementation.TAG) Latest bundle already exists and will be deleted, download will overwrite it.")
|
|
539
|
+
let res = self.implementation.delete(id: latest!.getId(), removeInfo: true)
|
|
540
|
+
if !res {
|
|
541
|
+
print("\(self.implementation.TAG) Delete version deleted: \(latest!.toString())")
|
|
542
|
+
} else {
|
|
543
|
+
print("\(self.implementation.TAG) Failed to delete failed bundle: \(latest!.toString())")
|
|
317
544
|
}
|
|
318
545
|
}
|
|
546
|
+
}
|
|
319
547
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
548
|
+
do {
|
|
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)
|
|
553
|
+
self.implementation.sendStats(action: "checksum_fail", versionName: next.getVersionName())
|
|
554
|
+
let id = next.getId()
|
|
555
|
+
let resDel = self.implementation.delete(id: id)
|
|
556
|
+
if !resDel {
|
|
557
|
+
print("\(self.implementation.TAG) Delete failed, id \(id) doesn't exist")
|
|
558
|
+
}
|
|
559
|
+
self.endBackGroundTask()
|
|
560
|
+
throw ObjectSavableError.checksum
|
|
327
561
|
}
|
|
562
|
+
self.notifyListeners("updateAvailable", data: ["bundle": next.toJSON()])
|
|
563
|
+
_ = self.implementation.setNextBundle(next: next.getId())
|
|
564
|
+
} catch {
|
|
565
|
+
print("\(self.implementation.TAG) Error downloading file", error.localizedDescription)
|
|
566
|
+
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
567
|
+
self.implementation.sendStats(action: "download_fail", versionName: current.getVersionName())
|
|
568
|
+
self.notifyListeners("downloadFailed", data: ["version": latestVersionName])
|
|
569
|
+
self.notifyListeners("noNeedUpdate", data: ["bundle": current.toJSON()])
|
|
328
570
|
}
|
|
571
|
+
} else {
|
|
572
|
+
print("\(self.implementation.TAG) No need to update, \(current.getId()) is the latest bundle.")
|
|
573
|
+
self.notifyListeners("noNeedUpdate", data: ["bundle": current.toJSON()])
|
|
329
574
|
}
|
|
575
|
+
self.endBackGroundTask()
|
|
330
576
|
}
|
|
577
|
+
}
|
|
331
578
|
|
|
332
|
-
|
|
579
|
+
@objc func appKilled() {
|
|
580
|
+
print("\(self.implementation.TAG) onActivityDestroyed: all activity destroyed")
|
|
581
|
+
self._checkCancelDelay(killed: true)
|
|
333
582
|
}
|
|
334
583
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
let
|
|
338
|
-
|
|
339
|
-
|
|
584
|
+
private func installNext() {
|
|
585
|
+
let delayUpdatePreferences = UserDefaults.standard.string(forKey: DELAY_CONDITION_PREFERENCES) ?? "[]"
|
|
586
|
+
let delayConditionList: [DelayCondition]? = fromJsonArr(json: delayUpdatePreferences).map { obj -> DelayCondition in
|
|
587
|
+
let kind: String = obj.value(forKey: "kind") as! String
|
|
588
|
+
let value: String? = obj.value(forKey: "value") as? String
|
|
589
|
+
return DelayCondition(kind: kind, value: value)
|
|
590
|
+
}
|
|
591
|
+
if delayConditionList != nil && delayConditionList?.capacity != 0 {
|
|
340
592
|
print("\(self.implementation.TAG) Update delayed to next backgrounding")
|
|
341
593
|
return
|
|
342
594
|
}
|
|
343
|
-
|
|
344
|
-
let fallback: BundleInfo = self.implementation.getFallbackVersion()
|
|
345
595
|
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
346
|
-
let next: BundleInfo? = self.implementation.
|
|
347
|
-
|
|
348
|
-
let success: Bool = current.getStatus() == BundleStatus.SUCCESS.localizedString
|
|
596
|
+
let next: BundleInfo? = self.implementation.getNextBundle()
|
|
349
597
|
|
|
350
|
-
|
|
351
|
-
print("\(self.implementation.TAG) Current bundle is: \(current.toString())")
|
|
352
|
-
|
|
353
|
-
if (next != nil && !next!.isErrorStatus() && (next!.getVersionName() != current.getVersionName())) {
|
|
598
|
+
if next != nil && !next!.isErrorStatus() && next!.getVersionName() != current.getVersionName() {
|
|
354
599
|
print("\(self.implementation.TAG) Next bundle is: \(next!.toString())")
|
|
355
|
-
if
|
|
356
|
-
print("\(self.implementation.TAG) Updated to bundle: \(next
|
|
357
|
-
|
|
600
|
+
if self.implementation.set(bundle: next!) && self._reload() {
|
|
601
|
+
print("\(self.implementation.TAG) Updated to bundle: \(next!.toString())")
|
|
602
|
+
_ = self.implementation.setNextBundle(next: Optional<String>.none)
|
|
358
603
|
} else {
|
|
359
|
-
print("\(self.implementation.TAG)
|
|
604
|
+
print("\(self.implementation.TAG) Update to bundle: \(next!.toString()) Failed!")
|
|
360
605
|
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
if(!current.isBuiltin()) {
|
|
365
|
-
// Don't try to roll back the builtin version. Nothing we can do.
|
|
366
|
-
|
|
367
|
-
self.implementation.rollback(bundle: current)
|
|
368
|
-
|
|
369
|
-
print("\(self.implementation.TAG) Update failed: 'notifyAppReady()' was never called.")
|
|
370
|
-
print("\(self.implementation.TAG) Version: \(current.toString()), is in error state.")
|
|
371
|
-
print("\(self.implementation.TAG) Will fallback to: \(fallback.toString()) on application restart.")
|
|
372
|
-
print("\(self.implementation.TAG) Did you forget to call 'notifyAppReady()' in your Capacitor App code?")
|
|
373
|
-
|
|
374
|
-
self.notifyListeners("updateFailed", data: [
|
|
375
|
-
"bundle": current.toJSON()
|
|
376
|
-
])
|
|
377
|
-
self.implementation.sendStats(action: "revert", bundle: current)
|
|
378
|
-
if (!fallback.isBuiltin() && !(fallback == current)) {
|
|
379
|
-
let res = self.implementation.set(bundle: fallback)
|
|
380
|
-
if (res && self._reload()) {
|
|
381
|
-
print("\(self.implementation.TAG) Revert to bundle: \(fallback.toString())")
|
|
382
|
-
} else {
|
|
383
|
-
print("\(self.implementation.TAG) Revert to bundle: \(fallback.toString()) Failed!")
|
|
384
|
-
}
|
|
385
|
-
} else {
|
|
386
|
-
if (self._reset(toAutoUpdate: false)) {
|
|
387
|
-
print("\(self.implementation.TAG) Reverted to 'builtin' bundle.")
|
|
388
|
-
}
|
|
389
|
-
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
390
608
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
609
|
+
@objc private func toJson(object: Any) -> String {
|
|
610
|
+
guard let data = try? JSONSerialization.data(withJSONObject: object, options: []) else {
|
|
611
|
+
return ""
|
|
612
|
+
}
|
|
613
|
+
return String(data: data, encoding: String.Encoding.utf8) ?? ""
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
@objc private func fromJsonArr(json: String) -> [NSObject] {
|
|
617
|
+
let jsonData = json.data(using: .utf8)!
|
|
618
|
+
let object = try? JSONSerialization.jsonObject(
|
|
619
|
+
with: jsonData,
|
|
620
|
+
options: .mutableContainers
|
|
621
|
+
) as? [NSObject]
|
|
622
|
+
return object ?? []
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
@objc func appMovedToForeground() {
|
|
626
|
+
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
627
|
+
self.implementation.sendStats(action: "app_moved_to_foreground", versionName: current.getVersionName())
|
|
628
|
+
if backgroundWork != nil && taskRunning {
|
|
629
|
+
backgroundWork!.cancel()
|
|
630
|
+
print("\(self.implementation.TAG) Background Timer Task canceled, Activity resumed before timer completes")
|
|
631
|
+
}
|
|
632
|
+
if self._isAutoUpdateEnabled() {
|
|
633
|
+
self.backgroundDownload()
|
|
634
|
+
} else {
|
|
635
|
+
print("\(self.implementation.TAG) Auto update is disabled")
|
|
636
|
+
}
|
|
637
|
+
self.checkAppReady()
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
@objc func appMovedToBackground() {
|
|
641
|
+
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
642
|
+
self.implementation.sendStats(action: "app_moved_to_background", versionName: current.getVersionName())
|
|
643
|
+
print("\(self.implementation.TAG) Check for pending update")
|
|
644
|
+
let delayUpdatePreferences = UserDefaults.standard.string(forKey: DELAY_CONDITION_PREFERENCES) ?? "[]"
|
|
645
|
+
|
|
646
|
+
let delayConditionList: [DelayCondition] = fromJsonArr(json: delayUpdatePreferences).map { obj -> DelayCondition in
|
|
647
|
+
let kind: String = obj.value(forKey: "kind") as! String
|
|
648
|
+
let value: String? = obj.value(forKey: "value") as? String
|
|
649
|
+
return DelayCondition(kind: kind, value: value)
|
|
650
|
+
}
|
|
651
|
+
var backgroundValue: String?
|
|
652
|
+
for delayCondition in delayConditionList {
|
|
653
|
+
if delayCondition.getKind() == "background" {
|
|
654
|
+
let value: String? = delayCondition.getValue()
|
|
655
|
+
backgroundValue = (value != nil && value != "") ? value! : "0"
|
|
415
656
|
}
|
|
416
657
|
}
|
|
658
|
+
if backgroundValue != nil {
|
|
659
|
+
self.taskRunning = true
|
|
660
|
+
let interval: Double = (Double(backgroundValue!) ?? 0.0) / 1000
|
|
661
|
+
self.backgroundWork?.cancel()
|
|
662
|
+
self.backgroundWork = DispatchWorkItem(block: {
|
|
663
|
+
// IOS never executes this task in background
|
|
664
|
+
self.taskRunning = false
|
|
665
|
+
self._checkCancelDelay(killed: false)
|
|
666
|
+
self.installNext()
|
|
667
|
+
})
|
|
668
|
+
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + interval, execute: self.backgroundWork!)
|
|
669
|
+
} else {
|
|
670
|
+
self._checkCancelDelay(killed: false)
|
|
671
|
+
self.installNext()
|
|
672
|
+
}
|
|
673
|
+
|
|
417
674
|
}
|
|
418
675
|
}
|