@capgo/capacitor-updater 6.14.26 → 6.14.29
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 +3 -2
- package/Package.swift +2 -2
- package/README.md +341 -74
- package/android/build.gradle +20 -8
- package/android/proguard-rules.pro +22 -5
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +52 -16
- package/android/src/main/java/ee/forgr/capacitor_updater/Callback.java +2 -2
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1196 -508
- package/android/src/main/java/ee/forgr/capacitor_updater/{CapacitorUpdater.java → CapgoUpdater.java} +522 -154
- package/android/src/main/java/ee/forgr/capacitor_updater/{CryptoCipher.java → CryptoCipherV1.java} +17 -9
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipherV2.java +15 -26
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayCondition.java +0 -3
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayUpdateUtils.java +260 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +300 -119
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +63 -25
- package/android/src/main/java/ee/forgr/capacitor_updater/Logger.java +338 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeDetector.java +72 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeMenu.java +169 -0
- package/dist/docs.json +652 -63
- package/dist/esm/definitions.d.ts +265 -15
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/history.d.ts +1 -0
- package/dist/esm/history.js +283 -0
- package/dist/esm/history.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +12 -1
- package/dist/esm/web.js +29 -2
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +311 -2
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +311 -2
- package/dist/plugin.js.map +1 -1
- package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/AES.swift +6 -3
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +1575 -0
- package/ios/{Plugin/CapacitorUpdater.swift → Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift} +365 -139
- package/ios/{Plugin/CryptoCipher.swift → Sources/CapacitorUpdaterPlugin/CryptoCipherV1.swift} +13 -6
- package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/CryptoCipherV2.swift +33 -27
- package/ios/Sources/CapacitorUpdaterPlugin/DelayUpdateUtils.swift +220 -0
- package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/InternalUtils.swift +47 -0
- package/ios/Sources/CapacitorUpdaterPlugin/Logger.swift +310 -0
- package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/RSA.swift +1 -0
- package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +112 -0
- package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/UserDefaultsExtension.swift +0 -2
- package/package.json +20 -16
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +0 -1030
- /package/{LICENCE → LICENSE} +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/BigInt.swift +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/BundleInfo.swift +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/BundleStatus.swift +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/DelayCondition.swift +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/DelayUntilNext.swift +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/Info.plist +0 -0
package/android/src/main/java/ee/forgr/capacitor_updater/{CryptoCipher.java → CryptoCipherV1.java}
RENAMED
|
@@ -10,6 +10,8 @@ package ee.forgr.capacitor_updater;
|
|
|
10
10
|
* Created by Awesometic
|
|
11
11
|
* It's encrypt returns Base64 encoded, and also decrypt for Base64 encoded cipher
|
|
12
12
|
* references: http://stackoverflow.com/questions/12471999/rsa-encryption-decryption-in-android
|
|
13
|
+
*
|
|
14
|
+
* V1 Encryption - uses privateKey (deprecated but kept for backwards compatibility)
|
|
13
15
|
*/
|
|
14
16
|
import android.util.Base64;
|
|
15
17
|
import android.util.Log;
|
|
@@ -41,7 +43,13 @@ import javax.crypto.spec.OAEPParameterSpec;
|
|
|
41
43
|
import javax.crypto.spec.PSource;
|
|
42
44
|
import javax.crypto.spec.SecretKeySpec;
|
|
43
45
|
|
|
44
|
-
public class
|
|
46
|
+
public class CryptoCipherV1 {
|
|
47
|
+
|
|
48
|
+
private static Logger logger;
|
|
49
|
+
|
|
50
|
+
public static void setLogger(Logger loggerInstance) {
|
|
51
|
+
logger = loggerInstance;
|
|
52
|
+
}
|
|
45
53
|
|
|
46
54
|
public static byte[] decryptRSA(byte[] source, PrivateKey privateKey)
|
|
47
55
|
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
|
@@ -145,10 +153,10 @@ public class CryptoCipher {
|
|
|
145
153
|
throws IOException {
|
|
146
154
|
// (str != null && !str.isEmpty())
|
|
147
155
|
if (privateKey == null || privateKey.isEmpty()) {
|
|
148
|
-
Log.i(
|
|
156
|
+
Log.i("[Capacitor-updater]", "Cannot found privateKey");
|
|
149
157
|
return;
|
|
150
158
|
} else if (ivSessionKey == null || ivSessionKey.isEmpty() || ivSessionKey.split(":").length != 2) {
|
|
151
|
-
Log.i(
|
|
159
|
+
Log.i("[Capacitor-updater]", "Cannot found sessionKey");
|
|
152
160
|
return;
|
|
153
161
|
}
|
|
154
162
|
try {
|
|
@@ -156,9 +164,9 @@ public class CryptoCipher {
|
|
|
156
164
|
String sessionKeyB64 = ivSessionKey.split(":")[1];
|
|
157
165
|
byte[] iv = Base64.decode(ivB64.getBytes(), Base64.DEFAULT);
|
|
158
166
|
byte[] sessionKey = Base64.decode(sessionKeyB64.getBytes(), Base64.DEFAULT);
|
|
159
|
-
PrivateKey pKey =
|
|
160
|
-
byte[] decryptedSessionKey =
|
|
161
|
-
SecretKey sKey =
|
|
167
|
+
PrivateKey pKey = CryptoCipherV1.stringToPrivateKey(privateKey);
|
|
168
|
+
byte[] decryptedSessionKey = CryptoCipherV1.decryptRSA(sessionKey, pKey);
|
|
169
|
+
SecretKey sKey = CryptoCipherV1.byteToSessionKey(decryptedSessionKey);
|
|
162
170
|
byte[] content = new byte[(int) file.length()];
|
|
163
171
|
|
|
164
172
|
try (
|
|
@@ -168,14 +176,14 @@ public class CryptoCipher {
|
|
|
168
176
|
) {
|
|
169
177
|
dis.readFully(content);
|
|
170
178
|
dis.close();
|
|
171
|
-
byte[] decrypted =
|
|
179
|
+
byte[] decrypted = CryptoCipherV1.decryptAES(content, sKey, iv);
|
|
172
180
|
// write the decrypted string to the file
|
|
173
181
|
try (final FileOutputStream fos = new FileOutputStream(file.getAbsolutePath())) {
|
|
174
182
|
fos.write(decrypted);
|
|
175
183
|
}
|
|
176
184
|
}
|
|
177
185
|
} catch (GeneralSecurityException e) {
|
|
178
|
-
Log.i(
|
|
186
|
+
Log.i("[Capacitor-updater]", "decryptFile fail");
|
|
179
187
|
e.printStackTrace();
|
|
180
188
|
throw new IOException("GeneralSecurityException");
|
|
181
189
|
}
|
|
@@ -193,7 +201,7 @@ public class CryptoCipher {
|
|
|
193
201
|
}
|
|
194
202
|
return String.format("%08x", crc.getValue());
|
|
195
203
|
} catch (IOException e) {
|
|
196
|
-
System.err.println(
|
|
204
|
+
System.err.println("[Capacitor-updater]" + " Cannot calc checksum: " + file.getPath() + " " + e.getMessage());
|
|
197
205
|
return "";
|
|
198
206
|
}
|
|
199
207
|
}
|
|
@@ -10,9 +10,10 @@ package ee.forgr.capacitor_updater;
|
|
|
10
10
|
* Created by Awesometic
|
|
11
11
|
* It's encrypt returns Base64 encoded, and also decrypt for Base64 encoded cipher
|
|
12
12
|
* references: http://stackoverflow.com/questions/12471999/rsa-encryption-decryption-in-android
|
|
13
|
+
*
|
|
14
|
+
* V2 Encryption - uses publicKey (modern encryption from main branch)
|
|
13
15
|
*/
|
|
14
16
|
import android.util.Base64;
|
|
15
|
-
import android.util.Log;
|
|
16
17
|
import java.io.BufferedInputStream;
|
|
17
18
|
import java.io.DataInputStream;
|
|
18
19
|
import java.io.File;
|
|
@@ -38,6 +39,12 @@ import javax.crypto.spec.SecretKeySpec;
|
|
|
38
39
|
|
|
39
40
|
public class CryptoCipherV2 {
|
|
40
41
|
|
|
42
|
+
private static Logger logger;
|
|
43
|
+
|
|
44
|
+
public static void setLogger(Logger loggerInstance) {
|
|
45
|
+
logger = loggerInstance;
|
|
46
|
+
}
|
|
47
|
+
|
|
41
48
|
public static byte[] decryptRSA(byte[] source, PublicKey publicKey)
|
|
42
49
|
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
|
43
50
|
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
|
@@ -135,11 +142,11 @@ public class CryptoCipherV2 {
|
|
|
135
142
|
|
|
136
143
|
public static void decryptFile(final File file, final String publicKey, final String ivSessionKey) throws IOException {
|
|
137
144
|
if (publicKey.isEmpty() || ivSessionKey == null || ivSessionKey.isEmpty() || ivSessionKey.split(":").length != 2) {
|
|
138
|
-
|
|
145
|
+
logger.info("Encryption not set, no public key or seesion, ignored");
|
|
139
146
|
return;
|
|
140
147
|
}
|
|
141
148
|
if (!publicKey.startsWith("-----BEGIN RSA PUBLIC KEY-----")) {
|
|
142
|
-
|
|
149
|
+
logger.error("The public key is not a valid RSA Public key");
|
|
143
150
|
return;
|
|
144
151
|
}
|
|
145
152
|
|
|
@@ -168,7 +175,7 @@ public class CryptoCipherV2 {
|
|
|
168
175
|
}
|
|
169
176
|
}
|
|
170
177
|
} catch (GeneralSecurityException e) {
|
|
171
|
-
|
|
178
|
+
logger.info("decryptFile fail");
|
|
172
179
|
e.printStackTrace();
|
|
173
180
|
throw new IOException("GeneralSecurityException");
|
|
174
181
|
}
|
|
@@ -176,25 +183,7 @@ public class CryptoCipherV2 {
|
|
|
176
183
|
|
|
177
184
|
public static String decryptChecksum(String checksum, String publicKey) throws IOException {
|
|
178
185
|
if (publicKey.isEmpty()) {
|
|
179
|
-
|
|
180
|
-
return checksum;
|
|
181
|
-
}
|
|
182
|
-
try {
|
|
183
|
-
byte[] checksumBytes = Base64.decode(checksum, Base64.DEFAULT);
|
|
184
|
-
PublicKey pKey = CryptoCipherV2.stringToPublicKey(publicKey);
|
|
185
|
-
byte[] decryptedChecksum = CryptoCipherV2.decryptRSA(checksumBytes, pKey);
|
|
186
|
-
// return Base64.encodeToString(decryptedChecksum, Base64.DEFAULT);
|
|
187
|
-
String result = Base64.encodeToString(decryptedChecksum, Base64.DEFAULT);
|
|
188
|
-
return result.replaceAll("\\s", ""); // Remove all whitespace, including newlines
|
|
189
|
-
} catch (GeneralSecurityException e) {
|
|
190
|
-
Log.e(CapacitorUpdater.TAG, "decryptChecksum fail: " + e.getMessage());
|
|
191
|
-
throw new IOException("Decryption failed: " + e.getMessage());
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
public static String decryptChecksum(String checksum, String publicKey, String version) throws IOException {
|
|
196
|
-
if (publicKey.isEmpty()) {
|
|
197
|
-
Log.e(CapacitorUpdater.TAG, "The public key is empty");
|
|
186
|
+
logger.error("No encryption set (public key) ignored");
|
|
198
187
|
return checksum;
|
|
199
188
|
}
|
|
200
189
|
try {
|
|
@@ -205,7 +194,7 @@ public class CryptoCipherV2 {
|
|
|
205
194
|
String result = Base64.encodeToString(decryptedChecksum, Base64.DEFAULT);
|
|
206
195
|
return result.replaceAll("\\s", ""); // Remove all whitespace, including newlines
|
|
207
196
|
} catch (GeneralSecurityException e) {
|
|
208
|
-
|
|
197
|
+
logger.error("decryptChecksum fail: " + e.getMessage());
|
|
209
198
|
throw new IOException("Decryption failed: " + e.getMessage());
|
|
210
199
|
}
|
|
211
200
|
}
|
|
@@ -216,7 +205,7 @@ public class CryptoCipherV2 {
|
|
|
216
205
|
try {
|
|
217
206
|
digest = MessageDigest.getInstance("SHA-256");
|
|
218
207
|
} catch (java.security.NoSuchAlgorithmException e) {
|
|
219
|
-
|
|
208
|
+
logger.error("SHA-256 algorithm not available");
|
|
220
209
|
return "";
|
|
221
210
|
}
|
|
222
211
|
|
|
@@ -235,7 +224,7 @@ public class CryptoCipherV2 {
|
|
|
235
224
|
}
|
|
236
225
|
return hexString.toString();
|
|
237
226
|
} catch (IOException e) {
|
|
238
|
-
|
|
227
|
+
logger.error("Cannot calc checksum v2: " + file.getPath() + " " + e.getMessage());
|
|
239
228
|
return "";
|
|
240
229
|
}
|
|
241
230
|
}
|
|
@@ -7,15 +7,12 @@
|
|
|
7
7
|
package ee.forgr.capacitor_updater;
|
|
8
8
|
|
|
9
9
|
import androidx.annotation.NonNull;
|
|
10
|
-
import com.google.gson.annotations.SerializedName;
|
|
11
10
|
import java.util.Objects;
|
|
12
11
|
|
|
13
12
|
public class DelayCondition {
|
|
14
13
|
|
|
15
|
-
@SerializedName("kind")
|
|
16
14
|
private DelayUntilNext kind;
|
|
17
15
|
|
|
18
|
-
@SerializedName("value")
|
|
19
16
|
private String value;
|
|
20
17
|
|
|
21
18
|
public DelayCondition(DelayUntilNext kind, String value) {
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
package ee.forgr.capacitor_updater;
|
|
2
|
+
|
|
3
|
+
import android.content.SharedPreferences;
|
|
4
|
+
import io.github.g00fy2.versioncompare.Version;
|
|
5
|
+
import java.text.SimpleDateFormat;
|
|
6
|
+
import java.util.ArrayList;
|
|
7
|
+
import java.util.Date;
|
|
8
|
+
import org.json.JSONArray;
|
|
9
|
+
import org.json.JSONException;
|
|
10
|
+
import org.json.JSONObject;
|
|
11
|
+
|
|
12
|
+
public class DelayUpdateUtils {
|
|
13
|
+
|
|
14
|
+
private final Logger logger;
|
|
15
|
+
|
|
16
|
+
public static final String DELAY_CONDITION_PREFERENCES = "DELAY_CONDITION_PREFERENCES_CAPGO";
|
|
17
|
+
public static final String BACKGROUND_TIMESTAMP_KEY = "BACKGROUND_TIMESTAMP_KEY_CAPGO";
|
|
18
|
+
|
|
19
|
+
private final SharedPreferences prefs;
|
|
20
|
+
private final SharedPreferences.Editor editor;
|
|
21
|
+
private final Version currentVersionNative;
|
|
22
|
+
|
|
23
|
+
public DelayUpdateUtils(SharedPreferences prefs, SharedPreferences.Editor editor, Version currentVersionNative, Logger logger) {
|
|
24
|
+
this.prefs = prefs;
|
|
25
|
+
this.editor = editor;
|
|
26
|
+
this.currentVersionNative = currentVersionNative;
|
|
27
|
+
this.logger = logger;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public enum CancelDelaySource {
|
|
31
|
+
KILLED,
|
|
32
|
+
BACKGROUND,
|
|
33
|
+
FOREGROUND
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public void checkCancelDelay(CancelDelaySource source) {
|
|
37
|
+
String delayUpdatePreferences = prefs.getString(DELAY_CONDITION_PREFERENCES, "[]");
|
|
38
|
+
ArrayList<DelayCondition> delayConditionList = parseDelayConditions(delayUpdatePreferences);
|
|
39
|
+
ArrayList<DelayCondition> delayConditionListToKeep = new ArrayList<>(delayConditionList.size());
|
|
40
|
+
int index = 0;
|
|
41
|
+
|
|
42
|
+
for (DelayCondition condition : delayConditionList) {
|
|
43
|
+
DelayUntilNext kind = condition.getKind();
|
|
44
|
+
String value = condition.getValue();
|
|
45
|
+
switch (kind) {
|
|
46
|
+
case background:
|
|
47
|
+
if (source == CancelDelaySource.FOREGROUND) {
|
|
48
|
+
long backgroundedAt = getBackgroundTimestamp();
|
|
49
|
+
long now = System.currentTimeMillis();
|
|
50
|
+
long delta = Math.max(0, now - backgroundedAt);
|
|
51
|
+
long longValue = 0L;
|
|
52
|
+
try {
|
|
53
|
+
longValue = Long.parseLong(value);
|
|
54
|
+
} catch (NumberFormatException e) {
|
|
55
|
+
logger.error(
|
|
56
|
+
"Background condition (value: " +
|
|
57
|
+
value +
|
|
58
|
+
") had an invalid value at index " +
|
|
59
|
+
index +
|
|
60
|
+
". We will likely remove it."
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (delta > longValue) {
|
|
65
|
+
logger.info(
|
|
66
|
+
"Background condition (value: " +
|
|
67
|
+
value +
|
|
68
|
+
") deleted at index " +
|
|
69
|
+
index +
|
|
70
|
+
". Delta: " +
|
|
71
|
+
delta +
|
|
72
|
+
", longValue: " +
|
|
73
|
+
longValue
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
delayConditionListToKeep.add(condition);
|
|
78
|
+
logger.info(
|
|
79
|
+
"Background delay (value: " +
|
|
80
|
+
value +
|
|
81
|
+
") condition kept at index " +
|
|
82
|
+
index +
|
|
83
|
+
" (source: " +
|
|
84
|
+
source.toString() +
|
|
85
|
+
")"
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
case kill:
|
|
90
|
+
if (source == CancelDelaySource.KILLED) {
|
|
91
|
+
logger.info("Kill delay (value: " + value + ") condition removed at index " + index + " after app kill");
|
|
92
|
+
} else {
|
|
93
|
+
delayConditionListToKeep.add(condition);
|
|
94
|
+
logger.info(
|
|
95
|
+
"Kill delay (value: " + value + ") condition kept at index " + index + " (source: " + source.toString() + ")"
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
case date:
|
|
100
|
+
if (!"".equals(value)) {
|
|
101
|
+
try {
|
|
102
|
+
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
|
103
|
+
Date date = sdf.parse(value);
|
|
104
|
+
assert date != null;
|
|
105
|
+
if (new Date().compareTo(date) > 0) {
|
|
106
|
+
logger.info("Date delay (value: " + value + ") condition removed due to expired date at index " + index);
|
|
107
|
+
} else {
|
|
108
|
+
delayConditionListToKeep.add(condition);
|
|
109
|
+
logger.info("Date delay (value: " + value + ") condition kept at index " + index);
|
|
110
|
+
}
|
|
111
|
+
} catch (final Exception e) {
|
|
112
|
+
logger.error(
|
|
113
|
+
"Date delay (value: " +
|
|
114
|
+
value +
|
|
115
|
+
") condition removed due to parsing issue at index " +
|
|
116
|
+
index +
|
|
117
|
+
" " +
|
|
118
|
+
e.getMessage()
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
logger.debug("Date delay (value: " + value + ") condition removed due to empty value at index " + index);
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
case nativeVersion:
|
|
126
|
+
if (!"".equals(value)) {
|
|
127
|
+
try {
|
|
128
|
+
final Version versionLimit = new Version(value);
|
|
129
|
+
if (this.currentVersionNative.isAtLeast(versionLimit)) {
|
|
130
|
+
logger.info(
|
|
131
|
+
"Native version delay (value: " + value + ") condition removed due to above limit at index " + index
|
|
132
|
+
);
|
|
133
|
+
} else {
|
|
134
|
+
delayConditionListToKeep.add(condition);
|
|
135
|
+
logger.info("Native version delay (value: " + value + ") condition kept at index " + index);
|
|
136
|
+
}
|
|
137
|
+
} catch (final Exception e) {
|
|
138
|
+
logger.error(
|
|
139
|
+
"Native version delay (value: " +
|
|
140
|
+
value +
|
|
141
|
+
") condition removed due to parsing issue at index " +
|
|
142
|
+
index +
|
|
143
|
+
" " +
|
|
144
|
+
e.getMessage()
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
logger.debug("Native version delay (value: " + value + ") condition removed due to empty value at index " + index);
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
index++;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (delayConditionListToKeep.isEmpty()) {
|
|
156
|
+
this.cancelDelay("checkCancelDelay");
|
|
157
|
+
} else {
|
|
158
|
+
this.setMultiDelay(convertDelayConditionsToJson(delayConditionListToKeep));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public ArrayList<DelayCondition> parseDelayConditions(String json) {
|
|
163
|
+
ArrayList<DelayCondition> conditions = new ArrayList<>();
|
|
164
|
+
if (json == null || json.isEmpty()) {
|
|
165
|
+
return conditions;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
JSONArray array = new JSONArray(json);
|
|
169
|
+
for (int i = 0; i < array.length(); i++) {
|
|
170
|
+
JSONObject item = array.optJSONObject(i);
|
|
171
|
+
if (item == null) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
String kindValue = item.optString("kind", "");
|
|
175
|
+
String value = item.optString("value", "");
|
|
176
|
+
if (kindValue.isEmpty()) {
|
|
177
|
+
logger.warn("Delay condition missing kind at index " + i);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
DelayUntilNext kind = DelayUntilNext.valueOf(kindValue);
|
|
182
|
+
conditions.add(new DelayCondition(kind, value));
|
|
183
|
+
} catch (IllegalArgumentException e) {
|
|
184
|
+
logger.warn("Unknown delay condition kind '" + kindValue + "' at index " + i);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
} catch (JSONException e) {
|
|
188
|
+
logger.error("Failed to parse delay conditions: " + e.getMessage());
|
|
189
|
+
}
|
|
190
|
+
return conditions;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private String convertDelayConditionsToJson(ArrayList<DelayCondition> conditions) {
|
|
194
|
+
JSONArray array = new JSONArray();
|
|
195
|
+
for (DelayCondition condition : conditions) {
|
|
196
|
+
try {
|
|
197
|
+
JSONObject obj = new JSONObject();
|
|
198
|
+
obj.put("kind", condition.getKind().name());
|
|
199
|
+
obj.put("value", condition.getValue());
|
|
200
|
+
array.put(obj);
|
|
201
|
+
} catch (JSONException e) {
|
|
202
|
+
logger.error("Failed to serialize delay condition: " + e.getMessage());
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return array.toString();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
public Boolean setMultiDelay(String delayConditions) {
|
|
209
|
+
try {
|
|
210
|
+
this.editor.putString(DELAY_CONDITION_PREFERENCES, delayConditions);
|
|
211
|
+
this.editor.commit();
|
|
212
|
+
logger.info("Delay update saved");
|
|
213
|
+
return true;
|
|
214
|
+
} catch (final Exception e) {
|
|
215
|
+
logger.error("Failed to delay update, [Error calling '_setMultiDelay()'] " + e.getMessage());
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
public void setBackgroundTimestamp(long backgroundTimestamp) {
|
|
221
|
+
try {
|
|
222
|
+
this.editor.putLong(BACKGROUND_TIMESTAMP_KEY, backgroundTimestamp);
|
|
223
|
+
this.editor.commit();
|
|
224
|
+
logger.info("Background timestamp set");
|
|
225
|
+
} catch (final Exception e) {
|
|
226
|
+
logger.error("Failed to delay update, [Error calling '_setBackgroundTimestamp()'] " + e.getMessage());
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
public void unsetBackgroundTimestamp() {
|
|
231
|
+
try {
|
|
232
|
+
this.editor.remove(BACKGROUND_TIMESTAMP_KEY);
|
|
233
|
+
this.editor.commit();
|
|
234
|
+
logger.info("Background timestamp unset");
|
|
235
|
+
} catch (final Exception e) {
|
|
236
|
+
logger.error("Failed to delay update, [Error calling '_unsetBackgroundTimestamp()'] " + e.getMessage());
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private long getBackgroundTimestamp() {
|
|
241
|
+
try {
|
|
242
|
+
return this.prefs.getLong(BACKGROUND_TIMESTAMP_KEY, 0);
|
|
243
|
+
} catch (final Exception e) {
|
|
244
|
+
logger.error("Failed to delay update, [Error calling '_getBackgroundTimestamp()'] " + e.getMessage());
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
public boolean cancelDelay(String source) {
|
|
250
|
+
try {
|
|
251
|
+
this.editor.remove(DELAY_CONDITION_PREFERENCES);
|
|
252
|
+
this.editor.commit();
|
|
253
|
+
logger.info("All delays canceled from " + source);
|
|
254
|
+
return true;
|
|
255
|
+
} catch (final Exception e) {
|
|
256
|
+
logger.error("Failed to cancel update delay " + e.getMessage());
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|