@capgo/capacitor-updater 7.8.11 → 7.9.1
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 +3 -3
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +3 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +25 -5
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +22 -9
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +20 -13
- package/ios/Plugin/CapgoUpdater.swift +20 -8
- package/package.json +1 -1
|
@@ -59,7 +59,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
59
59
|
private static final String statsUrlDefault = "https://plugin.capgo.app/stats";
|
|
60
60
|
private static final String channelUrlDefault = "https://plugin.capgo.app/channel_self";
|
|
61
61
|
|
|
62
|
-
private final String PLUGIN_VERSION = "7.
|
|
62
|
+
private final String PLUGIN_VERSION = "7.9.1";
|
|
63
63
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
64
64
|
|
|
65
65
|
private SharedPreferences.Editor editor;
|
|
@@ -233,7 +233,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
233
233
|
this.implementation.versionOs = Build.VERSION.RELEASE;
|
|
234
234
|
this.implementation.deviceID = this.prefs.getString("appUUID", UUID.randomUUID().toString()).toLowerCase();
|
|
235
235
|
this.editor.putString("appUUID", this.implementation.deviceID);
|
|
236
|
-
this.editor.
|
|
236
|
+
this.editor.apply();
|
|
237
237
|
logger.info("init for device " + this.implementation.deviceID);
|
|
238
238
|
logger.info("version native " + this.currentVersionNative.getOriginalString());
|
|
239
239
|
this.autoDeleteFailed = this.getConfig().getBoolean("autoDeleteFailed", true);
|
|
@@ -455,7 +455,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
455
455
|
logger.error("Error calculating previous native version " + e.getMessage());
|
|
456
456
|
}
|
|
457
457
|
this.editor.putString("LatestVersionNative", this.currentVersionNative.toString());
|
|
458
|
-
this.editor.
|
|
458
|
+
this.editor.apply();
|
|
459
459
|
}
|
|
460
460
|
|
|
461
461
|
public void notifyDownload(final String id, final int percent) {
|
|
@@ -27,6 +27,7 @@ import java.util.concurrent.Future;
|
|
|
27
27
|
import java.util.concurrent.TimeUnit;
|
|
28
28
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
29
29
|
import java.util.concurrent.atomic.AtomicLong;
|
|
30
|
+
import okhttp3.Interceptor;
|
|
30
31
|
import okhttp3.OkHttpClient;
|
|
31
32
|
import okhttp3.Protocol;
|
|
32
33
|
import okhttp3.Request;
|
|
@@ -55,12 +56,31 @@ public class DownloadService extends Worker {
|
|
|
55
56
|
public static final String CHECKSUM = "checksum";
|
|
56
57
|
public static final String PUBLIC_KEY = "publickey";
|
|
57
58
|
public static final String IS_MANIFEST = "is_manifest";
|
|
59
|
+
public static final String APP_ID = "app_id";
|
|
60
|
+
public static final String PLUGIN_VERSION = "plugin_version";
|
|
58
61
|
private static final String UPDATE_FILE = "update.dat";
|
|
59
62
|
|
|
60
|
-
private final OkHttpClient client
|
|
63
|
+
private final OkHttpClient client;
|
|
61
64
|
|
|
62
65
|
public DownloadService(@NonNull Context context, @NonNull WorkerParameters params) {
|
|
63
66
|
super(context, params);
|
|
67
|
+
// Get appId and plugin version from input data
|
|
68
|
+
String appId = getInputData().getString(APP_ID);
|
|
69
|
+
String pluginVersion = getInputData().getString(PLUGIN_VERSION);
|
|
70
|
+
|
|
71
|
+
// Build user agent with appId and plugin version
|
|
72
|
+
String userAgent =
|
|
73
|
+
"CapacitorUpdater/" + (pluginVersion != null ? pluginVersion : "unknown") + " (" + (appId != null ? appId : "unknown") + ")";
|
|
74
|
+
|
|
75
|
+
// Create OkHttpClient with custom user agent
|
|
76
|
+
this.client = new OkHttpClient.Builder()
|
|
77
|
+
.protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1))
|
|
78
|
+
.addInterceptor(chain -> {
|
|
79
|
+
Request originalRequest = chain.request();
|
|
80
|
+
Request requestWithUserAgent = originalRequest.newBuilder().header("User-Agent", userAgent).build();
|
|
81
|
+
return chain.proceed(requestWithUserAgent);
|
|
82
|
+
})
|
|
83
|
+
.build();
|
|
64
84
|
}
|
|
65
85
|
|
|
66
86
|
private void setProgress(int percent) {
|
|
@@ -466,14 +486,14 @@ public class DownloadService extends Worker {
|
|
|
466
486
|
|
|
467
487
|
private String calculateFileHash(File file) throws Exception {
|
|
468
488
|
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
|
469
|
-
FileInputStream fis = new FileInputStream(file);
|
|
470
489
|
byte[] byteArray = new byte[1024];
|
|
471
490
|
int bytesCount = 0;
|
|
472
491
|
|
|
473
|
-
|
|
474
|
-
|
|
492
|
+
try (FileInputStream fis = new FileInputStream(file)) {
|
|
493
|
+
while ((bytesCount = fis.read(byteArray)) != -1) {
|
|
494
|
+
digest.update(byteArray, 0, bytesCount);
|
|
495
|
+
}
|
|
475
496
|
}
|
|
476
|
-
fis.close();
|
|
477
497
|
|
|
478
498
|
byte[] bytes = digest.digest();
|
|
479
499
|
StringBuilder sb = new StringBuilder();
|
|
@@ -23,6 +23,7 @@ public class DownloadWorkerManager {
|
|
|
23
23
|
|
|
24
24
|
private static volatile boolean isInitialized = false;
|
|
25
25
|
private static final Set<String> activeVersions = new HashSet<>();
|
|
26
|
+
private static final Object activeVersionsLock = new Object();
|
|
26
27
|
|
|
27
28
|
private static synchronized void initializeIfNeeded(Context context) {
|
|
28
29
|
if (!isInitialized) {
|
|
@@ -36,8 +37,10 @@ public class DownloadWorkerManager {
|
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
public static
|
|
40
|
-
|
|
40
|
+
public static boolean isVersionDownloading(String version) {
|
|
41
|
+
synchronized (activeVersionsLock) {
|
|
42
|
+
return activeVersions.contains(version);
|
|
43
|
+
}
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
public static void enqueueDownload(
|
|
@@ -51,16 +54,20 @@ public class DownloadWorkerManager {
|
|
|
51
54
|
String checksum,
|
|
52
55
|
String publicKey,
|
|
53
56
|
boolean isManifest,
|
|
54
|
-
boolean isEmulator
|
|
57
|
+
boolean isEmulator,
|
|
58
|
+
String appId,
|
|
59
|
+
String pluginVersion
|
|
55
60
|
) {
|
|
56
61
|
initializeIfNeeded(context.getApplicationContext());
|
|
57
62
|
|
|
58
63
|
// If version is already downloading, don't start another one
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
synchronized (activeVersionsLock) {
|
|
65
|
+
if (activeVersions.contains(version)) {
|
|
66
|
+
logger.info("Version " + version + " is already downloading");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
activeVersions.add(version);
|
|
62
70
|
}
|
|
63
|
-
activeVersions.add(version);
|
|
64
71
|
|
|
65
72
|
// Create input data
|
|
66
73
|
Data inputData = new Data.Builder()
|
|
@@ -73,6 +80,8 @@ public class DownloadWorkerManager {
|
|
|
73
80
|
.putString(DownloadService.CHECKSUM, checksum)
|
|
74
81
|
.putBoolean(DownloadService.IS_MANIFEST, isManifest)
|
|
75
82
|
.putString(DownloadService.PUBLIC_KEY, publicKey)
|
|
83
|
+
.putString(DownloadService.APP_ID, appId)
|
|
84
|
+
.putString(DownloadService.PLUGIN_VERSION, pluginVersion)
|
|
76
85
|
.build();
|
|
77
86
|
|
|
78
87
|
// Create network constraints - be more lenient on emulators
|
|
@@ -110,12 +119,16 @@ public class DownloadWorkerManager {
|
|
|
110
119
|
public static void cancelVersionDownload(Context context, String version) {
|
|
111
120
|
initializeIfNeeded(context.getApplicationContext());
|
|
112
121
|
WorkManager.getInstance(context).cancelAllWorkByTag(version);
|
|
113
|
-
|
|
122
|
+
synchronized (activeVersionsLock) {
|
|
123
|
+
activeVersions.remove(version);
|
|
124
|
+
}
|
|
114
125
|
}
|
|
115
126
|
|
|
116
127
|
public static void cancelAllDownloads(Context context) {
|
|
117
128
|
initializeIfNeeded(context.getApplicationContext());
|
|
118
129
|
WorkManager.getInstance(context).cancelAllWorkByTag("capacitor_updater_download");
|
|
119
|
-
|
|
130
|
+
synchronized (activeVersionsLock) {
|
|
131
|
+
activeVersions.clear();
|
|
132
|
+
}
|
|
120
133
|
}
|
|
121
134
|
}
|
|
@@ -13,12 +13,12 @@ import WebKit
|
|
|
13
13
|
class WebViewLoadDelegate: NSObject, WKNavigationDelegate {
|
|
14
14
|
private let completion: (Bool) -> Void
|
|
15
15
|
private var hasCompleted = false
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
init(completion: @escaping (Bool) -> Void) {
|
|
18
18
|
self.completion = completion
|
|
19
19
|
super.init()
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
23
23
|
if !hasCompleted {
|
|
24
24
|
hasCompleted = true
|
|
@@ -26,7 +26,7 @@ class WebViewLoadDelegate: NSObject, WKNavigationDelegate {
|
|
|
26
26
|
completion(true)
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
|
31
31
|
if !hasCompleted {
|
|
32
32
|
hasCompleted = true
|
|
@@ -34,7 +34,7 @@ class WebViewLoadDelegate: NSObject, WKNavigationDelegate {
|
|
|
34
34
|
completion(false)
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
|
39
39
|
if !hasCompleted {
|
|
40
40
|
hasCompleted = true
|
|
@@ -86,7 +86,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
86
86
|
CAPPluginMethod(name: "isShakeMenuEnabled", returnType: CAPPluginReturnPromise)
|
|
87
87
|
]
|
|
88
88
|
public var implementation = CapgoUpdater()
|
|
89
|
-
private let PLUGIN_VERSION: String = "7.
|
|
89
|
+
private let PLUGIN_VERSION: String = "7.9.1"
|
|
90
90
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
91
91
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
92
92
|
static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
|
|
@@ -445,11 +445,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
445
445
|
logger.error("Cannot get capBridge")
|
|
446
446
|
return false
|
|
447
447
|
}
|
|
448
|
-
|
|
448
|
+
|
|
449
449
|
// Use semaphore to wait for WebView load completion
|
|
450
450
|
let loadSemaphore = DispatchSemaphore(value: 0)
|
|
451
451
|
var loadSuccess = false
|
|
452
|
-
|
|
452
|
+
|
|
453
453
|
// Set up navigation delegate to detect when loading completes
|
|
454
454
|
let navigationDelegate = WebViewLoadDelegate { [weak self] success in
|
|
455
455
|
loadSuccess = success
|
|
@@ -459,7 +459,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
459
459
|
}
|
|
460
460
|
loadSemaphore.signal()
|
|
461
461
|
}
|
|
462
|
-
|
|
462
|
+
|
|
463
463
|
if keepUrlPathAfterReload {
|
|
464
464
|
DispatchQueue.main.async {
|
|
465
465
|
guard let url = vc.webView?.url else {
|
|
@@ -488,10 +488,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
488
488
|
vc.webView?.reload()
|
|
489
489
|
}
|
|
490
490
|
}
|
|
491
|
-
|
|
491
|
+
|
|
492
492
|
// Wait for load completion with timeout
|
|
493
493
|
let result = loadSemaphore.wait(timeout: .now() + 10) // 10 second timeout
|
|
494
|
-
|
|
494
|
+
|
|
495
495
|
if result == .timedOut {
|
|
496
496
|
logger.error("Reload timed out after 10 seconds")
|
|
497
497
|
DispatchQueue.main.async {
|
|
@@ -499,7 +499,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
499
499
|
}
|
|
500
500
|
return false
|
|
501
501
|
}
|
|
502
|
-
|
|
502
|
+
|
|
503
503
|
return loadSuccess
|
|
504
504
|
}
|
|
505
505
|
return false
|
|
@@ -1150,6 +1150,8 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1150
1150
|
self.checkAppReady()
|
|
1151
1151
|
}
|
|
1152
1152
|
|
|
1153
|
+
private var periodicUpdateTimer: Timer?
|
|
1154
|
+
|
|
1153
1155
|
@objc func checkForUpdateAfterDelay() {
|
|
1154
1156
|
if periodCheckDelay == 0 || !self._isAutoUpdateEnabled() {
|
|
1155
1157
|
return
|
|
@@ -1158,7 +1160,12 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1158
1160
|
logger.error("Error no url or wrong format")
|
|
1159
1161
|
return
|
|
1160
1162
|
}
|
|
1161
|
-
|
|
1163
|
+
|
|
1164
|
+
// Clean up any existing timer
|
|
1165
|
+
periodicUpdateTimer?.invalidate()
|
|
1166
|
+
|
|
1167
|
+
periodicUpdateTimer = Timer.scheduledTimer(withTimeInterval: TimeInterval(periodCheckDelay), repeats: true) { [weak self] _ in
|
|
1168
|
+
guard let self = self else { return }
|
|
1162
1169
|
DispatchQueue.global(qos: .background).async {
|
|
1163
1170
|
let res = self.implementation.getLatest(url: url, channel: nil)
|
|
1164
1171
|
let current = self.implementation.getCurrentBundle()
|
|
@@ -1169,7 +1176,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1169
1176
|
}
|
|
1170
1177
|
}
|
|
1171
1178
|
}
|
|
1172
|
-
RunLoop.current.add(
|
|
1179
|
+
RunLoop.current.add(periodicUpdateTimer!, forMode: .default)
|
|
1173
1180
|
}
|
|
1174
1181
|
|
|
1175
1182
|
@objc func appMovedToBackground() {
|
|
@@ -42,6 +42,16 @@ import UIKit
|
|
|
42
42
|
public var deviceID = ""
|
|
43
43
|
public var publicKey: String = ""
|
|
44
44
|
|
|
45
|
+
private var userAgent: String {
|
|
46
|
+
return "CapacitorUpdater/\(PLUGIN_VERSION) (\(appId))"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private lazy var alamofireSession: Session = {
|
|
50
|
+
let configuration = URLSessionConfiguration.default
|
|
51
|
+
configuration.httpAdditionalHeaders = ["User-Agent": self.userAgent]
|
|
52
|
+
return Session(configuration: configuration)
|
|
53
|
+
}()
|
|
54
|
+
|
|
45
55
|
public var notifyDownloadRaw: (String, Int, Bool) -> Void = { _, _, _ in }
|
|
46
56
|
public func notifyDownload(id: String, percent: Int, ignoreMultipleOfTen: Bool = false) {
|
|
47
57
|
notifyDownloadRaw(id, percent, ignoreMultipleOfTen)
|
|
@@ -250,7 +260,7 @@ import UIKit
|
|
|
250
260
|
parameters.defaultChannel = channel
|
|
251
261
|
}
|
|
252
262
|
logger.info("Auto-update parameters: \(parameters)")
|
|
253
|
-
let request =
|
|
263
|
+
let request = alamofireSession.request(url, method: .post, parameters: parameters, encoder: JSONParameterEncoder.default, requestModifier: { $0.timeoutInterval = self.timeout })
|
|
254
264
|
|
|
255
265
|
request.validate().responseDecodable(of: AppVersionDec.self) { response in
|
|
256
266
|
switch response.result {
|
|
@@ -376,7 +386,7 @@ import UIKit
|
|
|
376
386
|
dispatchGroup.leave()
|
|
377
387
|
} else {
|
|
378
388
|
// File not in cache, download, decompress, and save to both cache and destination
|
|
379
|
-
|
|
389
|
+
self.alamofireSession.download(downloadUrl).responseData { response in
|
|
380
390
|
defer { dispatchGroup.leave() }
|
|
381
391
|
|
|
382
392
|
switch response.result {
|
|
@@ -601,7 +611,9 @@ import UIKit
|
|
|
601
611
|
mainError = error as NSError?
|
|
602
612
|
}
|
|
603
613
|
}
|
|
604
|
-
let
|
|
614
|
+
let configuration = URLSessionConfiguration.default
|
|
615
|
+
configuration.httpAdditionalHeaders = ["User-Agent": self.userAgent]
|
|
616
|
+
let session = Session(configuration: configuration, eventMonitors: [monitor])
|
|
605
617
|
|
|
606
618
|
let request = session.streamRequest(url, headers: requestHeaders).validate().onHTTPResponse(perform: { response in
|
|
607
619
|
if let contentLength = response.headers.value(for: "Content-Length") {
|
|
@@ -950,7 +962,7 @@ import UIKit
|
|
|
950
962
|
let semaphore: DispatchSemaphore = DispatchSemaphore(value: 0)
|
|
951
963
|
let parameters: InfoObject = self.createInfoObject()
|
|
952
964
|
|
|
953
|
-
let request =
|
|
965
|
+
let request = alamofireSession.request(self.channelUrl, method: .delete, parameters: parameters, encoder: JSONParameterEncoder.default, requestModifier: { $0.timeoutInterval = self.timeout })
|
|
954
966
|
|
|
955
967
|
request.validate().responseDecodable(of: SetChannelDec.self) { response in
|
|
956
968
|
switch response.result {
|
|
@@ -985,7 +997,7 @@ import UIKit
|
|
|
985
997
|
var parameters: InfoObject = self.createInfoObject()
|
|
986
998
|
parameters.channel = channel
|
|
987
999
|
|
|
988
|
-
let request =
|
|
1000
|
+
let request = alamofireSession.request(self.channelUrl, method: .post, parameters: parameters, encoder: JSONParameterEncoder.default, requestModifier: { $0.timeoutInterval = self.timeout })
|
|
989
1001
|
|
|
990
1002
|
request.validate().responseDecodable(of: SetChannelDec.self) { response in
|
|
991
1003
|
switch response.result {
|
|
@@ -1018,7 +1030,7 @@ import UIKit
|
|
|
1018
1030
|
}
|
|
1019
1031
|
let semaphore: DispatchSemaphore = DispatchSemaphore(value: 0)
|
|
1020
1032
|
let parameters: InfoObject = self.createInfoObject()
|
|
1021
|
-
let request =
|
|
1033
|
+
let request = alamofireSession.request(self.channelUrl, method: .put, parameters: parameters, encoder: JSONParameterEncoder.default, requestModifier: { $0.timeoutInterval = self.timeout })
|
|
1022
1034
|
|
|
1023
1035
|
request.validate().responseDecodable(of: GetChannelDec.self) { response in
|
|
1024
1036
|
defer {
|
|
@@ -1084,7 +1096,7 @@ import UIKit
|
|
|
1084
1096
|
return listChannels
|
|
1085
1097
|
}
|
|
1086
1098
|
|
|
1087
|
-
let request =
|
|
1099
|
+
let request = alamofireSession.request(url, method: .get, requestModifier: { $0.timeoutInterval = self.timeout })
|
|
1088
1100
|
|
|
1089
1101
|
request.validate().responseDecodable(of: ListChannelsDec.self) { response in
|
|
1090
1102
|
defer {
|
|
@@ -1137,7 +1149,7 @@ import UIKit
|
|
|
1137
1149
|
|
|
1138
1150
|
let operation = BlockOperation {
|
|
1139
1151
|
let semaphore = DispatchSemaphore(value: 0)
|
|
1140
|
-
|
|
1152
|
+
self.alamofireSession.request(
|
|
1141
1153
|
self.statsUrl,
|
|
1142
1154
|
method: .post,
|
|
1143
1155
|
parameters: parameters,
|