@capgo/capacitor-updater 7.13.5 → 7.13.15
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 +3 -3
- package/android/build.gradle +2 -2
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +2 -2
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +4 -4
- package/android/src/main/java/ee/forgr/capacitor_updater/{CryptoCipherV2.java → CryptoCipher.java} +7 -7
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +3 -3
- package/package.json +3 -4
- package/ios/Plugin/AES.swift +0 -69
- package/ios/Plugin/BigInt.swift +0 -55
- package/ios/Plugin/BundleInfo.swift +0 -113
- package/ios/Plugin/BundleStatus.swift +0 -48
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +0 -1182
- package/ios/Plugin/CapgoUpdater.swift +0 -1301
- package/ios/Plugin/CryptoCipherV2.swift +0 -187
- package/ios/Plugin/DelayCondition.swift +0 -74
- package/ios/Plugin/DelayUntilNext.swift +0 -30
- package/ios/Plugin/DelayUpdateUtils.swift +0 -222
- package/ios/Plugin/Info.plist +0 -28
- package/ios/Plugin/InternalUtils.swift +0 -303
- package/ios/Plugin/Logger.swift +0 -310
- package/ios/Plugin/RSA.swift +0 -274
- package/ios/Plugin/ShakeMenu.swift +0 -112
- package/ios/Plugin/UserDefaultsExtension.swift +0 -46
|
@@ -1,187 +0,0 @@
|
|
|
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
|
-
|
|
7
|
-
import Foundation
|
|
8
|
-
import CryptoKit
|
|
9
|
-
import BigInt
|
|
10
|
-
|
|
11
|
-
public struct CryptoCipherV2 {
|
|
12
|
-
private static var logger: Logger!
|
|
13
|
-
|
|
14
|
-
public static func setLogger(_ logger: Logger) {
|
|
15
|
-
self.logger = logger
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
public static func decryptChecksum(checksum: String, publicKey: String) throws -> String {
|
|
19
|
-
if publicKey.isEmpty {
|
|
20
|
-
logger.info("No encryption set (public key) ignored")
|
|
21
|
-
return checksum
|
|
22
|
-
}
|
|
23
|
-
do {
|
|
24
|
-
guard let checksumBytes = Data(base64Encoded: checksum) else {
|
|
25
|
-
logger.error("Cannot decode checksum as base64: \(checksum)")
|
|
26
|
-
throw CustomError.cannotDecode
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if checksumBytes.isEmpty {
|
|
30
|
-
logger.error("Decoded checksum is empty")
|
|
31
|
-
throw CustomError.cannotDecode
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
guard let rsaPublicKey = RSAPublicKey.load(rsaPublicKey: publicKey) else {
|
|
35
|
-
logger.error("The public key is not a valid RSA Public key")
|
|
36
|
-
throw CustomError.cannotDecode
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
guard let decryptedChecksum = rsaPublicKey.decrypt(data: checksumBytes) else {
|
|
40
|
-
logger.error("decryptChecksum fail")
|
|
41
|
-
throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return decryptedChecksum.base64EncodedString()
|
|
45
|
-
} catch {
|
|
46
|
-
logger.error("decryptChecksum fail: \(error.localizedDescription)")
|
|
47
|
-
throw CustomError.cannotDecode
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
public static func calcChecksum(filePath: URL) -> String {
|
|
51
|
-
let bufferSize = 1024 * 1024 * 5 // 5 MB
|
|
52
|
-
var sha256 = SHA256()
|
|
53
|
-
|
|
54
|
-
do {
|
|
55
|
-
let fileHandle: FileHandle
|
|
56
|
-
do {
|
|
57
|
-
fileHandle = try FileHandle(forReadingFrom: filePath)
|
|
58
|
-
} catch {
|
|
59
|
-
logger.error("Cannot open file for checksum: \(filePath.path) \(error)")
|
|
60
|
-
return ""
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
defer {
|
|
64
|
-
do {
|
|
65
|
-
try fileHandle.close()
|
|
66
|
-
} catch {
|
|
67
|
-
logger.error("Error closing file: \(error)")
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
while autoreleasepool(invoking: {
|
|
72
|
-
let fileData: Data
|
|
73
|
-
do {
|
|
74
|
-
if #available(iOS 13.4, *) {
|
|
75
|
-
fileData = try fileHandle.read(upToCount: bufferSize) ?? Data()
|
|
76
|
-
} else {
|
|
77
|
-
fileData = fileHandle.readData(ofLength: bufferSize)
|
|
78
|
-
}
|
|
79
|
-
} catch {
|
|
80
|
-
logger.error("Error reading file: \(error)")
|
|
81
|
-
return false
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if fileData.count > 0 {
|
|
85
|
-
sha256.update(data: fileData)
|
|
86
|
-
return true // Continue
|
|
87
|
-
} else {
|
|
88
|
-
return false // End of file
|
|
89
|
-
}
|
|
90
|
-
}) {}
|
|
91
|
-
|
|
92
|
-
let digest = sha256.finalize()
|
|
93
|
-
return digest.compactMap { String(format: "%02x", $0) }.joined()
|
|
94
|
-
} catch {
|
|
95
|
-
logger.error("Cannot get checksum: \(filePath.path) \(error)")
|
|
96
|
-
return ""
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
public static func decryptFile(filePath: URL, publicKey: String, sessionKey: String, version: String) throws {
|
|
101
|
-
if publicKey.isEmpty || sessionKey.isEmpty || sessionKey.components(separatedBy: ":").count != 2 {
|
|
102
|
-
logger.info("Encryption not set, no public key or seesion, ignored")
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if !publicKey.hasPrefix("-----BEGIN RSA PUBLIC KEY-----") {
|
|
107
|
-
logger.error("The public key is not a valid RSA Public key")
|
|
108
|
-
return
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
do {
|
|
112
|
-
guard let rsaPublicKey = RSAPublicKey.load(rsaPublicKey: publicKey) else {
|
|
113
|
-
logger.error("The public key is not a valid RSA Public key")
|
|
114
|
-
throw CustomError.cannotDecode
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
let sessionKeyComponents = sessionKey.components(separatedBy: ":")
|
|
118
|
-
let ivBase64 = sessionKeyComponents[0]
|
|
119
|
-
let encryptedKeyBase64 = sessionKeyComponents[1]
|
|
120
|
-
|
|
121
|
-
guard let ivData = Data(base64Encoded: ivBase64) else {
|
|
122
|
-
logger.error("Cannot decode sessionKey IV \(ivBase64)")
|
|
123
|
-
throw CustomError.cannotDecode
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if ivData.count != 16 {
|
|
127
|
-
logger.error("IV data has invalid length: \(ivData.count), expected 16")
|
|
128
|
-
throw CustomError.cannotDecode
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
guard let sessionKeyDataEncrypted = Data(base64Encoded: encryptedKeyBase64) else {
|
|
132
|
-
logger.error("Cannot decode sessionKey data \(encryptedKeyBase64)")
|
|
133
|
-
throw NSError(domain: "Invalid session key data", code: 1, userInfo: nil)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
guard let sessionKeyDataDecrypted = rsaPublicKey.decrypt(data: sessionKeyDataEncrypted) else {
|
|
137
|
-
logger.error("Failed to decrypt session key data")
|
|
138
|
-
throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if sessionKeyDataDecrypted.count != 16 {
|
|
142
|
-
logger.error("Decrypted session key has invalid length: \(sessionKeyDataDecrypted.count), expected 16")
|
|
143
|
-
throw NSError(domain: "Invalid decrypted session key", code: 5, userInfo: nil)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
let aesPrivateKey = AES128Key(iv: ivData, aes128Key: sessionKeyDataDecrypted, logger: logger)
|
|
147
|
-
|
|
148
|
-
let encryptedData: Data
|
|
149
|
-
do {
|
|
150
|
-
encryptedData = try Data(contentsOf: filePath)
|
|
151
|
-
} catch {
|
|
152
|
-
logger.error("Failed to read encrypted data: \(error)")
|
|
153
|
-
throw NSError(domain: "Failed to read encrypted data", code: 3, userInfo: nil)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if encryptedData.isEmpty {
|
|
157
|
-
logger.error("Encrypted file data is empty")
|
|
158
|
-
throw NSError(domain: "Empty encrypted data", code: 6, userInfo: nil)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
guard let decryptedData = aesPrivateKey.decrypt(data: encryptedData) else {
|
|
162
|
-
logger.error("Failed to decrypt data")
|
|
163
|
-
throw NSError(domain: "Failed to decrypt data", code: 4, userInfo: nil)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if decryptedData.isEmpty {
|
|
167
|
-
logger.error("Decrypted data is empty")
|
|
168
|
-
throw NSError(domain: "Empty decrypted data", code: 7, userInfo: nil)
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
do {
|
|
172
|
-
try decryptedData.write(to: filePath, options: .atomic)
|
|
173
|
-
if !FileManager.default.fileExists(atPath: filePath.path) {
|
|
174
|
-
logger.error("File was not created after write")
|
|
175
|
-
throw NSError(domain: "File write failed", code: 8, userInfo: nil)
|
|
176
|
-
}
|
|
177
|
-
} catch {
|
|
178
|
-
logger.error("Error writing decrypted file: \(error)")
|
|
179
|
-
throw error
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
} catch {
|
|
183
|
-
logger.error("decryptFile fail")
|
|
184
|
-
throw CustomError.cannotDecode
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
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
|
-
|
|
7
|
-
//
|
|
8
|
-
// DelayCondition.swift
|
|
9
|
-
// Plugin
|
|
10
|
-
//
|
|
11
|
-
// Created by Luca Peruzzo on 12/09/22.
|
|
12
|
-
// Copyright © 2022 Capgo. All rights reserved.
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
import Foundation
|
|
16
|
-
|
|
17
|
-
private func delayUntilNextValue(value: String) -> DelayUntilNext {
|
|
18
|
-
switch value {
|
|
19
|
-
case "background": return .background
|
|
20
|
-
case "kill": return .kill
|
|
21
|
-
case "nativeVersion": return .nativeVersion
|
|
22
|
-
case "date": return .date
|
|
23
|
-
default:
|
|
24
|
-
return .background
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
@objc public class DelayCondition: NSObject, Decodable, Encodable {
|
|
29
|
-
private let kind: DelayUntilNext
|
|
30
|
-
private let value: String?
|
|
31
|
-
|
|
32
|
-
convenience init(kind: String, value: String?) {
|
|
33
|
-
self.init(kind: delayUntilNextValue(value: kind), value: value)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
init(kind: DelayUntilNext, value: String?) {
|
|
37
|
-
self.kind = kind
|
|
38
|
-
self.value = value
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
public required init(from decoder: Decoder) throws {
|
|
42
|
-
let values: KeyedDecodingContainer<DelayCondition.CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
|
|
43
|
-
kind = try values.decode(DelayUntilNext.self, forKey: .kind)
|
|
44
|
-
value = try values.decode(String.self, forKey: .value)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
enum CodingKeys: String, CodingKey {
|
|
48
|
-
case kind, value
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
public func getKind() -> String {
|
|
52
|
-
return self.kind.description
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
public func getValue() -> String? {
|
|
56
|
-
return self.value
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public func toJSON() -> [String: String] {
|
|
60
|
-
return [
|
|
61
|
-
"kind": self.getKind(),
|
|
62
|
-
"value": self.getValue() ?? ""
|
|
63
|
-
]
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public static func == (lhs: DelayCondition, rhs: DelayCondition) -> Bool {
|
|
67
|
-
return lhs.getKind() == rhs.getKind() && lhs.getValue() == rhs.getValue()
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
public func toString() -> String {
|
|
71
|
-
return "{ \"kind\": \"\(self.getKind())\", \"value\": \"\(self.getValue() ?? "")\"}"
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
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
|
-
|
|
7
|
-
//
|
|
8
|
-
// DelayUntilNext.swift
|
|
9
|
-
// Plugin
|
|
10
|
-
//
|
|
11
|
-
// Created by Luca Peruzzo on 12/09/22.
|
|
12
|
-
// Copyright © 2022 Capgo. All rights reserved.
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
import Foundation
|
|
16
|
-
enum DelayUntilNext: Decodable, Encodable, CustomStringConvertible {
|
|
17
|
-
case background
|
|
18
|
-
case kill
|
|
19
|
-
case nativeVersion
|
|
20
|
-
case date
|
|
21
|
-
|
|
22
|
-
var description: String {
|
|
23
|
-
switch self {
|
|
24
|
-
case .background: return "background"
|
|
25
|
-
case .kill: return "kill"
|
|
26
|
-
case .nativeVersion: return "nativeVersion"
|
|
27
|
-
case .date: return "date"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,222 +0,0 @@
|
|
|
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
|
-
|
|
7
|
-
//
|
|
8
|
-
// DelayUpdateUtils.swift
|
|
9
|
-
// Plugin
|
|
10
|
-
//
|
|
11
|
-
// Created by Auto-generated based on Android implementation
|
|
12
|
-
// Copyright © 2024 Capgo. All rights reserved.
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
import Foundation
|
|
16
|
-
import Version
|
|
17
|
-
|
|
18
|
-
public class DelayUpdateUtils {
|
|
19
|
-
|
|
20
|
-
static let DELAY_CONDITION_PREFERENCES = "DELAY_CONDITION_PREFERENCES_CAPGO"
|
|
21
|
-
static let BACKGROUND_TIMESTAMP_KEY = "BACKGROUND_TIMESTAMP_KEY_CAPGO"
|
|
22
|
-
private let logger: Logger
|
|
23
|
-
|
|
24
|
-
private let currentVersionNative: Version
|
|
25
|
-
private let installNext: () -> Void
|
|
26
|
-
|
|
27
|
-
public enum CancelDelaySource {
|
|
28
|
-
case killed
|
|
29
|
-
case background
|
|
30
|
-
case foreground
|
|
31
|
-
|
|
32
|
-
var description: String {
|
|
33
|
-
switch self {
|
|
34
|
-
case .killed: return "KILLED"
|
|
35
|
-
case .background: return "BACKGROUND"
|
|
36
|
-
case .foreground: return "FOREGROUND"
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
public init(currentVersionNative: Version, installNext: @escaping () -> Void, logger: Logger) {
|
|
42
|
-
self.currentVersionNative = currentVersionNative
|
|
43
|
-
self.installNext = installNext
|
|
44
|
-
self.logger = logger
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
public func checkCancelDelay(source: CancelDelaySource) {
|
|
48
|
-
let delayUpdatePreferences = UserDefaults.standard.string(forKey: DelayUpdateUtils.DELAY_CONDITION_PREFERENCES) ?? "[]"
|
|
49
|
-
let delayConditionList: [DelayCondition] = fromJsonArr(json: delayUpdatePreferences).map { obj -> DelayCondition in
|
|
50
|
-
let kind: String = obj.value(forKey: "kind") as! String
|
|
51
|
-
let value: String? = obj.value(forKey: "value") as? String
|
|
52
|
-
return DelayCondition(kind: kind, value: value)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
var delayConditionListToKeep: [DelayCondition] = []
|
|
56
|
-
var index = 0
|
|
57
|
-
|
|
58
|
-
for condition in delayConditionList {
|
|
59
|
-
let kind = condition.getKind()
|
|
60
|
-
let value = condition.getValue()
|
|
61
|
-
|
|
62
|
-
switch kind {
|
|
63
|
-
case "background":
|
|
64
|
-
if source == .foreground {
|
|
65
|
-
let backgroundedAt = getBackgroundTimestamp()
|
|
66
|
-
let now = Int64(Date().timeIntervalSince1970 * 1000) // Convert to milliseconds
|
|
67
|
-
let delta = max(0, now - backgroundedAt)
|
|
68
|
-
|
|
69
|
-
var longValue: Int64 = 0
|
|
70
|
-
if let value = value, !value.isEmpty {
|
|
71
|
-
longValue = Int64(value) ?? 0
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if delta > longValue {
|
|
75
|
-
logger.info("Background condition (value: \(value ?? "")) deleted at index \(index). Delta: \(delta), longValue: \(longValue)")
|
|
76
|
-
} else {
|
|
77
|
-
delayConditionListToKeep.append(condition)
|
|
78
|
-
logger.info("Background delay (value: \(value ?? "")) condition kept at index \(index) (source: \(source.description))")
|
|
79
|
-
}
|
|
80
|
-
} else {
|
|
81
|
-
delayConditionListToKeep.append(condition)
|
|
82
|
-
logger.info("Background delay (value: \(value ?? "")) condition kept at index \(index) (source: \(source.description))")
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
case "kill":
|
|
86
|
-
if source == .killed {
|
|
87
|
-
self.installNext()
|
|
88
|
-
} else {
|
|
89
|
-
delayConditionListToKeep.append(condition)
|
|
90
|
-
logger.info("Kill delay (value: \(value ?? "")) condition kept at index \(index) (source: \(source.description))")
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
case "date":
|
|
94
|
-
if let value = value, !value.isEmpty {
|
|
95
|
-
do {
|
|
96
|
-
let dateFormatter = ISO8601DateFormatter()
|
|
97
|
-
dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
98
|
-
|
|
99
|
-
if let date = dateFormatter.date(from: value) {
|
|
100
|
-
if Date() > date {
|
|
101
|
-
logger.info("Date delay (value: \(value)) condition removed due to expired date at index \(index)")
|
|
102
|
-
} else {
|
|
103
|
-
delayConditionListToKeep.append(condition)
|
|
104
|
-
logger.info("Date delay (value: \(value)) condition kept at index \(index)")
|
|
105
|
-
}
|
|
106
|
-
} else {
|
|
107
|
-
logger.error("Date delay (value: \(value)) condition removed due to parsing issue at index \(index)")
|
|
108
|
-
}
|
|
109
|
-
} catch {
|
|
110
|
-
logger.error("Date delay (value: \(value)) condition removed due to parsing issue at index \(index): \(error)")
|
|
111
|
-
}
|
|
112
|
-
} else {
|
|
113
|
-
logger.error("Date delay (value: \(value ?? "")) condition removed due to empty value at index \(index)")
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
case "nativeVersion":
|
|
117
|
-
if let value = value, !value.isEmpty {
|
|
118
|
-
do {
|
|
119
|
-
let versionLimit = try Version(value)
|
|
120
|
-
if currentVersionNative >= versionLimit {
|
|
121
|
-
logger.info("Native version delay (value: \(value)) condition removed due to above limit at index \(index)")
|
|
122
|
-
} else {
|
|
123
|
-
delayConditionListToKeep.append(condition)
|
|
124
|
-
logger.info("Native version delay (value: \(value)) condition kept at index \(index)")
|
|
125
|
-
}
|
|
126
|
-
} catch {
|
|
127
|
-
logger.error("Native version delay (value: \(value)) condition removed due to parsing issue at index \(index): \(error)")
|
|
128
|
-
}
|
|
129
|
-
} else {
|
|
130
|
-
logger.error("Native version delay (value: \(value ?? "")) condition removed due to empty value at index \(index)")
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
default:
|
|
134
|
-
logger.error("Unknown delay condition kind: \(kind) at index \(index)")
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
index += 1
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if !delayConditionListToKeep.isEmpty {
|
|
141
|
-
let json = toJson(object: delayConditionListToKeep.map { $0.toJSON() })
|
|
142
|
-
_ = setMultiDelay(delayConditions: json)
|
|
143
|
-
} else {
|
|
144
|
-
// Clear all delay conditions if none are left to keep
|
|
145
|
-
_ = cancelDelay(source: "checkCancelDelay")
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
public func setMultiDelay(delayConditions: String) -> Bool {
|
|
150
|
-
do {
|
|
151
|
-
UserDefaults.standard.set(delayConditions, forKey: DelayUpdateUtils.DELAY_CONDITION_PREFERENCES)
|
|
152
|
-
UserDefaults.standard.synchronize()
|
|
153
|
-
logger.info("Delay update saved")
|
|
154
|
-
return true
|
|
155
|
-
} catch {
|
|
156
|
-
logger.error("Failed to delay update, [Error calling 'setMultiDelay()']: \(error)")
|
|
157
|
-
return false
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
public func setBackgroundTimestamp(_ backgroundTimestamp: Int64) {
|
|
162
|
-
do {
|
|
163
|
-
UserDefaults.standard.set(backgroundTimestamp, forKey: DelayUpdateUtils.BACKGROUND_TIMESTAMP_KEY)
|
|
164
|
-
UserDefaults.standard.synchronize()
|
|
165
|
-
logger.info("Background timestamp saved")
|
|
166
|
-
} catch {
|
|
167
|
-
logger.error("Failed to save background timestamp, [Error calling 'setBackgroundTimestamp()']: \(error)")
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
public func unsetBackgroundTimestamp() {
|
|
172
|
-
do {
|
|
173
|
-
UserDefaults.standard.removeObject(forKey: DelayUpdateUtils.BACKGROUND_TIMESTAMP_KEY)
|
|
174
|
-
UserDefaults.standard.synchronize()
|
|
175
|
-
logger.info("Background timestamp removed")
|
|
176
|
-
} catch {
|
|
177
|
-
logger.error("Failed to remove background timestamp, [Error calling 'unsetBackgroundTimestamp()']: \(error)")
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
private func getBackgroundTimestamp() -> Int64 {
|
|
182
|
-
do {
|
|
183
|
-
let timestamp = UserDefaults.standard.object(forKey: DelayUpdateUtils.BACKGROUND_TIMESTAMP_KEY) as? Int64 ?? 0
|
|
184
|
-
return timestamp
|
|
185
|
-
} catch {
|
|
186
|
-
logger.error("Failed to get background timestamp, [Error calling 'getBackgroundTimestamp()']: \(error)")
|
|
187
|
-
return 0
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
public func cancelDelay(source: String) -> Bool {
|
|
192
|
-
do {
|
|
193
|
-
UserDefaults.standard.removeObject(forKey: DelayUpdateUtils.DELAY_CONDITION_PREFERENCES)
|
|
194
|
-
UserDefaults.standard.synchronize()
|
|
195
|
-
logger.info("All delays canceled from \(source)")
|
|
196
|
-
return true
|
|
197
|
-
} catch {
|
|
198
|
-
logger.error("Failed to cancel update delay: \(error)")
|
|
199
|
-
return false
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// MARK: - Helper methods
|
|
204
|
-
|
|
205
|
-
private func toJson(object: Any) -> String {
|
|
206
|
-
guard let data = try? JSONSerialization.data(withJSONObject: object, options: []) else {
|
|
207
|
-
return ""
|
|
208
|
-
}
|
|
209
|
-
return String(data: data, encoding: String.Encoding.utf8) ?? ""
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
private func fromJsonArr(json: String) -> [NSObject] {
|
|
213
|
-
guard let jsonData = json.data(using: .utf8) else {
|
|
214
|
-
return []
|
|
215
|
-
}
|
|
216
|
-
let object = try? JSONSerialization.jsonObject(
|
|
217
|
-
with: jsonData,
|
|
218
|
-
options: .mutableContainers
|
|
219
|
-
) as? [NSObject]
|
|
220
|
-
return object ?? []
|
|
221
|
-
}
|
|
222
|
-
}
|
package/ios/Plugin/Info.plist
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
-
<plist version="1.0">
|
|
4
|
-
<dict>
|
|
5
|
-
<key>CFBundleDevelopmentRegion</key>
|
|
6
|
-
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
7
|
-
<key>CFBundleExecutable</key>
|
|
8
|
-
<string>$(EXECUTABLE_NAME)</string>
|
|
9
|
-
<key>CFBundleIdentifier</key>
|
|
10
|
-
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
11
|
-
<key>CFBundleInfoDictionaryVersion</key>
|
|
12
|
-
<string>6.0</string>
|
|
13
|
-
<key>CFBundleName</key>
|
|
14
|
-
<string>$(PRODUCT_NAME)</string>
|
|
15
|
-
<key>CFBundlePackageType</key>
|
|
16
|
-
<string>FMWK</string>
|
|
17
|
-
<key>CFBundleShortVersionString</key>
|
|
18
|
-
<string>1.0</string>
|
|
19
|
-
<key>CFBundleVersion</key>
|
|
20
|
-
<string>$(CURRENT_PROJECT_VERSION)</string>
|
|
21
|
-
<key>NSPrincipalClass</key>
|
|
22
|
-
<string></string>
|
|
23
|
-
<key>UIFileSharingEnabled</key>
|
|
24
|
-
<true/>
|
|
25
|
-
<key>LSSupportsOpeningDocumentsInPlace</key>
|
|
26
|
-
<true/>
|
|
27
|
-
</dict>
|
|
28
|
-
</plist>
|