@capgo/capacitor-updater 6.7.7-alpha.2 → 6.7.7-alpha.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/android/build.gradle +4 -1
- package/android/src/main/AndroidManifest.xml +0 -5
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +233 -192
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1 -3
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +75 -131
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +111 -0
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +1 -1
- package/package.json +1 -1
package/android/build.gradle
CHANGED
|
@@ -49,10 +49,13 @@ repositories {
|
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
dependencies {
|
|
52
|
+
def work_version = "2.9.1"
|
|
53
|
+
implementation "androidx.work:work-runtime:$work_version"
|
|
54
|
+
implementation "com.google.android.gms:play-services-tasks:18.2.0"
|
|
55
|
+
implementation "com.google.guava:guava:32.1.3-android"
|
|
52
56
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
53
57
|
implementation project(':capacitor-android')
|
|
54
58
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
55
|
-
// implementation 'com.android.volley:volley:1.2.1'
|
|
56
59
|
implementation 'io.github.g00fy2:versioncompare:1.5.0'
|
|
57
60
|
implementation 'com.google.code.gson:gson:2.11.0'
|
|
58
61
|
testImplementation "junit:junit:$junitVersion"
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
-
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
3
|
-
|
|
4
2
|
<application>
|
|
5
|
-
<service
|
|
6
|
-
android:name="ee.forgr.capacitor_updater.DownloadService"
|
|
7
|
-
android:exported="false" />
|
|
8
3
|
</application>
|
|
9
4
|
</manifest>
|
|
@@ -6,21 +6,21 @@
|
|
|
6
6
|
|
|
7
7
|
package ee.forgr.capacitor_updater;
|
|
8
8
|
|
|
9
|
-
import static android.content.Context.RECEIVER_NOT_EXPORTED;
|
|
10
|
-
|
|
11
|
-
import android.annotation.SuppressLint;
|
|
12
9
|
import android.app.Activity;
|
|
13
|
-
import android.content.BroadcastReceiver;
|
|
14
10
|
import android.content.Context;
|
|
15
|
-
import android.content.Intent;
|
|
16
|
-
import android.content.IntentFilter;
|
|
17
11
|
import android.content.SharedPreferences;
|
|
18
12
|
import android.os.Build;
|
|
19
|
-
import android.os.Bundle;
|
|
20
13
|
import android.util.Base64;
|
|
21
14
|
import android.util.Log;
|
|
15
|
+
import androidx.annotation.NonNull;
|
|
16
|
+
import androidx.lifecycle.LifecycleOwner;
|
|
17
|
+
import androidx.work.Data;
|
|
18
|
+
import androidx.work.WorkInfo;
|
|
19
|
+
import androidx.work.WorkManager;
|
|
22
20
|
import com.getcapacitor.JSObject;
|
|
23
21
|
import com.getcapacitor.plugin.WebView;
|
|
22
|
+
import com.google.common.util.concurrent.Futures;
|
|
23
|
+
import com.google.common.util.concurrent.ListenableFuture;
|
|
24
24
|
import java.io.BufferedInputStream;
|
|
25
25
|
import java.io.DataInputStream;
|
|
26
26
|
import java.io.File;
|
|
@@ -29,9 +29,6 @@ import java.io.FileNotFoundException;
|
|
|
29
29
|
import java.io.FileOutputStream;
|
|
30
30
|
import java.io.FilenameFilter;
|
|
31
31
|
import java.io.IOException;
|
|
32
|
-
import java.io.InputStream;
|
|
33
|
-
import java.net.URL;
|
|
34
|
-
import java.net.URLConnection;
|
|
35
32
|
import java.security.GeneralSecurityException;
|
|
36
33
|
import java.security.MessageDigest;
|
|
37
34
|
import java.security.PrivateKey;
|
|
@@ -251,87 +248,130 @@ public class CapacitorUpdater {
|
|
|
251
248
|
sourceFile.delete();
|
|
252
249
|
}
|
|
253
250
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
} else {
|
|
262
|
-
this.activity.registerReceiver(receiver, filter);
|
|
251
|
+
private void observeWorkProgress(Context context, String id) {
|
|
252
|
+
if (!(context instanceof LifecycleOwner)) {
|
|
253
|
+
Log.e(
|
|
254
|
+
TAG,
|
|
255
|
+
"Context is not a LifecycleOwner, cannot observe work progress"
|
|
256
|
+
);
|
|
257
|
+
return;
|
|
263
258
|
}
|
|
264
|
-
}
|
|
265
259
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
260
|
+
activity.runOnUiThread(() -> {
|
|
261
|
+
WorkManager.getInstance(context)
|
|
262
|
+
.getWorkInfosByTagLiveData(id)
|
|
263
|
+
.observe((LifecycleOwner) context, workInfos -> {
|
|
264
|
+
if (workInfos == null || workInfos.isEmpty()) return;
|
|
265
|
+
|
|
266
|
+
WorkInfo workInfo = workInfos.get(0);
|
|
267
|
+
Data progress = workInfo.getProgress();
|
|
268
|
+
|
|
269
|
+
switch (workInfo.getState()) {
|
|
270
|
+
case RUNNING:
|
|
271
|
+
int percent = progress.getInt(DownloadService.PERCENT, 0);
|
|
272
|
+
notifyDownload(id, percent);
|
|
273
|
+
break;
|
|
274
|
+
case SUCCEEDED:
|
|
275
|
+
Data outputData = workInfo.getOutputData();
|
|
276
|
+
String dest = outputData.getString(DownloadService.FILEDEST);
|
|
277
|
+
String version = outputData.getString(DownloadService.VERSION);
|
|
278
|
+
String sessionKey = outputData.getString(
|
|
279
|
+
DownloadService.SESSIONKEY
|
|
280
|
+
);
|
|
281
|
+
String checksum = outputData.getString(DownloadService.CHECKSUM);
|
|
282
|
+
boolean isManifest = outputData.getBoolean(
|
|
283
|
+
DownloadService.IS_MANIFEST,
|
|
284
|
+
false
|
|
285
|
+
);
|
|
269
286
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
287
|
+
boolean success = finishDownload(
|
|
288
|
+
id,
|
|
289
|
+
dest,
|
|
290
|
+
version,
|
|
291
|
+
sessionKey,
|
|
292
|
+
checksum,
|
|
293
|
+
true,
|
|
294
|
+
isManifest
|
|
295
|
+
);
|
|
296
|
+
if (!success) {
|
|
297
|
+
saveBundleInfo(
|
|
298
|
+
id,
|
|
299
|
+
new BundleInfo(
|
|
300
|
+
id,
|
|
301
|
+
version,
|
|
302
|
+
BundleStatus.ERROR,
|
|
303
|
+
new Date(System.currentTimeMillis()),
|
|
304
|
+
""
|
|
305
|
+
)
|
|
306
|
+
);
|
|
307
|
+
JSObject ret = new JSObject();
|
|
308
|
+
ret.put("version", getCurrentBundle().getVersionName());
|
|
309
|
+
ret.put("error", "finish_download_fail");
|
|
310
|
+
sendStats("finish_download_fail", version);
|
|
311
|
+
notifyListeners("downloadFailed", ret);
|
|
312
|
+
}
|
|
313
|
+
break;
|
|
314
|
+
case FAILED:
|
|
315
|
+
Data failedData = workInfo.getOutputData();
|
|
316
|
+
String error = failedData.getString(DownloadService.ERROR);
|
|
317
|
+
String failedVersion = failedData.getString(
|
|
318
|
+
DownloadService.VERSION
|
|
319
|
+
);
|
|
320
|
+
saveBundleInfo(
|
|
321
|
+
id,
|
|
322
|
+
new BundleInfo(
|
|
323
|
+
id,
|
|
324
|
+
failedVersion,
|
|
325
|
+
BundleStatus.ERROR,
|
|
326
|
+
new Date(System.currentTimeMillis()),
|
|
327
|
+
""
|
|
328
|
+
)
|
|
329
|
+
);
|
|
330
|
+
JSObject ret = new JSObject();
|
|
331
|
+
ret.put("version", getCurrentBundle().getVersionName());
|
|
332
|
+
if ("low_mem_fail".equals(error)) {
|
|
333
|
+
sendStats("low_mem_fail", failedVersion);
|
|
334
|
+
}
|
|
335
|
+
ret.put("error", error != null ? error : "download_fail");
|
|
336
|
+
sendStats("download_fail", failedVersion);
|
|
337
|
+
notifyListeners("downloadFailed", ret);
|
|
338
|
+
break;
|
|
319
339
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private void download(
|
|
345
|
+
final String id,
|
|
346
|
+
final String url,
|
|
347
|
+
final String dest,
|
|
348
|
+
final String version,
|
|
349
|
+
final String sessionKey,
|
|
350
|
+
final String checksum,
|
|
351
|
+
final JSONArray manifest
|
|
352
|
+
) {
|
|
353
|
+
if (this.activity == null) {
|
|
354
|
+
Log.e(TAG, "Activity is null, cannot observe work progress");
|
|
355
|
+
return;
|
|
333
356
|
}
|
|
334
|
-
|
|
357
|
+
observeWorkProgress(this.activity, id);
|
|
358
|
+
|
|
359
|
+
DownloadWorkerManager.enqueueDownload(
|
|
360
|
+
this.activity,
|
|
361
|
+
url,
|
|
362
|
+
id,
|
|
363
|
+
this.documentsDir.getAbsolutePath(),
|
|
364
|
+
dest,
|
|
365
|
+
version,
|
|
366
|
+
sessionKey,
|
|
367
|
+
checksum,
|
|
368
|
+
manifest != null
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
if (manifest != null) {
|
|
372
|
+
DataManager.getInstance().setManifest(manifest);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
335
375
|
|
|
336
376
|
private String decryptChecksum(String checksum, String version)
|
|
337
377
|
throws IOException {
|
|
@@ -457,77 +497,6 @@ public class CapacitorUpdater {
|
|
|
457
497
|
return true;
|
|
458
498
|
}
|
|
459
499
|
|
|
460
|
-
private void downloadFileBackground(
|
|
461
|
-
final String id,
|
|
462
|
-
final String url,
|
|
463
|
-
final String version,
|
|
464
|
-
final String sessionKey,
|
|
465
|
-
final String checksum,
|
|
466
|
-
final String dest,
|
|
467
|
-
final JSONArray manifest
|
|
468
|
-
) {
|
|
469
|
-
Intent intent = new Intent(this.activity, DownloadService.class);
|
|
470
|
-
intent.putExtra(DownloadService.URL, url);
|
|
471
|
-
intent.putExtra(DownloadService.FILEDEST, dest);
|
|
472
|
-
intent.putExtra(
|
|
473
|
-
DownloadService.DOCDIR,
|
|
474
|
-
this.documentsDir.getAbsolutePath()
|
|
475
|
-
);
|
|
476
|
-
intent.putExtra(DownloadService.ID, id);
|
|
477
|
-
intent.putExtra(DownloadService.VERSION, version);
|
|
478
|
-
intent.putExtra(DownloadService.SESSIONKEY, sessionKey);
|
|
479
|
-
intent.putExtra(DownloadService.CHECKSUM, checksum);
|
|
480
|
-
if (manifest != null) {
|
|
481
|
-
DataManager.getInstance().setManifest(manifest);
|
|
482
|
-
intent.putExtra(DownloadService.IS_MANIFEST, true);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
this.activity.startService(intent);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
private void downloadFile(
|
|
489
|
-
final String id,
|
|
490
|
-
final String url,
|
|
491
|
-
final String dest
|
|
492
|
-
) throws IOException {
|
|
493
|
-
final URL u = new URL(url);
|
|
494
|
-
final URLConnection connection = u.openConnection();
|
|
495
|
-
|
|
496
|
-
final File target = new File(this.documentsDir, dest);
|
|
497
|
-
Objects.requireNonNull(target.getParentFile()).mkdirs();
|
|
498
|
-
target.createNewFile();
|
|
499
|
-
|
|
500
|
-
final long totalLength = connection.getContentLength();
|
|
501
|
-
int bytesRead = 0;
|
|
502
|
-
int percent = 0;
|
|
503
|
-
this.notifyDownload(id, 10);
|
|
504
|
-
|
|
505
|
-
try (
|
|
506
|
-
InputStream is = connection.getInputStream();
|
|
507
|
-
DataInputStream dis = new DataInputStream(is);
|
|
508
|
-
FileOutputStream fos = new FileOutputStream(target)
|
|
509
|
-
) {
|
|
510
|
-
byte[] buffer = new byte[8192];
|
|
511
|
-
int length;
|
|
512
|
-
while ((length = dis.read(buffer)) > 0) {
|
|
513
|
-
fos.write(buffer, 0, length);
|
|
514
|
-
bytesRead += length;
|
|
515
|
-
final int newPercent = (int) ((bytesRead / (float) totalLength) * 100);
|
|
516
|
-
if (totalLength > 1 && newPercent != percent) {
|
|
517
|
-
percent = newPercent;
|
|
518
|
-
this.notifyDownload(id, this.calcTotalPercent(percent, 10, 70));
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
if (bytesRead == 0) {
|
|
522
|
-
throw new IOException("Failed to download: No data read from URL");
|
|
523
|
-
}
|
|
524
|
-
} catch (OutOfMemoryError e) {
|
|
525
|
-
Log.e(TAG, "OutOfMemoryError while downloading file", e);
|
|
526
|
-
this.sendStats("low_mem_fail");
|
|
527
|
-
throw new IOException("OutOfMemoryError while downloading file");
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
500
|
private void deleteDirectory(final File file) throws IOException {
|
|
532
501
|
if (file.isDirectory()) {
|
|
533
502
|
final File[] entries = file.listFiles();
|
|
@@ -727,6 +696,16 @@ public class CapacitorUpdater {
|
|
|
727
696
|
final JSONArray manifest
|
|
728
697
|
) {
|
|
729
698
|
final String id = this.randomString();
|
|
699
|
+
|
|
700
|
+
// Check if version is already downloading
|
|
701
|
+
if (
|
|
702
|
+
this.activity != null &&
|
|
703
|
+
DownloadWorkerManager.isVersionDownloading(version)
|
|
704
|
+
) {
|
|
705
|
+
Log.i(TAG, "Version already downloading: " + version);
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
|
|
730
709
|
this.saveBundleInfo(
|
|
731
710
|
id,
|
|
732
711
|
new BundleInfo(
|
|
@@ -740,13 +719,13 @@ public class CapacitorUpdater {
|
|
|
740
719
|
this.notifyDownload(id, 0);
|
|
741
720
|
this.notifyDownload(id, 5);
|
|
742
721
|
|
|
743
|
-
this.
|
|
722
|
+
this.download(
|
|
744
723
|
id,
|
|
745
724
|
url,
|
|
725
|
+
this.randomString(),
|
|
746
726
|
version,
|
|
747
727
|
sessionKey,
|
|
748
728
|
checksum,
|
|
749
|
-
this.randomString(),
|
|
750
729
|
manifest
|
|
751
730
|
);
|
|
752
731
|
}
|
|
@@ -771,29 +750,68 @@ public class CapacitorUpdater {
|
|
|
771
750
|
this.notifyDownload(id, 0);
|
|
772
751
|
this.notifyDownload(id, 5);
|
|
773
752
|
final String dest = this.randomString();
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
753
|
+
|
|
754
|
+
// Use the new WorkManager-based download
|
|
755
|
+
this.download(id, url, dest, version, sessionKey, checksum, null);
|
|
756
|
+
|
|
757
|
+
// Wait for completion
|
|
758
|
+
try {
|
|
759
|
+
ListenableFuture<List<WorkInfo>> future = WorkManager.getInstance(
|
|
760
|
+
activity
|
|
761
|
+
).getWorkInfosByTag(id);
|
|
762
|
+
|
|
763
|
+
List<WorkInfo> workInfos = Futures.getChecked(future, IOException.class);
|
|
764
|
+
|
|
765
|
+
if (workInfos != null && !workInfos.isEmpty()) {
|
|
766
|
+
WorkInfo workInfo = workInfos.get(0);
|
|
767
|
+
while (!workInfo.getState().isFinished()) {
|
|
768
|
+
Thread.sleep(100);
|
|
769
|
+
workInfos = Futures.getChecked(
|
|
770
|
+
WorkManager.getInstance(activity).getWorkInfosByTag(id),
|
|
771
|
+
IOException.class
|
|
772
|
+
);
|
|
773
|
+
if (workInfos != null && !workInfos.isEmpty()) {
|
|
774
|
+
workInfo = workInfos.get(0);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
if (workInfo.getState() == WorkInfo.State.SUCCEEDED) {
|
|
779
|
+
Data outputData = workInfo.getOutputData();
|
|
780
|
+
boolean success = finishDownload(
|
|
781
|
+
id,
|
|
782
|
+
outputData.getString(DownloadService.FILEDEST),
|
|
783
|
+
version,
|
|
784
|
+
sessionKey,
|
|
785
|
+
checksum,
|
|
786
|
+
true,
|
|
787
|
+
false
|
|
788
|
+
);
|
|
789
|
+
if (!success) {
|
|
790
|
+
throw new IOException("Failed to finish download");
|
|
791
|
+
}
|
|
792
|
+
} else {
|
|
793
|
+
Data outputData = workInfo.getOutputData();
|
|
794
|
+
String error = outputData.getString(DownloadService.ERROR);
|
|
795
|
+
throw new IOException(
|
|
796
|
+
error != null ? error : "Download failed: " + workInfo.getState()
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return getBundleInfo(id);
|
|
801
|
+
} catch (Exception e) {
|
|
802
|
+
Log.e(TAG, "Error waiting for download", e);
|
|
803
|
+
saveBundleInfo(
|
|
804
|
+
id,
|
|
805
|
+
new BundleInfo(
|
|
777
806
|
id,
|
|
778
|
-
dest,
|
|
779
807
|
version,
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
: BundleStatus.ERROR;
|
|
788
|
-
BundleInfo info = new BundleInfo(
|
|
789
|
-
id,
|
|
790
|
-
version,
|
|
791
|
-
status,
|
|
792
|
-
new Date(System.currentTimeMillis()),
|
|
793
|
-
checksum
|
|
794
|
-
);
|
|
795
|
-
this.saveBundleInfo(id, info);
|
|
796
|
-
return info;
|
|
808
|
+
BundleStatus.ERROR,
|
|
809
|
+
new Date(System.currentTimeMillis()),
|
|
810
|
+
""
|
|
811
|
+
)
|
|
812
|
+
);
|
|
813
|
+
throw new IOException("Error waiting for download: " + e.getMessage());
|
|
814
|
+
}
|
|
797
815
|
}
|
|
798
816
|
|
|
799
817
|
public List<BundleInfo> list() {
|
|
@@ -818,6 +836,13 @@ public class CapacitorUpdater {
|
|
|
818
836
|
Log.e(TAG, "Cannot delete " + id);
|
|
819
837
|
return false;
|
|
820
838
|
}
|
|
839
|
+
// Cancel download for this version if active
|
|
840
|
+
if (this.activity != null) {
|
|
841
|
+
DownloadWorkerManager.cancelVersionDownload(
|
|
842
|
+
this.activity,
|
|
843
|
+
deleted.getVersionName()
|
|
844
|
+
);
|
|
845
|
+
}
|
|
821
846
|
final File bundle = new File(this.documentsDir, bundleDirectory + "/" + id);
|
|
822
847
|
if (bundle.exists()) {
|
|
823
848
|
this.deleteDirectory(bundle);
|
|
@@ -929,6 +954,10 @@ public class CapacitorUpdater {
|
|
|
929
954
|
this.setCurrentBundle(new File("public"));
|
|
930
955
|
this.setFallbackBundle(null);
|
|
931
956
|
this.setNextBundle(null);
|
|
957
|
+
// Cancel any ongoing downloads
|
|
958
|
+
if (this.activity != null) {
|
|
959
|
+
DownloadWorkerManager.cancelAllDownloads(this.activity);
|
|
960
|
+
}
|
|
932
961
|
if (!internal) {
|
|
933
962
|
this.sendStats(
|
|
934
963
|
"reset",
|
|
@@ -970,7 +999,7 @@ public class CapacitorUpdater {
|
|
|
970
999
|
.enqueue(
|
|
971
1000
|
new okhttp3.Callback() {
|
|
972
1001
|
@Override
|
|
973
|
-
public void onFailure(Call call, IOException e) {
|
|
1002
|
+
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
|
974
1003
|
JSObject retError = new JSObject();
|
|
975
1004
|
retError.put("message", "Request failed: " + e.getMessage());
|
|
976
1005
|
retError.put("error", "network_error");
|
|
@@ -978,8 +1007,10 @@ public class CapacitorUpdater {
|
|
|
978
1007
|
}
|
|
979
1008
|
|
|
980
1009
|
@Override
|
|
981
|
-
public void onResponse(
|
|
982
|
-
|
|
1010
|
+
public void onResponse(
|
|
1011
|
+
@NonNull Call call,
|
|
1012
|
+
@NonNull Response response
|
|
1013
|
+
) throws IOException {
|
|
983
1014
|
try (ResponseBody responseBody = response.body()) {
|
|
984
1015
|
if (!response.isSuccessful()) {
|
|
985
1016
|
JSObject retError = new JSObject();
|
|
@@ -989,6 +1020,7 @@ public class CapacitorUpdater {
|
|
|
989
1020
|
return;
|
|
990
1021
|
}
|
|
991
1022
|
|
|
1023
|
+
assert responseBody != null;
|
|
992
1024
|
String responseData = responseBody.string();
|
|
993
1025
|
JSONObject jsonResponse = new JSONObject(responseData);
|
|
994
1026
|
JSObject ret = new JSObject();
|
|
@@ -1068,7 +1100,7 @@ public class CapacitorUpdater {
|
|
|
1068
1100
|
.enqueue(
|
|
1069
1101
|
new okhttp3.Callback() {
|
|
1070
1102
|
@Override
|
|
1071
|
-
public void onFailure(Call call, IOException e) {
|
|
1103
|
+
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
|
1072
1104
|
JSObject retError = new JSObject();
|
|
1073
1105
|
retError.put("message", "Request failed: " + e.getMessage());
|
|
1074
1106
|
retError.put("error", "network_error");
|
|
@@ -1076,8 +1108,10 @@ public class CapacitorUpdater {
|
|
|
1076
1108
|
}
|
|
1077
1109
|
|
|
1078
1110
|
@Override
|
|
1079
|
-
public void onResponse(
|
|
1080
|
-
|
|
1111
|
+
public void onResponse(
|
|
1112
|
+
@NonNull Call call,
|
|
1113
|
+
@NonNull Response response
|
|
1114
|
+
) throws IOException {
|
|
1081
1115
|
try (ResponseBody responseBody = response.body()) {
|
|
1082
1116
|
if (!response.isSuccessful()) {
|
|
1083
1117
|
JSObject retError = new JSObject();
|
|
@@ -1087,6 +1121,7 @@ public class CapacitorUpdater {
|
|
|
1087
1121
|
return;
|
|
1088
1122
|
}
|
|
1089
1123
|
|
|
1124
|
+
assert responseBody != null;
|
|
1090
1125
|
String responseData = responseBody.string();
|
|
1091
1126
|
JSONObject jsonResponse = new JSONObject(responseData);
|
|
1092
1127
|
JSObject ret = new JSObject();
|
|
@@ -1171,7 +1206,7 @@ public class CapacitorUpdater {
|
|
|
1171
1206
|
.enqueue(
|
|
1172
1207
|
new okhttp3.Callback() {
|
|
1173
1208
|
@Override
|
|
1174
|
-
public void onFailure(Call call, IOException e) {
|
|
1209
|
+
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
|
1175
1210
|
JSObject retError = new JSObject();
|
|
1176
1211
|
retError.put("message", "Request failed: " + e.getMessage());
|
|
1177
1212
|
retError.put("error", "network_error");
|
|
@@ -1179,10 +1214,13 @@ public class CapacitorUpdater {
|
|
|
1179
1214
|
}
|
|
1180
1215
|
|
|
1181
1216
|
@Override
|
|
1182
|
-
public void onResponse(
|
|
1183
|
-
|
|
1217
|
+
public void onResponse(
|
|
1218
|
+
@NonNull Call call,
|
|
1219
|
+
@NonNull Response response
|
|
1220
|
+
) throws IOException {
|
|
1184
1221
|
try (ResponseBody responseBody = response.body()) {
|
|
1185
1222
|
if (response.code() == 400) {
|
|
1223
|
+
assert responseBody != null;
|
|
1186
1224
|
String data = responseBody.string();
|
|
1187
1225
|
if (
|
|
1188
1226
|
data.contains("channel_not_found") &&
|
|
@@ -1205,6 +1243,7 @@ public class CapacitorUpdater {
|
|
|
1205
1243
|
return;
|
|
1206
1244
|
}
|
|
1207
1245
|
|
|
1246
|
+
assert responseBody != null;
|
|
1208
1247
|
String responseData = responseBody.string();
|
|
1209
1248
|
JSONObject jsonResponse = new JSONObject(responseData);
|
|
1210
1249
|
JSObject ret = new JSObject();
|
|
@@ -1269,13 +1308,15 @@ public class CapacitorUpdater {
|
|
|
1269
1308
|
.enqueue(
|
|
1270
1309
|
new okhttp3.Callback() {
|
|
1271
1310
|
@Override
|
|
1272
|
-
public void onFailure(Call call, IOException e) {
|
|
1311
|
+
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
|
1273
1312
|
Log.e(TAG, "Failed to send stats: " + e.getMessage());
|
|
1274
1313
|
}
|
|
1275
1314
|
|
|
1276
1315
|
@Override
|
|
1277
|
-
public void onResponse(
|
|
1278
|
-
|
|
1316
|
+
public void onResponse(
|
|
1317
|
+
@NonNull Call call,
|
|
1318
|
+
@NonNull Response response
|
|
1319
|
+
) throws IOException {
|
|
1279
1320
|
if (response.isSuccessful()) {
|
|
1280
1321
|
Log.i(
|
|
1281
1322
|
TAG,
|
|
@@ -57,7 +57,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
57
57
|
private static final String channelUrlDefault =
|
|
58
58
|
"https://plugin.capgo.app/channel_self";
|
|
59
59
|
|
|
60
|
-
private final String PLUGIN_VERSION = "6.7.7-alpha.
|
|
60
|
+
private final String PLUGIN_VERSION = "6.7.7-alpha.3";
|
|
61
61
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
62
62
|
|
|
63
63
|
private SharedPreferences.Editor editor;
|
|
@@ -1658,13 +1658,11 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1658
1658
|
backgroundTask.interrupt();
|
|
1659
1659
|
}
|
|
1660
1660
|
this.implementation.activity = getActivity();
|
|
1661
|
-
this.implementation.onResume();
|
|
1662
1661
|
}
|
|
1663
1662
|
|
|
1664
1663
|
@Override
|
|
1665
1664
|
public void handleOnPause() {
|
|
1666
1665
|
this.implementation.activity = getActivity();
|
|
1667
|
-
this.implementation.onPause();
|
|
1668
1666
|
}
|
|
1669
1667
|
|
|
1670
1668
|
@Override
|
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
package ee.forgr.capacitor_updater;
|
|
7
7
|
|
|
8
|
-
import android.
|
|
9
|
-
import android.content.Intent;
|
|
10
|
-
import android.os.PowerManager;
|
|
8
|
+
import android.content.Context;
|
|
11
9
|
import android.util.Log;
|
|
10
|
+
import androidx.annotation.NonNull;
|
|
11
|
+
import androidx.work.Data;
|
|
12
|
+
import androidx.work.Worker;
|
|
13
|
+
import androidx.work.WorkerParameters;
|
|
12
14
|
import java.io.*;
|
|
13
15
|
import java.io.FileInputStream;
|
|
14
16
|
import java.net.HttpURLConnection;
|
|
@@ -18,6 +20,7 @@ import java.security.MessageDigest;
|
|
|
18
20
|
import java.util.ArrayList;
|
|
19
21
|
import java.util.Arrays;
|
|
20
22
|
import java.util.List;
|
|
23
|
+
import java.util.Objects;
|
|
21
24
|
import java.util.concurrent.ExecutorService;
|
|
22
25
|
import java.util.concurrent.Executors;
|
|
23
26
|
import java.util.concurrent.Future;
|
|
@@ -33,7 +36,7 @@ import org.brotli.dec.BrotliInputStream;
|
|
|
33
36
|
import org.json.JSONArray;
|
|
34
37
|
import org.json.JSONObject;
|
|
35
38
|
|
|
36
|
-
public class DownloadService extends
|
|
39
|
+
public class DownloadService extends Worker {
|
|
37
40
|
|
|
38
41
|
public static final String TAG = "Capacitor-updater";
|
|
39
42
|
public static final String URL = "URL";
|
|
@@ -45,68 +48,62 @@ public class DownloadService extends IntentService {
|
|
|
45
48
|
public static final String VERSION = "version";
|
|
46
49
|
public static final String SESSIONKEY = "sessionkey";
|
|
47
50
|
public static final String CHECKSUM = "checksum";
|
|
48
|
-
public static final String NOTIFICATION = "service receiver";
|
|
49
|
-
public static final String PERCENTDOWNLOAD = "percent receiver";
|
|
50
51
|
public static final String IS_MANIFEST = "is_manifest";
|
|
51
|
-
public static final String MANIFEST = "manifest";
|
|
52
52
|
private static final String UPDATE_FILE = "update.dat";
|
|
53
53
|
|
|
54
54
|
private final OkHttpClient client = new OkHttpClient.Builder()
|
|
55
55
|
.protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1))
|
|
56
56
|
.build();
|
|
57
|
-
private PowerManager.WakeLock wakeLock;
|
|
58
57
|
|
|
59
|
-
public DownloadService(
|
|
60
|
-
|
|
58
|
+
public DownloadService(
|
|
59
|
+
@NonNull Context context,
|
|
60
|
+
@NonNull WorkerParameters params
|
|
61
|
+
) {
|
|
62
|
+
super(context, params);
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
|
|
67
|
-
wakeLock = powerManager.newWakeLock(
|
|
68
|
-
PowerManager.PARTIAL_WAKE_LOCK,
|
|
69
|
-
"CapacitorUpdater::DownloadWakeLock"
|
|
70
|
-
);
|
|
65
|
+
private void setProgress(int percent) {
|
|
66
|
+
Data progress = new Data.Builder().putInt(PERCENT, percent).build();
|
|
67
|
+
setProgressAsync(progress);
|
|
71
68
|
}
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
Log.w(TAG + " DownloadService", "DownloadService killed/destroyed");
|
|
77
|
-
if (wakeLock != null && wakeLock.isHeld()) {
|
|
78
|
-
wakeLock.release();
|
|
79
|
-
}
|
|
70
|
+
private Result createFailureResult(String error) {
|
|
71
|
+
Data output = new Data.Builder().putString(ERROR, error).build();
|
|
72
|
+
return Result.failure(output);
|
|
80
73
|
}
|
|
81
74
|
|
|
82
|
-
private
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
75
|
+
private Result createSuccessResult(
|
|
76
|
+
String dest,
|
|
77
|
+
String version,
|
|
78
|
+
String sessionKey,
|
|
79
|
+
String checksum,
|
|
80
|
+
boolean isManifest
|
|
81
|
+
) {
|
|
82
|
+
Data output = new Data.Builder()
|
|
83
|
+
.putString(FILEDEST, dest)
|
|
84
|
+
.putString(VERSION, version)
|
|
85
|
+
.putString(SESSIONKEY, sessionKey)
|
|
86
|
+
.putString(CHECKSUM, checksum)
|
|
87
|
+
.putBoolean(IS_MANIFEST, isManifest)
|
|
88
|
+
.build();
|
|
89
|
+
return Result.success(output);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
@NonNull
|
|
92
93
|
@Override
|
|
93
|
-
|
|
94
|
+
public Result doWork() {
|
|
94
95
|
try {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
String
|
|
98
|
-
String
|
|
99
|
-
String
|
|
100
|
-
String
|
|
101
|
-
String
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
Log.d(
|
|
107
|
-
TAG + " DownloadService",
|
|
108
|
-
"onHandleIntent isManifest: " + isManifest
|
|
109
|
-
);
|
|
96
|
+
String url = getInputData().getString(URL);
|
|
97
|
+
String id = getInputData().getString(ID);
|
|
98
|
+
String documentsDir = getInputData().getString(DOCDIR);
|
|
99
|
+
String dest = getInputData().getString(FILEDEST);
|
|
100
|
+
String version = getInputData().getString(VERSION);
|
|
101
|
+
String sessionKey = getInputData().getString(SESSIONKEY);
|
|
102
|
+
String checksum = getInputData().getString(CHECKSUM);
|
|
103
|
+
boolean isManifest = getInputData().getBoolean(IS_MANIFEST, false);
|
|
104
|
+
|
|
105
|
+
Log.d(TAG, "doWork isManifest: " + isManifest);
|
|
106
|
+
|
|
110
107
|
if (isManifest) {
|
|
111
108
|
JSONArray manifest = DataManager.getInstance().getAndClearManifest();
|
|
112
109
|
if (manifest != null) {
|
|
@@ -118,17 +115,10 @@ public class DownloadService extends IntentService {
|
|
|
118
115
|
sessionKey,
|
|
119
116
|
manifest.toString()
|
|
120
117
|
);
|
|
118
|
+
return createSuccessResult(dest, version, sessionKey, checksum, true);
|
|
121
119
|
} else {
|
|
122
|
-
Log.e(TAG
|
|
123
|
-
|
|
124
|
-
"",
|
|
125
|
-
id,
|
|
126
|
-
version,
|
|
127
|
-
checksum,
|
|
128
|
-
sessionKey,
|
|
129
|
-
"Manifest is null",
|
|
130
|
-
false
|
|
131
|
-
);
|
|
120
|
+
Log.e(TAG, "Manifest is null");
|
|
121
|
+
return createFailureResult("Manifest is null");
|
|
132
122
|
}
|
|
133
123
|
} else {
|
|
134
124
|
handleSingleFileDownload(
|
|
@@ -140,12 +130,24 @@ public class DownloadService extends IntentService {
|
|
|
140
130
|
sessionKey,
|
|
141
131
|
checksum
|
|
142
132
|
);
|
|
133
|
+
return createSuccessResult(dest, version, sessionKey, checksum, false);
|
|
143
134
|
}
|
|
144
|
-
}
|
|
145
|
-
|
|
135
|
+
} catch (Exception e) {
|
|
136
|
+
Log.e(TAG, "Error in doWork", e);
|
|
137
|
+
return createFailureResult(e.getMessage());
|
|
146
138
|
}
|
|
147
139
|
}
|
|
148
140
|
|
|
141
|
+
private int calcTotalPercent(long downloadedBytes, long contentLength) {
|
|
142
|
+
if (contentLength <= 0) {
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
int percent = (int) (((double) downloadedBytes / contentLength) * 100);
|
|
146
|
+
percent = Math.max(10, percent);
|
|
147
|
+
percent = Math.min(70, percent);
|
|
148
|
+
return percent;
|
|
149
|
+
}
|
|
150
|
+
|
|
149
151
|
private void handleManifestDownload(
|
|
150
152
|
String id,
|
|
151
153
|
String documentsDir,
|
|
@@ -155,7 +157,7 @@ public class DownloadService extends IntentService {
|
|
|
155
157
|
String manifestString
|
|
156
158
|
) {
|
|
157
159
|
try {
|
|
158
|
-
Log.d(TAG
|
|
160
|
+
Log.d(TAG, "handleManifestDownload");
|
|
159
161
|
JSONArray manifest = new JSONArray(manifestString);
|
|
160
162
|
File destFolder = new File(documentsDir, dest);
|
|
161
163
|
File cacheFolder = new File(
|
|
@@ -204,7 +206,7 @@ public class DownloadService extends IntentService {
|
|
|
204
206
|
|
|
205
207
|
// Ensure parent directories of the target file exist
|
|
206
208
|
if (
|
|
207
|
-
!targetFile.getParentFile().exists() &&
|
|
209
|
+
!Objects.requireNonNull(targetFile.getParentFile()).exists() &&
|
|
208
210
|
!targetFile.getParentFile().mkdirs()
|
|
209
211
|
) {
|
|
210
212
|
throw new IOException(
|
|
@@ -217,12 +219,12 @@ public class DownloadService extends IntentService {
|
|
|
217
219
|
try {
|
|
218
220
|
if (builtinFile.exists() && verifyChecksum(builtinFile, fileHash)) {
|
|
219
221
|
copyFile(builtinFile, targetFile);
|
|
220
|
-
Log.d(TAG
|
|
222
|
+
Log.d(TAG, "using builtin file " + fileName);
|
|
221
223
|
} else if (
|
|
222
224
|
cacheFile.exists() && verifyChecksum(cacheFile, fileHash)
|
|
223
225
|
) {
|
|
224
226
|
copyFile(cacheFile, targetFile);
|
|
225
|
-
Log.d(TAG
|
|
227
|
+
Log.d(TAG, "already cached " + fileName);
|
|
226
228
|
} else {
|
|
227
229
|
downloadAndVerify(
|
|
228
230
|
downloadUrl,
|
|
@@ -235,13 +237,9 @@ public class DownloadService extends IntentService {
|
|
|
235
237
|
|
|
236
238
|
long completed = completedFiles.incrementAndGet();
|
|
237
239
|
int percent = calcTotalPercent(completed, totalFiles);
|
|
238
|
-
|
|
240
|
+
setProgress(percent);
|
|
239
241
|
} catch (Exception e) {
|
|
240
|
-
Log.e(
|
|
241
|
-
TAG + " DownloadService",
|
|
242
|
-
"Error processing file: " + fileName,
|
|
243
|
-
e
|
|
244
|
-
);
|
|
242
|
+
Log.e(TAG, "Error processing file: " + fileName, e);
|
|
245
243
|
hasError.set(true);
|
|
246
244
|
}
|
|
247
245
|
});
|
|
@@ -253,7 +251,7 @@ public class DownloadService extends IntentService {
|
|
|
253
251
|
try {
|
|
254
252
|
future.get();
|
|
255
253
|
} catch (Exception e) {
|
|
256
|
-
Log.e(TAG
|
|
254
|
+
Log.e(TAG, "Error waiting for download", e);
|
|
257
255
|
hasError.set(true);
|
|
258
256
|
}
|
|
259
257
|
}
|
|
@@ -271,11 +269,8 @@ public class DownloadService extends IntentService {
|
|
|
271
269
|
if (hasError.get()) {
|
|
272
270
|
throw new IOException("One or more files failed to download");
|
|
273
271
|
}
|
|
274
|
-
|
|
275
|
-
publishResults(dest, id, version, "", sessionKey, "", true);
|
|
276
272
|
} catch (Exception e) {
|
|
277
|
-
Log.e(TAG
|
|
278
|
-
publishResults("", id, version, "", sessionKey, e.getMessage(), true);
|
|
273
|
+
Log.e(TAG, "Error in handleManifestDownload", e);
|
|
279
274
|
}
|
|
280
275
|
}
|
|
281
276
|
|
|
@@ -308,14 +303,12 @@ public class DownloadService extends IntentService {
|
|
|
308
303
|
String updateVersion = reader.readLine();
|
|
309
304
|
if (!updateVersion.equals(version)) {
|
|
310
305
|
clearDownloadData(documentsDir);
|
|
311
|
-
downloadedBytes = 0;
|
|
312
306
|
} else {
|
|
313
307
|
downloadedBytes = tempFile.length();
|
|
314
308
|
}
|
|
315
309
|
}
|
|
316
310
|
} else {
|
|
317
311
|
clearDownloadData(documentsDir);
|
|
318
|
-
downloadedBytes = 0;
|
|
319
312
|
}
|
|
320
313
|
|
|
321
314
|
if (downloadedBytes > 0) {
|
|
@@ -331,7 +324,6 @@ public class DownloadService extends IntentService {
|
|
|
331
324
|
responseCode == HttpURLConnection.HTTP_OK ||
|
|
332
325
|
responseCode == HttpURLConnection.HTTP_PARTIAL
|
|
333
326
|
) {
|
|
334
|
-
String contentType = httpConn.getContentType();
|
|
335
327
|
long contentLength = httpConn.getContentLength() + downloadedBytes;
|
|
336
328
|
|
|
337
329
|
try (
|
|
@@ -375,7 +367,7 @@ public class DownloadService extends IntentService {
|
|
|
375
367
|
lastNotifiedPercent += 10;
|
|
376
368
|
// Artificial delay using CPU-bound calculation to take ~5 seconds
|
|
377
369
|
double result = 0;
|
|
378
|
-
|
|
370
|
+
setProgress(lastNotifiedPercent);
|
|
379
371
|
}
|
|
380
372
|
}
|
|
381
373
|
|
|
@@ -385,7 +377,6 @@ public class DownloadService extends IntentService {
|
|
|
385
377
|
// Rename the temp file with the final name (dest)
|
|
386
378
|
tempFile.renameTo(new File(documentsDir, dest));
|
|
387
379
|
infoFile.delete();
|
|
388
|
-
publishResults(dest, id, version, checksum, sessionKey, "", false);
|
|
389
380
|
}
|
|
390
381
|
} else {
|
|
391
382
|
infoFile.delete();
|
|
@@ -397,26 +388,10 @@ public class DownloadService extends IntentService {
|
|
|
397
388
|
}
|
|
398
389
|
} catch (OutOfMemoryError e) {
|
|
399
390
|
e.printStackTrace();
|
|
400
|
-
|
|
401
|
-
"",
|
|
402
|
-
id,
|
|
403
|
-
version,
|
|
404
|
-
checksum,
|
|
405
|
-
sessionKey,
|
|
406
|
-
"low_mem_fail",
|
|
407
|
-
false
|
|
408
|
-
);
|
|
391
|
+
throw new RuntimeException("low_mem_fail");
|
|
409
392
|
} catch (Exception e) {
|
|
410
393
|
e.printStackTrace();
|
|
411
|
-
|
|
412
|
-
"",
|
|
413
|
-
id,
|
|
414
|
-
version,
|
|
415
|
-
checksum,
|
|
416
|
-
sessionKey,
|
|
417
|
-
e.getLocalizedMessage(),
|
|
418
|
-
false
|
|
419
|
-
);
|
|
394
|
+
throw new RuntimeException(e.getLocalizedMessage());
|
|
420
395
|
}
|
|
421
396
|
}
|
|
422
397
|
|
|
@@ -433,37 +408,6 @@ public class DownloadService extends IntentService {
|
|
|
433
408
|
}
|
|
434
409
|
}
|
|
435
410
|
|
|
436
|
-
private void notifyDownload(String id, int percent) {
|
|
437
|
-
Intent intent = new Intent(PERCENTDOWNLOAD);
|
|
438
|
-
intent.setPackage(getPackageName());
|
|
439
|
-
intent.putExtra(ID, id);
|
|
440
|
-
intent.putExtra(PERCENT, percent);
|
|
441
|
-
sendBroadcast(intent);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
private void publishResults(
|
|
445
|
-
String dest,
|
|
446
|
-
String id,
|
|
447
|
-
String version,
|
|
448
|
-
String checksum,
|
|
449
|
-
String sessionKey,
|
|
450
|
-
String error,
|
|
451
|
-
boolean isManifest
|
|
452
|
-
) {
|
|
453
|
-
Intent intent = new Intent(NOTIFICATION);
|
|
454
|
-
intent.setPackage(getPackageName());
|
|
455
|
-
if (dest != null && !dest.isEmpty()) {
|
|
456
|
-
intent.putExtra(FILEDEST, dest);
|
|
457
|
-
}
|
|
458
|
-
intent.putExtra(ERROR, error);
|
|
459
|
-
intent.putExtra(ID, id);
|
|
460
|
-
intent.putExtra(VERSION, version);
|
|
461
|
-
intent.putExtra(SESSIONKEY, sessionKey);
|
|
462
|
-
intent.putExtra(CHECKSUM, checksum);
|
|
463
|
-
intent.putExtra(IS_MANIFEST, isManifest);
|
|
464
|
-
sendBroadcast(intent);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
411
|
// Helper methods
|
|
468
412
|
|
|
469
413
|
private void copyFile(File source, File dest) throws IOException {
|
|
@@ -484,7 +428,7 @@ public class DownloadService extends IntentService {
|
|
|
484
428
|
String expectedHash,
|
|
485
429
|
String id
|
|
486
430
|
) throws Exception {
|
|
487
|
-
Log.d(TAG
|
|
431
|
+
Log.d(TAG, "downloadAndVerify " + downloadUrl);
|
|
488
432
|
|
|
489
433
|
Request request = new Request.Builder().url(downloadUrl).build();
|
|
490
434
|
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
package ee.forgr.capacitor_updater;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.util.Log;
|
|
5
|
+
import androidx.work.BackoffPolicy;
|
|
6
|
+
import androidx.work.Configuration;
|
|
7
|
+
import androidx.work.Constraints;
|
|
8
|
+
import androidx.work.Data;
|
|
9
|
+
import androidx.work.NetworkType;
|
|
10
|
+
import androidx.work.OneTimeWorkRequest;
|
|
11
|
+
import androidx.work.WorkManager;
|
|
12
|
+
import androidx.work.WorkRequest;
|
|
13
|
+
import java.util.HashSet;
|
|
14
|
+
import java.util.Set;
|
|
15
|
+
import java.util.concurrent.TimeUnit;
|
|
16
|
+
|
|
17
|
+
public class DownloadWorkerManager {
|
|
18
|
+
|
|
19
|
+
private static final String TAG = "DownloadWorkerManager";
|
|
20
|
+
private static volatile boolean isInitialized = false;
|
|
21
|
+
private static final Set<String> activeVersions = new HashSet<>();
|
|
22
|
+
|
|
23
|
+
private static synchronized void initializeIfNeeded(Context context) {
|
|
24
|
+
if (!isInitialized) {
|
|
25
|
+
try {
|
|
26
|
+
Configuration config = new Configuration.Builder()
|
|
27
|
+
.setMinimumLoggingLevel(android.util.Log.INFO)
|
|
28
|
+
.build();
|
|
29
|
+
WorkManager.initialize(context, config);
|
|
30
|
+
isInitialized = true;
|
|
31
|
+
} catch (IllegalStateException e) {
|
|
32
|
+
// WorkManager was already initialized, ignore
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public static synchronized boolean isVersionDownloading(String version) {
|
|
38
|
+
return activeVersions.contains(version);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public static void enqueueDownload(
|
|
42
|
+
Context context,
|
|
43
|
+
String url,
|
|
44
|
+
String id,
|
|
45
|
+
String documentsDir,
|
|
46
|
+
String dest,
|
|
47
|
+
String version,
|
|
48
|
+
String sessionKey,
|
|
49
|
+
String checksum,
|
|
50
|
+
boolean isManifest
|
|
51
|
+
) {
|
|
52
|
+
initializeIfNeeded(context.getApplicationContext());
|
|
53
|
+
|
|
54
|
+
// If version is already downloading, don't start another one
|
|
55
|
+
if (isVersionDownloading(version)) {
|
|
56
|
+
Log.i(TAG, "Version " + version + " is already downloading");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
activeVersions.add(version);
|
|
60
|
+
|
|
61
|
+
// Create input data
|
|
62
|
+
Data inputData = new Data.Builder()
|
|
63
|
+
.putString(DownloadService.URL, url)
|
|
64
|
+
.putString(DownloadService.ID, id)
|
|
65
|
+
.putString(DownloadService.DOCDIR, documentsDir)
|
|
66
|
+
.putString(DownloadService.FILEDEST, dest)
|
|
67
|
+
.putString(DownloadService.VERSION, version)
|
|
68
|
+
.putString(DownloadService.SESSIONKEY, sessionKey)
|
|
69
|
+
.putString(DownloadService.CHECKSUM, checksum)
|
|
70
|
+
.putBoolean(DownloadService.IS_MANIFEST, isManifest)
|
|
71
|
+
.build();
|
|
72
|
+
|
|
73
|
+
// Create network constraints
|
|
74
|
+
Constraints constraints = new Constraints.Builder()
|
|
75
|
+
.setRequiredNetworkType(NetworkType.CONNECTED)
|
|
76
|
+
.build();
|
|
77
|
+
|
|
78
|
+
// Create work request with tags for tracking
|
|
79
|
+
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(
|
|
80
|
+
DownloadService.class
|
|
81
|
+
)
|
|
82
|
+
.setConstraints(constraints)
|
|
83
|
+
.setInputData(inputData)
|
|
84
|
+
.addTag(id)
|
|
85
|
+
.addTag(version) // Add version tag for tracking
|
|
86
|
+
.addTag("capacitor_updater_download")
|
|
87
|
+
.setBackoffCriteria(
|
|
88
|
+
BackoffPolicy.LINEAR,
|
|
89
|
+
WorkRequest.MIN_BACKOFF_MILLIS,
|
|
90
|
+
TimeUnit.MILLISECONDS
|
|
91
|
+
)
|
|
92
|
+
.build();
|
|
93
|
+
|
|
94
|
+
// Enqueue work
|
|
95
|
+
WorkManager.getInstance(context).enqueue(workRequest);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public static void cancelVersionDownload(Context context, String version) {
|
|
99
|
+
initializeIfNeeded(context.getApplicationContext());
|
|
100
|
+
WorkManager.getInstance(context).cancelAllWorkByTag(version);
|
|
101
|
+
activeVersions.remove(version);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public static void cancelAllDownloads(Context context) {
|
|
105
|
+
initializeIfNeeded(context.getApplicationContext());
|
|
106
|
+
WorkManager.getInstance(context).cancelAllWorkByTag(
|
|
107
|
+
"capacitor_updater_download"
|
|
108
|
+
);
|
|
109
|
+
activeVersions.clear();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -44,7 +44,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
44
44
|
CAPPluginMethod(name: "isAutoUpdateAvailable", returnType: CAPPluginReturnPromise)
|
|
45
45
|
]
|
|
46
46
|
public var implementation = CapacitorUpdater()
|
|
47
|
-
private let PLUGIN_VERSION: String = "6.7.7-alpha.
|
|
47
|
+
private let PLUGIN_VERSION: String = "6.7.7-alpha.3"
|
|
48
48
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
49
49
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
50
50
|
static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
|