@capgo/capacitor-updater 7.2.20 → 7.3.3
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 +6 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +48 -26
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +152 -141
- package/android/src/main/java/ee/forgr/capacitor_updater/{CapacitorUpdater.java → CapgoUpdater.java} +93 -59
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipherV2.java +13 -8
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayUpdateUtils.java +43 -43
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +24 -20
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +8 -4
- package/android/src/main/java/ee/forgr/capacitor_updater/Logger.java +338 -0
- package/dist/docs.json +16 -0
- package/dist/esm/definitions.d.ts +7 -0
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Plugin/AES.swift +5 -3
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +108 -94
- package/ios/Plugin/{CapacitorUpdater.swift → CapgoUpdater.swift} +71 -69
- package/ios/Plugin/CryptoCipherV2.swift +31 -26
- package/ios/Plugin/DelayUpdateUtils.swift +26 -24
- package/ios/Plugin/Logger.swift +289 -0
- package/ios/Plugin/UserDefaultsExtension.swift +0 -2
- package/package.json +1 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
package ee.forgr.capacitor_updater;
|
|
2
2
|
|
|
3
3
|
import android.content.SharedPreferences;
|
|
4
|
-
import android.util.Log;
|
|
5
4
|
import com.google.common.reflect.TypeToken;
|
|
6
5
|
import com.google.gson.Gson;
|
|
7
6
|
import io.github.g00fy2.versioncompare.Version;
|
|
@@ -12,6 +11,8 @@ import java.util.Date;
|
|
|
12
11
|
|
|
13
12
|
public class DelayUpdateUtils {
|
|
14
13
|
|
|
14
|
+
private final Logger logger;
|
|
15
|
+
|
|
15
16
|
public static final String DELAY_CONDITION_PREFERENCES = "DELAY_CONDITION_PREFERENCES_CAPGO";
|
|
16
17
|
public static final String BACKGROUND_TIMESTAMP_KEY = "BACKGROUND_TIMESTAMP_KEY_CAPGO";
|
|
17
18
|
|
|
@@ -20,11 +21,18 @@ public class DelayUpdateUtils {
|
|
|
20
21
|
private final Version currentVersionNative;
|
|
21
22
|
private final Runnable installNext;
|
|
22
23
|
|
|
23
|
-
public DelayUpdateUtils(
|
|
24
|
+
public DelayUpdateUtils(
|
|
25
|
+
SharedPreferences prefs,
|
|
26
|
+
SharedPreferences.Editor editor,
|
|
27
|
+
Version currentVersionNative,
|
|
28
|
+
Runnable installNext,
|
|
29
|
+
Logger logger
|
|
30
|
+
) {
|
|
24
31
|
this.prefs = prefs;
|
|
25
32
|
this.editor = editor;
|
|
26
33
|
this.currentVersionNative = currentVersionNative;
|
|
27
34
|
this.installNext = installNext;
|
|
35
|
+
this.logger = logger;
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
public enum CancelDelaySource {
|
|
@@ -54,8 +62,7 @@ public class DelayUpdateUtils {
|
|
|
54
62
|
try {
|
|
55
63
|
longValue = Long.parseLong(value);
|
|
56
64
|
} catch (NumberFormatException e) {
|
|
57
|
-
|
|
58
|
-
CapacitorUpdater.TAG,
|
|
65
|
+
logger.error(
|
|
59
66
|
"Background condition (value: " +
|
|
60
67
|
value +
|
|
61
68
|
") had an invalid value at index " +
|
|
@@ -65,8 +72,7 @@ public class DelayUpdateUtils {
|
|
|
65
72
|
}
|
|
66
73
|
|
|
67
74
|
if (delta > longValue) {
|
|
68
|
-
|
|
69
|
-
CapacitorUpdater.TAG,
|
|
75
|
+
logger.info(
|
|
70
76
|
"Background condition (value: " +
|
|
71
77
|
value +
|
|
72
78
|
") deleted at index " +
|
|
@@ -79,8 +85,7 @@ public class DelayUpdateUtils {
|
|
|
79
85
|
}
|
|
80
86
|
} else {
|
|
81
87
|
delayConditionListToKeep.add(condition);
|
|
82
|
-
|
|
83
|
-
CapacitorUpdater.TAG,
|
|
88
|
+
logger.info(
|
|
84
89
|
"Background delay (value: " +
|
|
85
90
|
value +
|
|
86
91
|
") condition kept at index " +
|
|
@@ -96,8 +101,7 @@ public class DelayUpdateUtils {
|
|
|
96
101
|
this.installNext.run();
|
|
97
102
|
} else {
|
|
98
103
|
delayConditionListToKeep.add(condition);
|
|
99
|
-
|
|
100
|
-
CapacitorUpdater.TAG,
|
|
104
|
+
logger.info(
|
|
101
105
|
"Kill delay (value: " + value + ") condition kept at index " + index + " (source: " + source.toString() + ")"
|
|
102
106
|
);
|
|
103
107
|
}
|
|
@@ -109,26 +113,23 @@ public class DelayUpdateUtils {
|
|
|
109
113
|
Date date = sdf.parse(value);
|
|
110
114
|
assert date != null;
|
|
111
115
|
if (new Date().compareTo(date) > 0) {
|
|
112
|
-
|
|
113
|
-
CapacitorUpdater.TAG,
|
|
114
|
-
"Date delay (value: " + value + ") condition removed due to expired date at index " + index
|
|
115
|
-
);
|
|
116
|
+
logger.info("Date delay (value: " + value + ") condition removed due to expired date at index " + index);
|
|
116
117
|
} else {
|
|
117
118
|
delayConditionListToKeep.add(condition);
|
|
118
|
-
|
|
119
|
+
logger.info("Date delay (value: " + value + ") condition kept at index " + index);
|
|
119
120
|
}
|
|
120
121
|
} catch (final Exception e) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
logger.error(
|
|
123
|
+
"Date delay (value: " +
|
|
124
|
+
value +
|
|
125
|
+
") condition removed due to parsing issue at index " +
|
|
126
|
+
index +
|
|
127
|
+
" " +
|
|
128
|
+
e.getMessage()
|
|
125
129
|
);
|
|
126
130
|
}
|
|
127
131
|
} else {
|
|
128
|
-
|
|
129
|
-
CapacitorUpdater.TAG,
|
|
130
|
-
"Date delay (value: " + value + ") condition removed due to empty value at index " + index
|
|
131
|
-
);
|
|
132
|
+
logger.debug("Date delay (value: " + value + ") condition removed due to empty value at index " + index);
|
|
132
133
|
}
|
|
133
134
|
break;
|
|
134
135
|
case DelayUntilNext.nativeVersion:
|
|
@@ -136,26 +137,25 @@ public class DelayUpdateUtils {
|
|
|
136
137
|
try {
|
|
137
138
|
final Version versionLimit = new Version(value);
|
|
138
139
|
if (this.currentVersionNative.isAtLeast(versionLimit)) {
|
|
139
|
-
|
|
140
|
-
CapacitorUpdater.TAG,
|
|
140
|
+
logger.info(
|
|
141
141
|
"Native version delay (value: " + value + ") condition removed due to above limit at index " + index
|
|
142
142
|
);
|
|
143
143
|
} else {
|
|
144
144
|
delayConditionListToKeep.add(condition);
|
|
145
|
-
|
|
145
|
+
logger.info("Native version delay (value: " + value + ") condition kept at index " + index);
|
|
146
146
|
}
|
|
147
147
|
} catch (final Exception e) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
logger.error(
|
|
149
|
+
"Native version delay (value: " +
|
|
150
|
+
value +
|
|
151
|
+
") condition removed due to parsing issue at index " +
|
|
152
|
+
index +
|
|
153
|
+
" " +
|
|
154
|
+
e.getMessage()
|
|
152
155
|
);
|
|
153
156
|
}
|
|
154
157
|
} else {
|
|
155
|
-
|
|
156
|
-
CapacitorUpdater.TAG,
|
|
157
|
-
"Native version delay (value: " + value + ") condition removed due to empty value at index " + index
|
|
158
|
-
);
|
|
158
|
+
logger.debug("Native version delay (value: " + value + ") condition removed due to empty value at index " + index);
|
|
159
159
|
}
|
|
160
160
|
break;
|
|
161
161
|
}
|
|
@@ -171,10 +171,10 @@ public class DelayUpdateUtils {
|
|
|
171
171
|
try {
|
|
172
172
|
this.editor.putString(DELAY_CONDITION_PREFERENCES, delayConditions);
|
|
173
173
|
this.editor.commit();
|
|
174
|
-
|
|
174
|
+
logger.info("Delay update saved");
|
|
175
175
|
return true;
|
|
176
176
|
} catch (final Exception e) {
|
|
177
|
-
|
|
177
|
+
logger.error("Failed to delay update, [Error calling '_setMultiDelay()'] " + e.getMessage());
|
|
178
178
|
return false;
|
|
179
179
|
}
|
|
180
180
|
}
|
|
@@ -183,9 +183,9 @@ public class DelayUpdateUtils {
|
|
|
183
183
|
try {
|
|
184
184
|
this.editor.putLong(BACKGROUND_TIMESTAMP_KEY, backgroundTimestamp);
|
|
185
185
|
this.editor.commit();
|
|
186
|
-
|
|
186
|
+
logger.info("Delay update saved");
|
|
187
187
|
} catch (final Exception e) {
|
|
188
|
-
|
|
188
|
+
logger.error("Failed to delay update, [Error calling '_setBackgroundTimestamp()'] " + e.getMessage());
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
|
|
@@ -193,9 +193,9 @@ public class DelayUpdateUtils {
|
|
|
193
193
|
try {
|
|
194
194
|
this.editor.remove(BACKGROUND_TIMESTAMP_KEY);
|
|
195
195
|
this.editor.commit();
|
|
196
|
-
|
|
196
|
+
logger.info("Delay update saved");
|
|
197
197
|
} catch (final Exception e) {
|
|
198
|
-
|
|
198
|
+
logger.error("Failed to delay update, [Error calling '_unsetBackgroundTimestamp()'] " + e.getMessage());
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
|
|
@@ -203,7 +203,7 @@ public class DelayUpdateUtils {
|
|
|
203
203
|
try {
|
|
204
204
|
return this.prefs.getLong(BACKGROUND_TIMESTAMP_KEY, 0);
|
|
205
205
|
} catch (final Exception e) {
|
|
206
|
-
|
|
206
|
+
logger.error("Failed to delay update, [Error calling '_getBackgroundTimestamp()'] " + e.getMessage());
|
|
207
207
|
return 0;
|
|
208
208
|
}
|
|
209
209
|
}
|
|
@@ -212,10 +212,10 @@ public class DelayUpdateUtils {
|
|
|
212
212
|
try {
|
|
213
213
|
this.editor.remove(DELAY_CONDITION_PREFERENCES);
|
|
214
214
|
this.editor.commit();
|
|
215
|
-
|
|
215
|
+
logger.info("All delays canceled from " + source);
|
|
216
216
|
return true;
|
|
217
217
|
} catch (final Exception e) {
|
|
218
|
-
|
|
218
|
+
logger.error("Failed to cancel update delay " + e.getMessage());
|
|
219
219
|
return false;
|
|
220
220
|
}
|
|
221
221
|
}
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
package ee.forgr.capacitor_updater;
|
|
7
7
|
|
|
8
8
|
import android.content.Context;
|
|
9
|
-
import android.util.Log;
|
|
10
9
|
import androidx.annotation.NonNull;
|
|
11
10
|
import androidx.work.Data;
|
|
12
11
|
import androidx.work.Worker;
|
|
@@ -39,7 +38,12 @@ import org.json.JSONObject;
|
|
|
39
38
|
|
|
40
39
|
public class DownloadService extends Worker {
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
private static Logger logger;
|
|
42
|
+
|
|
43
|
+
public static void setLogger(Logger loggerInstance) {
|
|
44
|
+
logger = loggerInstance;
|
|
45
|
+
}
|
|
46
|
+
|
|
43
47
|
public static final String URL = "URL";
|
|
44
48
|
public static final String ID = "id";
|
|
45
49
|
public static final String PERCENT = "percent";
|
|
@@ -94,7 +98,7 @@ public class DownloadService extends Worker {
|
|
|
94
98
|
String publicKey = getInputData().getString(PUBLIC_KEY);
|
|
95
99
|
boolean isManifest = getInputData().getBoolean(IS_MANIFEST, false);
|
|
96
100
|
|
|
97
|
-
|
|
101
|
+
logger.debug("doWork isManifest: " + isManifest);
|
|
98
102
|
|
|
99
103
|
if (isManifest) {
|
|
100
104
|
JSONArray manifest = DataManager.getInstance().getAndClearManifest();
|
|
@@ -102,7 +106,7 @@ public class DownloadService extends Worker {
|
|
|
102
106
|
handleManifestDownload(id, documentsDir, dest, version, sessionKey, publicKey, manifest.toString());
|
|
103
107
|
return createSuccessResult(dest, version, sessionKey, checksum, true);
|
|
104
108
|
} else {
|
|
105
|
-
|
|
109
|
+
logger.error("Manifest is null");
|
|
106
110
|
return createFailureResult("Manifest is null");
|
|
107
111
|
}
|
|
108
112
|
} else {
|
|
@@ -134,7 +138,7 @@ public class DownloadService extends Worker {
|
|
|
134
138
|
String manifestString
|
|
135
139
|
) {
|
|
136
140
|
try {
|
|
137
|
-
|
|
141
|
+
logger.debug("handleManifestDownload");
|
|
138
142
|
JSONArray manifest = new JSONArray(manifestString);
|
|
139
143
|
File destFolder = new File(documentsDir, dest);
|
|
140
144
|
File cacheFolder = new File(getApplicationContext().getCacheDir(), "capgo_downloads");
|
|
@@ -167,7 +171,7 @@ public class DownloadService extends Worker {
|
|
|
167
171
|
try {
|
|
168
172
|
fileHash = CryptoCipherV2.decryptChecksum(fileHash, publicKey);
|
|
169
173
|
} catch (Exception e) {
|
|
170
|
-
|
|
174
|
+
logger.error("Error decrypting checksum for " + fileName + "fileHash: " + fileHash);
|
|
171
175
|
hasError.set(true);
|
|
172
176
|
continue;
|
|
173
177
|
}
|
|
@@ -180,7 +184,7 @@ public class DownloadService extends Worker {
|
|
|
180
184
|
|
|
181
185
|
// Ensure parent directories of the target file exist
|
|
182
186
|
if (!Objects.requireNonNull(targetFile.getParentFile()).exists() && !targetFile.getParentFile().mkdirs()) {
|
|
183
|
-
|
|
187
|
+
logger.error("Failed to create parent directory for: " + targetFile.getAbsolutePath());
|
|
184
188
|
hasError.set(true);
|
|
185
189
|
continue;
|
|
186
190
|
}
|
|
@@ -189,10 +193,10 @@ public class DownloadService extends Worker {
|
|
|
189
193
|
try {
|
|
190
194
|
if (builtinFile.exists() && verifyChecksum(builtinFile, finalFileHash)) {
|
|
191
195
|
copyFile(builtinFile, targetFile);
|
|
192
|
-
|
|
196
|
+
logger.debug("using builtin file " + fileName);
|
|
193
197
|
} else if (cacheFile.exists() && verifyChecksum(cacheFile, finalFileHash)) {
|
|
194
198
|
copyFile(cacheFile, targetFile);
|
|
195
|
-
|
|
199
|
+
logger.debug("already cached " + fileName);
|
|
196
200
|
} else {
|
|
197
201
|
downloadAndVerify(downloadUrl, targetFile, cacheFile, finalFileHash, sessionKey, publicKey);
|
|
198
202
|
}
|
|
@@ -201,7 +205,7 @@ public class DownloadService extends Worker {
|
|
|
201
205
|
int percent = calcTotalPercent(completed, totalFiles);
|
|
202
206
|
setProgress(percent);
|
|
203
207
|
} catch (Exception e) {
|
|
204
|
-
|
|
208
|
+
logger.error("Error processing file: " + fileName + " " + e.getMessage());
|
|
205
209
|
hasError.set(true);
|
|
206
210
|
}
|
|
207
211
|
});
|
|
@@ -213,7 +217,7 @@ public class DownloadService extends Worker {
|
|
|
213
217
|
try {
|
|
214
218
|
future.get();
|
|
215
219
|
} catch (Exception e) {
|
|
216
|
-
|
|
220
|
+
logger.error("Error waiting for download " + e.getMessage());
|
|
217
221
|
hasError.set(true);
|
|
218
222
|
}
|
|
219
223
|
}
|
|
@@ -229,11 +233,11 @@ public class DownloadService extends Worker {
|
|
|
229
233
|
}
|
|
230
234
|
|
|
231
235
|
if (hasError.get()) {
|
|
232
|
-
|
|
236
|
+
logger.error("One or more files failed to download");
|
|
233
237
|
throw new IOException("One or more files failed to download");
|
|
234
238
|
}
|
|
235
239
|
} catch (Exception e) {
|
|
236
|
-
|
|
240
|
+
logger.error("Error in handleManifestDownload " + e.getMessage());
|
|
237
241
|
throw new RuntimeException(e.getLocalizedMessage());
|
|
238
242
|
}
|
|
239
243
|
}
|
|
@@ -347,7 +351,7 @@ public class DownloadService extends Worker {
|
|
|
347
351
|
infoFile.createNewFile();
|
|
348
352
|
tempFile.createNewFile();
|
|
349
353
|
} catch (IOException e) {
|
|
350
|
-
|
|
354
|
+
logger.error("Error in clearDownloadData " + e.getMessage());
|
|
351
355
|
// not a fatal error, so we don't throw an exception
|
|
352
356
|
}
|
|
353
357
|
}
|
|
@@ -373,7 +377,7 @@ public class DownloadService extends Worker {
|
|
|
373
377
|
String sessionKey,
|
|
374
378
|
String publicKey
|
|
375
379
|
) throws Exception {
|
|
376
|
-
|
|
380
|
+
logger.debug("downloadAndVerify " + downloadUrl);
|
|
377
381
|
|
|
378
382
|
Request request = new Request.Builder().url(downloadUrl).build();
|
|
379
383
|
|
|
@@ -409,7 +413,7 @@ public class DownloadService extends Worker {
|
|
|
409
413
|
}
|
|
410
414
|
|
|
411
415
|
if (!publicKey.isEmpty() && sessionKey != null && !sessionKey.isEmpty()) {
|
|
412
|
-
|
|
416
|
+
logger.debug("Decrypting file " + targetFile.getName());
|
|
413
417
|
CryptoCipherV2.decryptFile(compressedFile, publicKey, sessionKey);
|
|
414
418
|
}
|
|
415
419
|
|
|
@@ -482,7 +486,7 @@ public class DownloadService extends Worker {
|
|
|
482
486
|
private byte[] decompressBrotli(byte[] data, String fileName) throws IOException {
|
|
483
487
|
// Validate input
|
|
484
488
|
if (data == null) {
|
|
485
|
-
|
|
489
|
+
logger.error("Error: Null data received for " + fileName);
|
|
486
490
|
throw new IOException("Null data received");
|
|
487
491
|
}
|
|
488
492
|
|
|
@@ -509,7 +513,7 @@ public class DownloadService extends Worker {
|
|
|
509
513
|
return Arrays.copyOfRange(data, 3, data.length - 1);
|
|
510
514
|
}
|
|
511
515
|
} catch (ArrayIndexOutOfBoundsException e) {
|
|
512
|
-
|
|
516
|
+
logger.error("Error: Malformed data for " + fileName);
|
|
513
517
|
throw new IOException("Malformed data structure");
|
|
514
518
|
}
|
|
515
519
|
}
|
|
@@ -527,13 +531,13 @@ public class DownloadService extends Worker {
|
|
|
527
531
|
}
|
|
528
532
|
return bos.toByteArray();
|
|
529
533
|
} catch (IOException e) {
|
|
530
|
-
|
|
534
|
+
logger.error("Error: Brotli process failed for " + fileName + ". Status: " + e.getMessage());
|
|
531
535
|
// Add hex dump for debugging
|
|
532
536
|
StringBuilder hexDump = new StringBuilder();
|
|
533
537
|
for (int i = 0; i < Math.min(32, data.length); i++) {
|
|
534
538
|
hexDump.append(String.format("%02x ", data[i]));
|
|
535
539
|
}
|
|
536
|
-
|
|
540
|
+
logger.error("Error: Raw data (" + fileName + "): " + hexDump.toString());
|
|
537
541
|
throw e;
|
|
538
542
|
}
|
|
539
543
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
package ee.forgr.capacitor_updater;
|
|
2
2
|
|
|
3
3
|
import android.content.Context;
|
|
4
|
-
import android.util.Log;
|
|
5
4
|
import androidx.work.BackoffPolicy;
|
|
6
5
|
import androidx.work.Configuration;
|
|
7
6
|
import androidx.work.Constraints;
|
|
@@ -16,7 +15,12 @@ import java.util.concurrent.TimeUnit;
|
|
|
16
15
|
|
|
17
16
|
public class DownloadWorkerManager {
|
|
18
17
|
|
|
19
|
-
private static
|
|
18
|
+
private static Logger logger;
|
|
19
|
+
|
|
20
|
+
public static void setLogger(Logger loggerInstance) {
|
|
21
|
+
logger = loggerInstance;
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
private static volatile boolean isInitialized = false;
|
|
21
25
|
private static final Set<String> activeVersions = new HashSet<>();
|
|
22
26
|
|
|
@@ -53,7 +57,7 @@ public class DownloadWorkerManager {
|
|
|
53
57
|
|
|
54
58
|
// If version is already downloading, don't start another one
|
|
55
59
|
if (isVersionDownloading(version)) {
|
|
56
|
-
|
|
60
|
+
logger.info("Version " + version + " is already downloading");
|
|
57
61
|
return;
|
|
58
62
|
}
|
|
59
63
|
activeVersions.add(version);
|
|
@@ -74,7 +78,7 @@ public class DownloadWorkerManager {
|
|
|
74
78
|
// Create network constraints - be more lenient on emulators
|
|
75
79
|
Constraints.Builder constraintsBuilder = new Constraints.Builder();
|
|
76
80
|
if (isEmulator) {
|
|
77
|
-
|
|
81
|
+
logger.info("Emulator detected - using lenient network constraints");
|
|
78
82
|
// On emulators, use NOT_REQUIRED to avoid background network issues
|
|
79
83
|
constraintsBuilder.setRequiredNetworkType(NetworkType.NOT_REQUIRED);
|
|
80
84
|
} else {
|