@capgo/capacitor-updater 3.2.0 → 3.2.1-alpha.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/README.md +207 -130
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +130 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleStatus.java +36 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +403 -288
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +485 -268
- package/dist/docs.json +512 -109
- package/dist/esm/definitions.d.ts +201 -69
- package/dist/esm/web.d.ts +21 -16
- package/dist/esm/web.js +29 -23
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +29 -23
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +29 -23
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/BundleInfo.swift +94 -0
- package/ios/Plugin/BundleStatus.swift +41 -0
- package/ios/Plugin/CapacitorUpdater.swift +341 -88
- package/ios/Plugin/CapacitorUpdaterPlugin.m +4 -2
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +277 -166
- package/ios/Plugin/ObjectPreferences.swift +97 -0
- package/package.json +2 -1
|
@@ -10,6 +10,11 @@ extension URL {
|
|
|
10
10
|
return FileManager().fileExists(atPath: self.path)
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
+
extension Date {
|
|
14
|
+
func adding(minutes: Int) -> Date {
|
|
15
|
+
return Calendar.current.date(byAdding: .minute, value: minutes, to: self)!
|
|
16
|
+
}
|
|
17
|
+
}
|
|
13
18
|
struct AppVersionDec: Decodable {
|
|
14
19
|
let version: String?
|
|
15
20
|
let url: String?
|
|
@@ -36,28 +41,111 @@ extension Bundle {
|
|
|
36
41
|
}
|
|
37
42
|
}
|
|
38
43
|
|
|
44
|
+
extension ISO8601DateFormatter {
|
|
45
|
+
convenience init(_ formatOptions: Options) {
|
|
46
|
+
self.init()
|
|
47
|
+
self.formatOptions = formatOptions
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
extension Formatter {
|
|
51
|
+
static let iso8601withFractionalSeconds = ISO8601DateFormatter([.withInternetDateTime, .withFractionalSeconds])
|
|
52
|
+
}
|
|
53
|
+
extension Date {
|
|
54
|
+
var iso8601withFractionalSeconds: String { return Formatter.iso8601withFractionalSeconds.string(from: self) }
|
|
55
|
+
}
|
|
56
|
+
extension String {
|
|
57
|
+
|
|
58
|
+
var fileURL: URL {
|
|
59
|
+
return URL(fileURLWithPath: self)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
var lastPathComponent:String {
|
|
63
|
+
get {
|
|
64
|
+
return fileURL.lastPathComponent
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
var iso8601withFractionalSeconds: Date? {
|
|
68
|
+
return Formatter.iso8601withFractionalSeconds.date(from: self)
|
|
69
|
+
}
|
|
70
|
+
func trim(using characterSet: CharacterSet = .whitespacesAndNewlines) -> String {
|
|
71
|
+
return trimmingCharacters(in: characterSet)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
enum CustomError: Error {
|
|
76
|
+
// Throw when an unzip fail
|
|
77
|
+
case cannotUnzip
|
|
78
|
+
case cannotUnflat
|
|
79
|
+
case cannotCreateDirectory
|
|
80
|
+
case cannotDeleteDirectory
|
|
81
|
+
|
|
82
|
+
// Throw in all other cases
|
|
83
|
+
case unexpected(code: Int)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
extension CustomError: LocalizedError {
|
|
87
|
+
public var errorDescription: String? {
|
|
88
|
+
switch self {
|
|
89
|
+
case .cannotUnzip:
|
|
90
|
+
return NSLocalizedString(
|
|
91
|
+
"The file cannot be unzip",
|
|
92
|
+
comment: "Invalid zip"
|
|
93
|
+
)
|
|
94
|
+
case .cannotCreateDirectory:
|
|
95
|
+
return NSLocalizedString(
|
|
96
|
+
"The folder cannot be created",
|
|
97
|
+
comment: "Invalid folder"
|
|
98
|
+
)
|
|
99
|
+
case .cannotDeleteDirectory:
|
|
100
|
+
return NSLocalizedString(
|
|
101
|
+
"The folder cannot be deleted",
|
|
102
|
+
comment: "Invalid folder"
|
|
103
|
+
)
|
|
104
|
+
case .cannotUnflat:
|
|
105
|
+
return NSLocalizedString(
|
|
106
|
+
"The file cannot be unflat",
|
|
107
|
+
comment: "Invalid folder"
|
|
108
|
+
)
|
|
109
|
+
case .unexpected(_):
|
|
110
|
+
return NSLocalizedString(
|
|
111
|
+
"An unexpected error occurred.",
|
|
112
|
+
comment: "Unexpected Error"
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
39
118
|
@objc public class CapacitorUpdater: NSObject {
|
|
40
119
|
|
|
120
|
+
private let versionBuild = Bundle.main.releaseVersionNumber ?? ""
|
|
121
|
+
private let versionCode = Bundle.main.buildVersionNumber ?? ""
|
|
122
|
+
private let versionOs = ProcessInfo().operatingSystemVersion.getFullVersion()
|
|
123
|
+
private let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
|
124
|
+
private let libraryDir = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!
|
|
125
|
+
private let bundleDirectoryHot = "versions"
|
|
126
|
+
private let DEFAULT_FOLDER = ""
|
|
127
|
+
private let bundleDirectory = "NoCloud/ionic_built_snapshots"
|
|
128
|
+
private let INFO_SUFFIX = "_info"
|
|
129
|
+
private let FALLBACK_VERSION = "pastVersion"
|
|
130
|
+
private let NEXT_VERSION = "nextVersion"
|
|
131
|
+
|
|
132
|
+
private var lastPathHot = ""
|
|
133
|
+
private var lastPathPersist = ""
|
|
134
|
+
|
|
135
|
+
public let TAG = "✨ Capacitor-updater:";
|
|
136
|
+
public let CAP_SERVER_PATH = "serverBasePath"
|
|
137
|
+
public let pluginVersion = "3.2.1-alpha.0"
|
|
41
138
|
public var statsUrl = ""
|
|
42
139
|
public var appId = ""
|
|
43
140
|
public var deviceID = UIDevice.current.identifierForVendor?.uuidString ?? ""
|
|
44
|
-
public var notifyDownload: (Int) -> Void = { _ in }
|
|
45
|
-
public var pluginVersion = "3.2.0"
|
|
46
|
-
private var versionBuild = Bundle.main.releaseVersionNumber ?? ""
|
|
47
|
-
private var versionCode = Bundle.main.buildVersionNumber ?? ""
|
|
48
|
-
private var versionOs = ProcessInfo().operatingSystemVersion.getFullVersion()
|
|
49
|
-
private var lastPathHot = ""
|
|
50
|
-
private var lastPathPersist = ""
|
|
51
|
-
private let basePathHot = "versions"
|
|
52
|
-
private let basePathPersist = "NoCloud/ionic_built_snapshots"
|
|
53
|
-
private let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
|
54
|
-
private let libraryUrl = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!
|
|
55
141
|
|
|
56
|
-
|
|
142
|
+
public var notifyDownload: (String, Int) -> Void = { _,_ in }
|
|
143
|
+
|
|
144
|
+
private func calcTotalPercent(percent: Int, min: Int, max: Int) -> Int {
|
|
57
145
|
return (percent * (max - min)) / 100 + min;
|
|
58
146
|
}
|
|
59
147
|
|
|
60
|
-
|
|
148
|
+
private func randomString(length: Int) -> String {
|
|
61
149
|
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
62
150
|
return String((0..<length).map{ _ in letters.randomElement()! })
|
|
63
151
|
}
|
|
@@ -66,25 +154,27 @@ extension Bundle {
|
|
|
66
154
|
// Hot Reload path /var/mobile/Containers/Data/Application/8C0C07BE-0FD3-4FD4-B7DF-90A88E12B8C3/Documents/FOLDER
|
|
67
155
|
// Normal /private/var/containers/Bundle/Application/8C0C07BE-0FD3-4FD4-B7DF-90A88E12B8C3/App.app/public
|
|
68
156
|
|
|
69
|
-
private func prepareFolder(source: URL) {
|
|
157
|
+
private func prepareFolder(source: URL) throws {
|
|
70
158
|
if (!FileManager.default.fileExists(atPath: source.path)) {
|
|
71
159
|
do {
|
|
72
160
|
try FileManager.default.createDirectory(atPath: source.path, withIntermediateDirectories: true, attributes: nil)
|
|
73
161
|
} catch {
|
|
74
|
-
print("
|
|
162
|
+
print("\(self.TAG) Cannot createDirectory \(source.path)")
|
|
163
|
+
throw CustomError.cannotCreateDirectory
|
|
75
164
|
}
|
|
76
165
|
}
|
|
77
166
|
}
|
|
78
167
|
|
|
79
|
-
private func deleteFolder(source: URL) {
|
|
168
|
+
private func deleteFolder(source: URL) throws {
|
|
80
169
|
do {
|
|
81
170
|
try FileManager.default.removeItem(atPath: source.path)
|
|
82
171
|
} catch {
|
|
83
|
-
print("
|
|
172
|
+
print("\(self.TAG) File not removed. \(source.path)")
|
|
173
|
+
throw CustomError.cannotDeleteDirectory
|
|
84
174
|
}
|
|
85
175
|
}
|
|
86
176
|
|
|
87
|
-
private func unflatFolder(source: URL, dest: URL) -> Bool {
|
|
177
|
+
private func unflatFolder(source: URL, dest: URL) throws -> Bool {
|
|
88
178
|
let index = source.appendingPathComponent("index.html")
|
|
89
179
|
do {
|
|
90
180
|
let files = try FileManager.default.contentsOfDirectory(atPath: source.path)
|
|
@@ -96,35 +186,37 @@ extension Bundle {
|
|
|
96
186
|
return false
|
|
97
187
|
}
|
|
98
188
|
} catch {
|
|
99
|
-
print("
|
|
100
|
-
|
|
189
|
+
print("\(self.TAG) File not moved. source: \(source.path) dest: \(dest.path)")
|
|
190
|
+
throw CustomError.cannotUnflat
|
|
101
191
|
}
|
|
102
192
|
}
|
|
103
193
|
|
|
104
|
-
private func saveDownloaded(sourceZip: URL,
|
|
105
|
-
prepareFolder(source: base)
|
|
106
|
-
let destHot = base.appendingPathComponent(
|
|
107
|
-
let destUnZip =
|
|
108
|
-
SSZipArchive.unzipFile(atPath: sourceZip.path, toDestination: destUnZip.path)
|
|
109
|
-
|
|
110
|
-
|
|
194
|
+
private func saveDownloaded(sourceZip: URL, id: String, base: URL) throws {
|
|
195
|
+
try prepareFolder(source: base)
|
|
196
|
+
let destHot = base.appendingPathComponent(id)
|
|
197
|
+
let destUnZip = documentsDir.appendingPathComponent(randomString(length: 10))
|
|
198
|
+
if (!SSZipArchive.unzipFile(atPath: sourceZip.path, toDestination: destUnZip.path)) {
|
|
199
|
+
throw CustomError.cannotUnzip
|
|
200
|
+
}
|
|
201
|
+
if (try unflatFolder(source: destUnZip, dest: destHot)) {
|
|
202
|
+
try deleteFolder(source: destUnZip)
|
|
111
203
|
}
|
|
112
204
|
}
|
|
113
205
|
|
|
114
|
-
|
|
206
|
+
public func getLatest(url: URL) -> AppVersion? {
|
|
115
207
|
let semaphore = DispatchSemaphore(value: 0)
|
|
116
208
|
let latest = AppVersion()
|
|
117
|
-
let
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
"
|
|
124
|
-
"
|
|
125
|
-
"
|
|
209
|
+
let parameters: [String: String] = [
|
|
210
|
+
"platform": "ios",
|
|
211
|
+
"device_id": self.deviceID,
|
|
212
|
+
"app_id": self.appId,
|
|
213
|
+
"version_build": self.versionBuild,
|
|
214
|
+
"version_code": self.versionCode,
|
|
215
|
+
"version_os": self.versionOs,
|
|
216
|
+
"plugin_version": self.pluginVersion,
|
|
217
|
+
"version_name": self.getCurrentBundle().getVersionName()
|
|
126
218
|
]
|
|
127
|
-
let request = AF.request(url,
|
|
219
|
+
let request = AF.request(url, method: .post,parameters: parameters, encoder: JSONParameterEncoder.default)
|
|
128
220
|
|
|
129
221
|
request.validate().responseDecodable(of: AppVersionDec.self) { response in
|
|
130
222
|
switch response.result {
|
|
@@ -140,9 +232,10 @@ extension Bundle {
|
|
|
140
232
|
}
|
|
141
233
|
if let message = response.value?.message {
|
|
142
234
|
latest.message = message
|
|
235
|
+
print("\(self.TAG) Auto-update message: \(message)")
|
|
143
236
|
}
|
|
144
237
|
case let .failure(error):
|
|
145
|
-
print("
|
|
238
|
+
print("\(self.TAG) Error getting Latest", error )
|
|
146
239
|
}
|
|
147
240
|
semaphore.signal()
|
|
148
241
|
}
|
|
@@ -150,9 +243,16 @@ extension Bundle {
|
|
|
150
243
|
return latest.url != "" ? latest : nil
|
|
151
244
|
}
|
|
152
245
|
|
|
153
|
-
|
|
246
|
+
private func setCurrentBundle(bundle: String) {
|
|
247
|
+
UserDefaults.standard.set(bundle, forKey: self.CAP_SERVER_PATH)
|
|
248
|
+
print("\(self.TAG) Current bundle set to: \(bundle)")
|
|
249
|
+
UserDefaults.standard.synchronize()
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
public func download(url: URL, version: String) throws -> BundleInfo {
|
|
154
253
|
let semaphore = DispatchSemaphore(value: 0)
|
|
155
|
-
|
|
254
|
+
let id: String = self.randomString(length: 10)
|
|
255
|
+
var mainError: NSError? = nil
|
|
156
256
|
let destination: DownloadRequest.Destination = { _, _ in
|
|
157
257
|
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
|
158
258
|
let fileURL = documentsURL.appendingPathComponent(self.randomString(length: 10))
|
|
@@ -163,104 +263,143 @@ extension Bundle {
|
|
|
163
263
|
|
|
164
264
|
request.downloadProgress { progress in
|
|
165
265
|
let percent = self.calcTotalPercent(percent: Int(progress.fractionCompleted * 100), min: 10, max: 70)
|
|
166
|
-
self.notifyDownload(percent)
|
|
266
|
+
self.notifyDownload(id, percent)
|
|
167
267
|
}
|
|
168
268
|
request.responseURL { (response) in
|
|
169
269
|
if let fileURL = response.fileURL {
|
|
170
270
|
switch response.result {
|
|
171
271
|
case .success:
|
|
172
|
-
self.notifyDownload(71)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
272
|
+
self.notifyDownload(id, 71)
|
|
273
|
+
do {
|
|
274
|
+
try self.saveDownloaded(sourceZip: fileURL, id: id, base: self.documentsDir.appendingPathComponent(self.bundleDirectoryHot))
|
|
275
|
+
self.notifyDownload(id, 85)
|
|
276
|
+
try self.saveDownloaded(sourceZip: fileURL, id: id, base: self.libraryDir.appendingPathComponent(self.bundleDirectory))
|
|
277
|
+
self.notifyDownload(id, 100)
|
|
278
|
+
try self.deleteFolder(source: fileURL)
|
|
279
|
+
} catch {
|
|
280
|
+
print("\(self.TAG) download unzip error", error)
|
|
281
|
+
mainError = error as NSError
|
|
282
|
+
}
|
|
179
283
|
case let .failure(error):
|
|
180
|
-
print("
|
|
181
|
-
|
|
284
|
+
print("\(self.TAG) download error", error)
|
|
285
|
+
mainError = error as NSError
|
|
182
286
|
}
|
|
183
287
|
}
|
|
184
288
|
semaphore.signal()
|
|
185
289
|
}
|
|
186
|
-
self.
|
|
290
|
+
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.DOWNLOADING, downloaded: Date()))
|
|
291
|
+
self.notifyDownload(id, 0)
|
|
187
292
|
semaphore.wait()
|
|
188
|
-
|
|
293
|
+
if (mainError != nil) {
|
|
294
|
+
throw mainError!
|
|
295
|
+
}
|
|
296
|
+
let info: BundleInfo = BundleInfo(id: id, version: version, status: BundleStatus.PENDING, downloaded: Date())
|
|
297
|
+
self.saveBundleInfo(id: id, bundle: info)
|
|
298
|
+
return info
|
|
189
299
|
}
|
|
190
300
|
|
|
191
|
-
|
|
192
|
-
let dest =
|
|
301
|
+
public func list() -> [BundleInfo] {
|
|
302
|
+
let dest = documentsDir.appendingPathComponent(bundleDirectoryHot)
|
|
193
303
|
do {
|
|
194
304
|
let files = try FileManager.default.contentsOfDirectory(atPath: dest.path)
|
|
195
|
-
|
|
305
|
+
var res: [BundleInfo] = []
|
|
306
|
+
print("\(self.TAG) list File : \(dest.path)")
|
|
307
|
+
if (dest.exist) {
|
|
308
|
+
for id in files {
|
|
309
|
+
res.append(self.getBundleInfo(id: id));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return res
|
|
196
313
|
} catch {
|
|
197
|
-
print("
|
|
314
|
+
print("\(self.TAG) No version available \(dest.path)")
|
|
198
315
|
return []
|
|
199
316
|
}
|
|
200
317
|
}
|
|
201
318
|
|
|
202
|
-
|
|
203
|
-
let
|
|
204
|
-
let
|
|
319
|
+
public func delete(id: String) -> Bool {
|
|
320
|
+
let deleted: BundleInfo = self.getBundleInfo(id: id)
|
|
321
|
+
let destHot = documentsDir.appendingPathComponent(bundleDirectoryHot).appendingPathComponent(id)
|
|
322
|
+
let destPersist = libraryDir.appendingPathComponent(bundleDirectory).appendingPathComponent(id)
|
|
205
323
|
do {
|
|
206
324
|
try FileManager.default.removeItem(atPath: destHot.path)
|
|
207
325
|
} catch {
|
|
208
|
-
print("
|
|
326
|
+
print("\(self.TAG) Hot Folder \(destHot.path), not removed.")
|
|
209
327
|
}
|
|
210
328
|
do {
|
|
211
329
|
try FileManager.default.removeItem(atPath: destPersist.path)
|
|
212
330
|
} catch {
|
|
213
|
-
print("
|
|
331
|
+
print("\(self.TAG) Folder \(destPersist.path), not removed.")
|
|
214
332
|
return false
|
|
215
333
|
}
|
|
216
|
-
|
|
334
|
+
self.removeBundleInfo(id: id)
|
|
335
|
+
self.sendStats(action: "delete", bundle: deleted)
|
|
217
336
|
return true
|
|
218
337
|
}
|
|
219
338
|
|
|
220
|
-
|
|
221
|
-
|
|
339
|
+
public func getBundleDirectory(id: String) -> URL {
|
|
340
|
+
return libraryDir.appendingPathComponent(self.bundleDirectory).appendingPathComponent(id)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
public func set(bundle: BundleInfo) -> Bool {
|
|
344
|
+
return self.set(id: bundle.getId());
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
public func set(id: String) -> Bool {
|
|
348
|
+
let destHot = self.getPathHot(id: id)
|
|
349
|
+
let destHotPersist = self.getPathPersist(id: id)
|
|
222
350
|
let indexHot = destHot.appendingPathComponent("index.html")
|
|
223
|
-
let destHotPersist = libraryUrl.appendingPathComponent(basePathPersist).appendingPathComponent(version)
|
|
224
351
|
let indexPersist = destHotPersist.appendingPathComponent("index.html")
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
352
|
+
let existing: BundleInfo = self.getBundleInfo(id: id)
|
|
353
|
+
let bundle: URL = self.getBundleDirectory(id: id)
|
|
354
|
+
print("bundle", bundle.path)
|
|
355
|
+
if (bundle.isDirectory && destHotPersist.isDirectory && indexHot.exist && indexPersist.exist) {
|
|
356
|
+
self.setCurrentBundle(bundle: String(bundle.path.suffix(10)))
|
|
357
|
+
self.setBundleStatus(id: id, status: BundleStatus.PENDING)
|
|
358
|
+
sendStats(action: "set", bundle: existing)
|
|
230
359
|
return true
|
|
231
360
|
}
|
|
232
|
-
sendStats(action: "set_fail",
|
|
361
|
+
sendStats(action: "set_fail", bundle: existing)
|
|
233
362
|
return false
|
|
234
363
|
}
|
|
235
364
|
|
|
236
|
-
|
|
237
|
-
return
|
|
365
|
+
public func getPathHot(id: String) -> URL {
|
|
366
|
+
return documentsDir.appendingPathComponent(self.bundleDirectoryHot).appendingPathComponent(id)
|
|
238
367
|
}
|
|
239
368
|
|
|
240
|
-
|
|
241
|
-
return
|
|
369
|
+
public func getPathPersist(id: String) -> URL {
|
|
370
|
+
return libraryDir.appendingPathComponent(self.bundleDirectory).appendingPathComponent(id)
|
|
242
371
|
}
|
|
243
372
|
|
|
244
|
-
|
|
245
|
-
|
|
373
|
+
public func reset() {
|
|
374
|
+
self.reset(isInternal: false)
|
|
246
375
|
}
|
|
247
376
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
UserDefaults.standard.set("", forKey: "lastPathPersist")
|
|
253
|
-
UserDefaults.standard.set("", forKey: "versionName")
|
|
377
|
+
public func reset(isInternal: Bool) {
|
|
378
|
+
self.setCurrentBundle(bundle: "")
|
|
379
|
+
self.setFallbackVersion(fallback: Optional<BundleInfo>.none)
|
|
380
|
+
let _ = self.setNextVersion(next: Optional<String>.none)
|
|
254
381
|
UserDefaults.standard.synchronize()
|
|
382
|
+
if(!isInternal) {
|
|
383
|
+
sendStats(action: "reset", bundle: self.getCurrentBundle())
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
public func commit(bundle: BundleInfo) {
|
|
388
|
+
self.setBundleStatus(id: bundle.getId(), status: BundleStatus.SUCCESS)
|
|
389
|
+
self.setFallbackVersion(fallback: bundle)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
public func rollback(bundle: BundleInfo) {
|
|
393
|
+
self.setBundleStatus(id: bundle.getId(), status: BundleStatus.ERROR);
|
|
255
394
|
}
|
|
256
395
|
|
|
257
|
-
|
|
396
|
+
func sendStats(action: String, bundle: BundleInfo) {
|
|
258
397
|
if (statsUrl == "") { return }
|
|
259
398
|
let parameters: [String: String] = [
|
|
260
399
|
"platform": "ios",
|
|
261
400
|
"action": action,
|
|
262
401
|
"device_id": self.deviceID,
|
|
263
|
-
"version_name":
|
|
402
|
+
"version_name": bundle.getVersionName(),
|
|
264
403
|
"version_build": self.versionBuild,
|
|
265
404
|
"version_code": self.versionCode,
|
|
266
405
|
"version_os": self.versionOs,
|
|
@@ -270,8 +409,122 @@ extension Bundle {
|
|
|
270
409
|
|
|
271
410
|
DispatchQueue.global(qos: .background).async {
|
|
272
411
|
let _ = AF.request(self.statsUrl, method: .post,parameters: parameters, encoder: JSONParameterEncoder.default)
|
|
273
|
-
print("
|
|
412
|
+
print("\(self.TAG) Stats send for \(action), version \(bundle.getVersionName())")
|
|
274
413
|
}
|
|
275
414
|
}
|
|
276
|
-
|
|
415
|
+
|
|
416
|
+
public func getBundleInfo(id: String = BundleInfo.ID_BUILTIN) -> BundleInfo {
|
|
417
|
+
print("\(self.TAG) Getting info for bundle [\(id)]")
|
|
418
|
+
if(BundleInfo.ID_BUILTIN == id) {
|
|
419
|
+
return BundleInfo(id: id, version: "", status: BundleStatus.SUCCESS)
|
|
420
|
+
}
|
|
421
|
+
do {
|
|
422
|
+
let result: BundleInfo = try UserDefaults.standard.getObj(forKey: "\(id)\(self.INFO_SUFFIX)", castTo: BundleInfo.self)
|
|
423
|
+
print("\(self.TAG) Returning info bundle [\(id)]", result.toString())
|
|
424
|
+
return result
|
|
425
|
+
} catch {
|
|
426
|
+
print("\(self.TAG) Failed to parse info for bundle [\(id)]", error.localizedDescription)
|
|
427
|
+
return BundleInfo(id: id, version: "", status: BundleStatus.PENDING)
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
public func getBundleInfoByVersionName(version: String) -> BundleInfo? {
|
|
432
|
+
let installed : Array<BundleInfo> = self.list()
|
|
433
|
+
for i in installed {
|
|
434
|
+
if(i.getVersionName() == version) {
|
|
435
|
+
return i
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return nil
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
private func removeBundleInfo(id: String) {
|
|
442
|
+
self.saveBundleInfo(id: id, bundle: nil)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
private func saveBundleInfo(id: String, bundle: BundleInfo?) {
|
|
446
|
+
if (bundle != nil && (bundle!.isBuiltin() || bundle!.isUnknown())) {
|
|
447
|
+
print("\(self.TAG) Not saving info for bundle [\(id)]", bundle!.toString())
|
|
448
|
+
return
|
|
449
|
+
}
|
|
450
|
+
if(bundle == nil) {
|
|
451
|
+
print("\(self.TAG) Removing info for bundle [\(id)]")
|
|
452
|
+
UserDefaults.standard.removeObject(forKey: "\(id)\(self.INFO_SUFFIX)")
|
|
453
|
+
} else {
|
|
454
|
+
let update = bundle!.setId(id: id)
|
|
455
|
+
print("\(self.TAG) Storing info for bundle [\(id)]", update.toString())
|
|
456
|
+
do {
|
|
457
|
+
try UserDefaults.standard.setObj(update, forKey: "\(id)\(self.INFO_SUFFIX)")
|
|
458
|
+
} catch {
|
|
459
|
+
print("\(self.TAG) Failed to save info for bundle [\(id)]", error.localizedDescription)
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
UserDefaults.standard.synchronize()
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
public func setVersionName(id: String, version: String) {
|
|
466
|
+
print("\(self.TAG) Setting version for folder [\(id)] to \(version)")
|
|
467
|
+
let info = self.getBundleInfo(id: id)
|
|
468
|
+
self.saveBundleInfo(id: id, bundle: info.setVersionName(version: version))
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private func setBundleStatus(id: String, status: BundleStatus) {
|
|
472
|
+
print("\(self.TAG) Setting status for bundle [\(id)] to \(status)")
|
|
473
|
+
let info = self.getBundleInfo(id: id)
|
|
474
|
+
self.saveBundleInfo(id: id, bundle: info.setStatus(status: status.localizedString))
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
private func getCurrentBundleVersion() -> String {
|
|
478
|
+
if(self.isUsingBuiltin()) {
|
|
479
|
+
return BundleInfo.ID_BUILTIN
|
|
480
|
+
} else {
|
|
481
|
+
let path: String = self.getCurrentBundleId()
|
|
482
|
+
return path.lastPathComponent
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
public func getCurrentBundle() -> BundleInfo {
|
|
487
|
+
return self.getBundleInfo(id: self.getCurrentBundleId());
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
public func getCurrentBundleId() -> String {
|
|
491
|
+
return UserDefaults.standard.string(forKey: self.CAP_SERVER_PATH) ?? self.DEFAULT_FOLDER
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
public func isUsingBuiltin() -> Bool {
|
|
495
|
+
return self.getCurrentBundleId() == self.DEFAULT_FOLDER
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
public func getFallbackVersion() -> BundleInfo {
|
|
499
|
+
let id: String = UserDefaults.standard.string(forKey: self.FALLBACK_VERSION) ?? BundleInfo.ID_BUILTIN
|
|
500
|
+
return self.getBundleInfo(id: id)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
private func setFallbackVersion(fallback: BundleInfo?) {
|
|
504
|
+
UserDefaults.standard.set(fallback == nil ? BundleInfo.ID_BUILTIN : fallback!.getId(), forKey: self.FALLBACK_VERSION)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
public func getNextVersion() -> BundleInfo? {
|
|
508
|
+
let id: String = UserDefaults.standard.string(forKey: self.NEXT_VERSION) ?? ""
|
|
509
|
+
if(id != "") {
|
|
510
|
+
return self.getBundleInfo(id: id)
|
|
511
|
+
} else {
|
|
512
|
+
return nil
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
public func setNextVersion(next: String?) -> Bool {
|
|
517
|
+
if (next == nil) {
|
|
518
|
+
UserDefaults.standard.removeObject(forKey: self.NEXT_VERSION)
|
|
519
|
+
} else {
|
|
520
|
+
let bundle: URL = self.getBundleDirectory(id: next!)
|
|
521
|
+
if (!bundle.exist) {
|
|
522
|
+
return false
|
|
523
|
+
}
|
|
524
|
+
UserDefaults.standard.set(next, forKey: self.NEXT_VERSION)
|
|
525
|
+
self.setBundleStatus(id: next!, status: BundleStatus.PENDING);
|
|
526
|
+
}
|
|
527
|
+
UserDefaults.standard.synchronize()
|
|
528
|
+
return true
|
|
529
|
+
}
|
|
277
530
|
}
|
|
@@ -11,8 +11,10 @@ CAP_PLUGIN(CapacitorUpdaterPlugin, "CapacitorUpdater",
|
|
|
11
11
|
CAP_PLUGIN_METHOD(reset, CAPPluginReturnPromise);
|
|
12
12
|
CAP_PLUGIN_METHOD(current, CAPPluginReturnPromise);
|
|
13
13
|
CAP_PLUGIN_METHOD(reload, CAPPluginReturnPromise);
|
|
14
|
-
CAP_PLUGIN_METHOD(versionName, CAPPluginReturnPromise);
|
|
15
14
|
CAP_PLUGIN_METHOD(notifyAppReady, CAPPluginReturnPromise);
|
|
16
|
-
CAP_PLUGIN_METHOD(
|
|
15
|
+
CAP_PLUGIN_METHOD(setDelay, CAPPluginReturnPromise);
|
|
17
16
|
CAP_PLUGIN_METHOD(getId, CAPPluginReturnPromise);
|
|
17
|
+
CAP_PLUGIN_METHOD(getPluginVersion, CAPPluginReturnPromise);
|
|
18
|
+
CAP_PLUGIN_METHOD(next, CAPPluginReturnPromise);
|
|
19
|
+
CAP_PLUGIN_METHOD(isAutoUpdateEnabled, CAPPluginReturnPromise);
|
|
18
20
|
)
|