@capgo/capacitor-updater 7.25.0 → 7.27.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/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +11 -3
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +33 -9
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +100 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +18 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +19 -0
- package/package.json +1 -1
|
@@ -71,7 +71,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
71
71
|
private static final String[] BREAKING_EVENT_NAMES = { "breakingAvailable", "majorAvailable" };
|
|
72
72
|
private static final String LAST_FAILED_BUNDLE_PREF_KEY = "CapacitorUpdater.lastFailedBundle";
|
|
73
73
|
|
|
74
|
-
private final String pluginVersion = "7.
|
|
74
|
+
private final String pluginVersion = "7.27.0";
|
|
75
75
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
76
76
|
|
|
77
77
|
private SharedPreferences.Editor editor;
|
|
@@ -1304,6 +1304,9 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1304
1304
|
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, channel, (res) -> {
|
|
1305
1305
|
JSObject jsRes = mapToJSObject(res);
|
|
1306
1306
|
if (jsRes.has("error")) {
|
|
1307
|
+
String error = jsRes.getString("error");
|
|
1308
|
+
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : "server did not provide a message";
|
|
1309
|
+
logger.error("getLatest failed with error: " + error + ", message: " + errorMessage);
|
|
1307
1310
|
call.reject(jsRes.getString("error"));
|
|
1308
1311
|
return;
|
|
1309
1312
|
} else if (jsRes.has("message")) {
|
|
@@ -1409,7 +1412,11 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1409
1412
|
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, null, (res) -> {
|
|
1410
1413
|
JSObject jsRes = mapToJSObject(res);
|
|
1411
1414
|
if (jsRes.has("error")) {
|
|
1412
|
-
|
|
1415
|
+
String error = jsRes.getString("error");
|
|
1416
|
+
String errorMessage = jsRes.has("message")
|
|
1417
|
+
? jsRes.getString("message")
|
|
1418
|
+
: "server did not provide a message";
|
|
1419
|
+
logger.error("getLatest failed with error: " + error + ", message: " + errorMessage);
|
|
1413
1420
|
} else if (jsRes.has("version")) {
|
|
1414
1421
|
String newVersion = jsRes.getString("version");
|
|
1415
1422
|
String currentVersion = String.valueOf(CapacitorUpdaterPlugin.this.implementation.getCurrentBundle());
|
|
@@ -1613,7 +1620,8 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1613
1620
|
// Handle network errors and other failures first
|
|
1614
1621
|
if (jsRes.has("error")) {
|
|
1615
1622
|
String error = jsRes.getString("error");
|
|
1616
|
-
|
|
1623
|
+
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : "server did not provide a message";
|
|
1624
|
+
logger.error("getLatest failed with error: " + error + ", message: " + errorMessage);
|
|
1617
1625
|
String latestVersion = jsRes.has("version") ? jsRes.getString("version") : current.getVersionName();
|
|
1618
1626
|
if ("response_error".equals(error)) {
|
|
1619
1627
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
@@ -353,7 +353,15 @@ public class CapgoUpdater {
|
|
|
353
353
|
manifest != null,
|
|
354
354
|
this.isEmulator(),
|
|
355
355
|
this.appId,
|
|
356
|
-
this.pluginVersion
|
|
356
|
+
this.pluginVersion,
|
|
357
|
+
this.isProd(),
|
|
358
|
+
this.statsUrl,
|
|
359
|
+
this.deviceID,
|
|
360
|
+
this.versionBuild,
|
|
361
|
+
this.versionCode,
|
|
362
|
+
this.versionOs,
|
|
363
|
+
this.customId,
|
|
364
|
+
this.defaultChannel
|
|
357
365
|
);
|
|
358
366
|
|
|
359
367
|
if (manifest != null) {
|
|
@@ -880,8 +888,12 @@ public class CapgoUpdater {
|
|
|
880
888
|
// Check for server-side errors first
|
|
881
889
|
if (jsonResponse.has("error")) {
|
|
882
890
|
Map<String, Object> retError = new HashMap<>();
|
|
883
|
-
retError.put("
|
|
884
|
-
|
|
891
|
+
retError.put("error", jsonResponse.getString("error"));
|
|
892
|
+
if (jsonResponse.has("message")) {
|
|
893
|
+
retError.put("message", jsonResponse.getString("message"));
|
|
894
|
+
} else {
|
|
895
|
+
retError.put("message", "server did not provide a message");
|
|
896
|
+
}
|
|
885
897
|
callback.callback(retError);
|
|
886
898
|
return;
|
|
887
899
|
}
|
|
@@ -1008,8 +1020,12 @@ public class CapgoUpdater {
|
|
|
1008
1020
|
// Check for server-side errors first
|
|
1009
1021
|
if (jsonResponse.has("error")) {
|
|
1010
1022
|
Map<String, Object> retError = new HashMap<>();
|
|
1011
|
-
retError.put("
|
|
1012
|
-
|
|
1023
|
+
retError.put("error", jsonResponse.getString("error"));
|
|
1024
|
+
if (jsonResponse.has("message")) {
|
|
1025
|
+
retError.put("message", jsonResponse.getString("message"));
|
|
1026
|
+
} else {
|
|
1027
|
+
retError.put("message", "server did not provide a message");
|
|
1028
|
+
}
|
|
1013
1029
|
callback.callback(retError);
|
|
1014
1030
|
return;
|
|
1015
1031
|
}
|
|
@@ -1161,8 +1177,12 @@ public class CapgoUpdater {
|
|
|
1161
1177
|
// Check for server-side errors first
|
|
1162
1178
|
if (jsonResponse.has("error")) {
|
|
1163
1179
|
Map<String, Object> retError = new HashMap<>();
|
|
1164
|
-
retError.put("
|
|
1165
|
-
|
|
1180
|
+
retError.put("error", jsonResponse.getString("error"));
|
|
1181
|
+
if (jsonResponse.has("message")) {
|
|
1182
|
+
retError.put("message", jsonResponse.getString("message"));
|
|
1183
|
+
} else {
|
|
1184
|
+
retError.put("message", "server did not provide a message");
|
|
1185
|
+
}
|
|
1166
1186
|
callback.callback(retError);
|
|
1167
1187
|
return;
|
|
1168
1188
|
}
|
|
@@ -1289,8 +1309,12 @@ public class CapgoUpdater {
|
|
|
1289
1309
|
JSONObject json = new JSONObject(data);
|
|
1290
1310
|
if (json.has("error")) {
|
|
1291
1311
|
Map<String, Object> retError = new HashMap<>();
|
|
1292
|
-
retError.put("
|
|
1293
|
-
|
|
1312
|
+
retError.put("error", json.getString("error"));
|
|
1313
|
+
if (json.has("message")) {
|
|
1314
|
+
retError.put("message", json.getString("message"));
|
|
1315
|
+
} else {
|
|
1316
|
+
retError.put("message", "server did not provide a message");
|
|
1317
|
+
}
|
|
1294
1318
|
callback.callback(retError);
|
|
1295
1319
|
return;
|
|
1296
1320
|
}
|
|
@@ -28,10 +28,14 @@ import java.util.concurrent.Future;
|
|
|
28
28
|
import java.util.concurrent.TimeUnit;
|
|
29
29
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
30
30
|
import java.util.concurrent.atomic.AtomicLong;
|
|
31
|
+
import okhttp3.Call;
|
|
32
|
+
import okhttp3.Callback;
|
|
31
33
|
import okhttp3.Interceptor;
|
|
34
|
+
import okhttp3.MediaType;
|
|
32
35
|
import okhttp3.OkHttpClient;
|
|
33
36
|
import okhttp3.Protocol;
|
|
34
37
|
import okhttp3.Request;
|
|
38
|
+
import okhttp3.RequestBody;
|
|
35
39
|
import okhttp3.Response;
|
|
36
40
|
import okhttp3.ResponseBody;
|
|
37
41
|
import okio.Buffer;
|
|
@@ -64,6 +68,15 @@ public class DownloadService extends Worker {
|
|
|
64
68
|
public static final String IS_MANIFEST = "is_manifest";
|
|
65
69
|
public static final String APP_ID = "app_id";
|
|
66
70
|
public static final String pluginVersion = "plugin_version";
|
|
71
|
+
public static final String STATS_URL = "stats_url";
|
|
72
|
+
public static final String DEVICE_ID = "device_id";
|
|
73
|
+
public static final String CUSTOM_ID = "custom_id";
|
|
74
|
+
public static final String VERSION_BUILD = "version_build";
|
|
75
|
+
public static final String VERSION_CODE = "version_code";
|
|
76
|
+
public static final String VERSION_OS = "version_os";
|
|
77
|
+
public static final String DEFAULT_CHANNEL = "default_channel";
|
|
78
|
+
public static final String IS_PROD = "is_prod";
|
|
79
|
+
public static final String IS_EMULATOR = "is_emulator";
|
|
67
80
|
private static final String UPDATE_FILE = "update.dat";
|
|
68
81
|
|
|
69
82
|
// Shared OkHttpClient to prevent resource leaks
|
|
@@ -130,6 +143,11 @@ public class DownloadService extends Worker {
|
|
|
130
143
|
return Result.success(output);
|
|
131
144
|
}
|
|
132
145
|
|
|
146
|
+
private String getInputString(String key, String fallback) {
|
|
147
|
+
String value = getInputData().getString(key);
|
|
148
|
+
return value != null ? value : fallback;
|
|
149
|
+
}
|
|
150
|
+
|
|
133
151
|
@NonNull
|
|
134
152
|
@Override
|
|
135
153
|
public Result doWork() {
|
|
@@ -174,6 +192,62 @@ public class DownloadService extends Worker {
|
|
|
174
192
|
return percent;
|
|
175
193
|
}
|
|
176
194
|
|
|
195
|
+
private void sendStatsAsync(String action, String version) {
|
|
196
|
+
try {
|
|
197
|
+
String statsUrl = getInputData().getString(STATS_URL);
|
|
198
|
+
if (statsUrl == null || statsUrl.isEmpty()) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
JSONObject json = new JSONObject();
|
|
203
|
+
json.put("platform", "android");
|
|
204
|
+
json.put("app_id", getInputString(APP_ID, "unknown"));
|
|
205
|
+
json.put("plugin_version", getInputString(pluginVersion, "unknown"));
|
|
206
|
+
json.put("version_name", version != null ? version : "");
|
|
207
|
+
json.put("old_version_name", "");
|
|
208
|
+
json.put("action", action);
|
|
209
|
+
json.put("device_id", getInputString(DEVICE_ID, ""));
|
|
210
|
+
json.put("custom_id", getInputString(CUSTOM_ID, ""));
|
|
211
|
+
json.put("version_build", getInputString(VERSION_BUILD, ""));
|
|
212
|
+
json.put("version_code", getInputString(VERSION_CODE, ""));
|
|
213
|
+
json.put("version_os", getInputString(VERSION_OS, currentVersionOs));
|
|
214
|
+
json.put("defaultChannel", getInputString(DEFAULT_CHANNEL, ""));
|
|
215
|
+
json.put("is_prod", getInputData().getBoolean(IS_PROD, true));
|
|
216
|
+
json.put("is_emulator", getInputData().getBoolean(IS_EMULATOR, false));
|
|
217
|
+
|
|
218
|
+
Request request = new Request.Builder()
|
|
219
|
+
.url(statsUrl)
|
|
220
|
+
.post(RequestBody.create(json.toString(), MediaType.get("application/json")))
|
|
221
|
+
.build();
|
|
222
|
+
|
|
223
|
+
sharedClient
|
|
224
|
+
.newCall(request)
|
|
225
|
+
.enqueue(
|
|
226
|
+
new Callback() {
|
|
227
|
+
@Override
|
|
228
|
+
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
|
229
|
+
if (logger != null) {
|
|
230
|
+
logger.error("Failed to send stats: " + e.getMessage());
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
@Override
|
|
235
|
+
public void onResponse(@NonNull Call call, @NonNull Response response) {
|
|
236
|
+
try (ResponseBody body = response.body()) {
|
|
237
|
+
// nothing else to do, just closing body
|
|
238
|
+
} catch (Exception ignored) {} finally {
|
|
239
|
+
response.close();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
} catch (Exception e) {
|
|
245
|
+
if (logger != null) {
|
|
246
|
+
logger.error("sendStatsAsync error: " + e.getMessage());
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
177
251
|
private void handleManifestDownload(
|
|
178
252
|
String id,
|
|
179
253
|
String documentsDir,
|
|
@@ -185,6 +259,10 @@ public class DownloadService extends Worker {
|
|
|
185
259
|
) {
|
|
186
260
|
try {
|
|
187
261
|
logger.debug("handleManifestDownload");
|
|
262
|
+
|
|
263
|
+
// Send stats for manifest download start
|
|
264
|
+
sendStatsAsync("download_manifest_start", version);
|
|
265
|
+
|
|
188
266
|
JSONArray manifest = new JSONArray(manifestString);
|
|
189
267
|
File destFolder = new File(documentsDir, dest);
|
|
190
268
|
File cacheFolder = new File(getApplicationContext().getCacheDir(), "capgo_downloads");
|
|
@@ -252,6 +330,7 @@ public class DownloadService extends Worker {
|
|
|
252
330
|
setProgress(percent);
|
|
253
331
|
} catch (Exception e) {
|
|
254
332
|
logger.error("Error processing file: " + fileName + " " + e.getMessage());
|
|
333
|
+
sendStatsAsync("download_manifest_file_fail", version + ":" + fileName);
|
|
255
334
|
hasError.set(true);
|
|
256
335
|
}
|
|
257
336
|
});
|
|
@@ -282,6 +361,9 @@ public class DownloadService extends Worker {
|
|
|
282
361
|
logger.error("One or more files failed to download");
|
|
283
362
|
throw new IOException("One or more files failed to download");
|
|
284
363
|
}
|
|
364
|
+
|
|
365
|
+
// Send stats for manifest download complete
|
|
366
|
+
sendStatsAsync("download_manifest_complete", version);
|
|
285
367
|
} catch (Exception e) {
|
|
286
368
|
logger.error("Error in handleManifestDownload " + e.getMessage());
|
|
287
369
|
throw new RuntimeException(e.getLocalizedMessage());
|
|
@@ -297,6 +379,9 @@ public class DownloadService extends Worker {
|
|
|
297
379
|
String sessionKey,
|
|
298
380
|
String checksum
|
|
299
381
|
) {
|
|
382
|
+
// Send stats for zip download start
|
|
383
|
+
sendStatsAsync("download_zip_start", version);
|
|
384
|
+
|
|
300
385
|
File target = new File(documentsDir, dest);
|
|
301
386
|
File infoFile = new File(documentsDir, UPDATE_FILE);
|
|
302
387
|
File tempFile = new File(documentsDir, "temp" + ".tmp");
|
|
@@ -404,6 +489,9 @@ public class DownloadService extends Worker {
|
|
|
404
489
|
throw new RuntimeException("Failed to rename temp file to final destination");
|
|
405
490
|
}
|
|
406
491
|
infoFile.delete();
|
|
492
|
+
|
|
493
|
+
// Send stats for zip download complete
|
|
494
|
+
sendStatsAsync("download_zip_complete", version);
|
|
407
495
|
} catch (OutOfMemoryError e) {
|
|
408
496
|
logger.error("Out of memory during download: " + e.getMessage());
|
|
409
497
|
// Try to free some memory
|
|
@@ -503,6 +591,7 @@ public class DownloadService extends Worker {
|
|
|
503
591
|
|
|
504
592
|
try (Response response = sharedClient.newCall(request).execute()) {
|
|
505
593
|
if (!response.isSuccessful()) {
|
|
594
|
+
sendStatsAsync("download_manifest_file_fail", getInputData().getString(VERSION) + ":" + finalTargetFile.getName());
|
|
506
595
|
throw new IOException("Unexpected response code: " + response.code());
|
|
507
596
|
}
|
|
508
597
|
|
|
@@ -526,7 +615,16 @@ public class DownloadService extends Worker {
|
|
|
526
615
|
try (FileInputStream fis = new FileInputStream(compressedFile)) {
|
|
527
616
|
byte[] compressedData = new byte[(int) compressedFile.length()];
|
|
528
617
|
fis.read(compressedData);
|
|
529
|
-
byte[] decompressedData
|
|
618
|
+
byte[] decompressedData;
|
|
619
|
+
try {
|
|
620
|
+
decompressedData = decompressBrotli(compressedData, targetFile.getName());
|
|
621
|
+
} catch (IOException e) {
|
|
622
|
+
sendStatsAsync(
|
|
623
|
+
"download_manifest_brotli_fail",
|
|
624
|
+
getInputData().getString(VERSION) + ":" + finalTargetFile.getName()
|
|
625
|
+
);
|
|
626
|
+
throw e;
|
|
627
|
+
}
|
|
530
628
|
|
|
531
629
|
// Write decompressed data atomically
|
|
532
630
|
try (java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(decompressedData)) {
|
|
@@ -552,6 +650,7 @@ public class DownloadService extends Worker {
|
|
|
552
650
|
}
|
|
553
651
|
} else {
|
|
554
652
|
finalTargetFile.delete();
|
|
653
|
+
sendStatsAsync("download_manifest_checksum_fail", getInputData().getString(VERSION) + ":" + finalTargetFile.getName());
|
|
555
654
|
throw new IOException(
|
|
556
655
|
"Checksum verification failed for: " +
|
|
557
656
|
downloadUrl +
|
|
@@ -61,7 +61,15 @@ public class DownloadWorkerManager {
|
|
|
61
61
|
boolean isManifest,
|
|
62
62
|
boolean isEmulator,
|
|
63
63
|
String appId,
|
|
64
|
-
String pluginVersion
|
|
64
|
+
String pluginVersion,
|
|
65
|
+
boolean isProd,
|
|
66
|
+
String statsUrl,
|
|
67
|
+
String deviceId,
|
|
68
|
+
String versionBuild,
|
|
69
|
+
String versionCode,
|
|
70
|
+
String versionOs,
|
|
71
|
+
String customId,
|
|
72
|
+
String defaultChannel
|
|
65
73
|
) {
|
|
66
74
|
initializeIfNeeded(context.getApplicationContext());
|
|
67
75
|
|
|
@@ -81,6 +89,15 @@ public class DownloadWorkerManager {
|
|
|
81
89
|
.putString(DownloadService.PUBLIC_KEY, publicKey)
|
|
82
90
|
.putString(DownloadService.APP_ID, appId)
|
|
83
91
|
.putString(DownloadService.pluginVersion, pluginVersion)
|
|
92
|
+
.putString(DownloadService.STATS_URL, statsUrl)
|
|
93
|
+
.putString(DownloadService.DEVICE_ID, deviceId)
|
|
94
|
+
.putString(DownloadService.VERSION_BUILD, versionBuild)
|
|
95
|
+
.putString(DownloadService.VERSION_CODE, versionCode)
|
|
96
|
+
.putString(DownloadService.VERSION_OS, versionOs)
|
|
97
|
+
.putString(DownloadService.CUSTOM_ID, customId)
|
|
98
|
+
.putString(DownloadService.DEFAULT_CHANNEL, defaultChannel)
|
|
99
|
+
.putBoolean(DownloadService.IS_PROD, isProd)
|
|
100
|
+
.putBoolean(DownloadService.IS_EMULATOR, isEmulator)
|
|
84
101
|
.build();
|
|
85
102
|
|
|
86
103
|
// Create network constraints - be more lenient on emulators
|
|
@@ -54,7 +54,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
54
54
|
CAPPluginMethod(name: "isShakeMenuEnabled", returnType: CAPPluginReturnPromise)
|
|
55
55
|
]
|
|
56
56
|
public var implementation = CapgoUpdater()
|
|
57
|
-
private let pluginVersion: String = "7.
|
|
57
|
+
private let pluginVersion: String = "7.27.0"
|
|
58
58
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
59
59
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
60
60
|
static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
|
|
@@ -409,6 +409,9 @@ import UIKit
|
|
|
409
409
|
let bundleInfo = BundleInfo(id: id, version: version, status: BundleStatus.DOWNLOADING, downloaded: Date(), checksum: "")
|
|
410
410
|
self.saveBundleInfo(id: id, bundle: bundleInfo)
|
|
411
411
|
|
|
412
|
+
// Send stats for manifest download start
|
|
413
|
+
self.sendStats(action: "download_manifest_start", versionName: version)
|
|
414
|
+
|
|
412
415
|
// Notify the start of the download process
|
|
413
416
|
self.notifyDownload(id: id, percent: 0, ignoreMultipleOfTen: true)
|
|
414
417
|
|
|
@@ -467,6 +470,7 @@ import UIKit
|
|
|
467
470
|
do {
|
|
468
471
|
let statusCode = response.response?.statusCode ?? 200
|
|
469
472
|
if statusCode < 200 || statusCode >= 300 {
|
|
473
|
+
self.sendStats(action: "download_manifest_file_fail", versionName: "\(version):\(fileName)")
|
|
470
474
|
if let stringData = String(data: data, encoding: .utf8) {
|
|
471
475
|
throw NSError(domain: "StatusCodeError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to fetch. Status code (\(statusCode)) invalid. Data: \(stringData) for file \(fileName) at url \(downloadUrl)"])
|
|
472
476
|
} else {
|
|
@@ -498,6 +502,7 @@ import UIKit
|
|
|
498
502
|
if isBrotli {
|
|
499
503
|
// Decompress the Brotli data
|
|
500
504
|
guard let decompressedData = self.decompressBrotli(data: finalData, fileName: fileName) else {
|
|
505
|
+
self.sendStats(action: "download_manifest_brotli_fail", versionName: "\(version):\(finalFileName)")
|
|
501
506
|
throw NSError(domain: "BrotliDecompressionError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to decompress Brotli data for file \(fileName) at url \(downloadUrl)"])
|
|
502
507
|
}
|
|
503
508
|
finalData = decompressedData
|
|
@@ -508,6 +513,7 @@ import UIKit
|
|
|
508
513
|
// assume that calcChecksum != null
|
|
509
514
|
let calculatedChecksum = CryptoCipher.calcChecksum(filePath: destFilePath)
|
|
510
515
|
if calculatedChecksum != fileHash {
|
|
516
|
+
self.sendStats(action: "download_manifest_checksum_fail", versionName: "\(version):\(finalFileName)")
|
|
511
517
|
throw NSError(domain: "ChecksumError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Computed checksum is not equal to required checksum (\(calculatedChecksum) != \(fileHash)) for file \(fileName) at url \(downloadUrl)"])
|
|
512
518
|
}
|
|
513
519
|
}
|
|
@@ -523,6 +529,8 @@ import UIKit
|
|
|
523
529
|
self.logger.error("downloadManifest \(id) \(fileName) error: \(error.localizedDescription)")
|
|
524
530
|
}
|
|
525
531
|
case .failure(let error):
|
|
532
|
+
downloadError = error
|
|
533
|
+
self.sendStats(action: "download_manifest_file_fail", versionName: "\(version):\(fileName)")
|
|
526
534
|
self.logger.error("downloadManifest \(id) \(fileName) download error: \(error.localizedDescription). Debug response: \(response.debugDescription).")
|
|
527
535
|
}
|
|
528
536
|
}
|
|
@@ -542,6 +550,9 @@ import UIKit
|
|
|
542
550
|
let updatedBundle = bundleInfo.setStatus(status: BundleStatus.PENDING.localizedString)
|
|
543
551
|
self.saveBundleInfo(id: id, bundle: updatedBundle)
|
|
544
552
|
|
|
553
|
+
// Send stats for manifest download complete
|
|
554
|
+
self.sendStats(action: "download_manifest_complete", versionName: version)
|
|
555
|
+
|
|
545
556
|
logger.info("downloadManifest done \(id)")
|
|
546
557
|
return updatedBundle
|
|
547
558
|
}
|
|
@@ -672,6 +683,10 @@ import UIKit
|
|
|
672
683
|
var lastSentProgress = 0
|
|
673
684
|
var totalReceivedBytes: Int64 = loadDownloadProgress() // Retrieving the amount of already downloaded data if exist, defined at 0 otherwise
|
|
674
685
|
let requestHeaders: HTTPHeaders = ["Range": "bytes=\(totalReceivedBytes)-"]
|
|
686
|
+
|
|
687
|
+
// Send stats for zip download start
|
|
688
|
+
self.sendStats(action: "download_zip_start", versionName: version)
|
|
689
|
+
|
|
675
690
|
// Opening connection for streaming the bytes
|
|
676
691
|
if totalReceivedBytes == 0 {
|
|
677
692
|
self.notifyDownload(id: id, percent: 0, ignoreMultipleOfTen: true)
|
|
@@ -780,6 +795,10 @@ import UIKit
|
|
|
780
795
|
let info = BundleInfo(id: id, version: version, status: BundleStatus.PENDING, downloaded: Date(), checksum: checksum)
|
|
781
796
|
self.saveBundleInfo(id: id, bundle: info)
|
|
782
797
|
self.cleanDownloadData()
|
|
798
|
+
|
|
799
|
+
// Send stats for zip download complete
|
|
800
|
+
self.sendStats(action: "download_zip_complete", versionName: version)
|
|
801
|
+
|
|
783
802
|
self.notifyDownload(id: id, percent: 100)
|
|
784
803
|
logger.info("Downloading: 100% (complete)")
|
|
785
804
|
return info
|