@capgo/capacitor-updater 8.51.23 → 8.51.25
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 +2 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +10 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/Logger.java +63 -4
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +24 -8
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +8 -1
- package/ios/Sources/CapacitorUpdaterPlugin/Logger.swift +75 -5
- package/package.json +1 -1
|
@@ -146,7 +146,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
146
146
|
static final int APPLICATION_EXIT_REASON_USER_REQUESTED = 10;
|
|
147
147
|
static final int APPLICATION_EXIT_REASON_DEPENDENCY_DIED = 12;
|
|
148
148
|
|
|
149
|
-
private final String pluginVersion = "8.51.
|
|
149
|
+
private final String pluginVersion = "8.51.25";
|
|
150
150
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
151
151
|
|
|
152
152
|
private SharedPreferences.Editor editor;
|
|
@@ -2663,6 +2663,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2663
2663
|
CapacitorUpdaterPlugin.this.editor,
|
|
2664
2664
|
DEFAULT_CHANNEL_PREF_KEY,
|
|
2665
2665
|
configDefaultChannel,
|
|
2666
|
+
CapacitorUpdaterPlugin.this.allowSetDefaultChannel,
|
|
2666
2667
|
(res) -> {
|
|
2667
2668
|
JSObject jsRes = InternalUtils.mapToJSObject(res);
|
|
2668
2669
|
if (jsRes.has("error")) {
|
|
@@ -2507,8 +2507,18 @@ public class CapgoUpdater {
|
|
|
2507
2507
|
final SharedPreferences.Editor editor,
|
|
2508
2508
|
final String defaultChannelKey,
|
|
2509
2509
|
final String configDefaultChannel,
|
|
2510
|
+
final boolean allowSetDefaultChannel,
|
|
2510
2511
|
final Callback callback
|
|
2511
2512
|
) {
|
|
2513
|
+
if (!allowSetDefaultChannel) {
|
|
2514
|
+
logger.error("unsetChannel is disabled by allowSetDefaultChannel config");
|
|
2515
|
+
final Map<String, Object> retError = new HashMap<>();
|
|
2516
|
+
retError.put("message", "unsetChannel is disabled by configuration");
|
|
2517
|
+
retError.put("error", "disabled_by_config");
|
|
2518
|
+
callback.callback(retError);
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2512
2522
|
// Clear persisted defaultChannel and revert to config value
|
|
2513
2523
|
editor.remove(defaultChannelKey);
|
|
2514
2524
|
editor.apply();
|
|
@@ -5,15 +5,24 @@ import android.util.Log;
|
|
|
5
5
|
import androidx.annotation.NonNull;
|
|
6
6
|
import androidx.annotation.Nullable;
|
|
7
7
|
import com.getcapacitor.*;
|
|
8
|
+
import java.nio.ByteBuffer;
|
|
9
|
+
import java.nio.CharBuffer;
|
|
10
|
+
import java.nio.charset.CharsetEncoder;
|
|
11
|
+
import java.nio.charset.CoderResult;
|
|
12
|
+
import java.nio.charset.CodingErrorAction;
|
|
13
|
+
import java.nio.charset.StandardCharsets;
|
|
8
14
|
import java.util.ArrayList;
|
|
9
15
|
import java.util.Arrays;
|
|
10
16
|
import java.util.Date;
|
|
11
17
|
import java.util.Locale;
|
|
12
18
|
import java.util.Map;
|
|
13
19
|
import org.jetbrains.annotations.Contract;
|
|
20
|
+
import org.json.JSONObject;
|
|
14
21
|
|
|
15
22
|
public class Logger {
|
|
16
23
|
|
|
24
|
+
static final int MAX_WEBVIEW_LOG_PAYLOAD_CHARS = 4096;
|
|
25
|
+
|
|
17
26
|
private Bridge bridge;
|
|
18
27
|
|
|
19
28
|
public enum LogLevel {
|
|
@@ -250,13 +259,63 @@ public class Logger {
|
|
|
250
259
|
|
|
251
260
|
// Send to JavaScript if webView is available
|
|
252
261
|
if (bridge != null && bridge.getWebView() != null) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
null
|
|
256
|
-
|
|
262
|
+
String script = buildWebViewConsoleScript(level, tag, formattedMessage);
|
|
263
|
+
if (script != null) {
|
|
264
|
+
bridge.eval(script, null);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
@Nullable
|
|
270
|
+
static String consoleMethodForLevel(@NonNull LogLevel level) {
|
|
271
|
+
switch (level) {
|
|
272
|
+
case error:
|
|
273
|
+
return "error";
|
|
274
|
+
case warn:
|
|
275
|
+
return "warn";
|
|
276
|
+
case info:
|
|
277
|
+
return "info";
|
|
278
|
+
case debug:
|
|
279
|
+
return "debug";
|
|
280
|
+
default:
|
|
281
|
+
return null;
|
|
257
282
|
}
|
|
258
283
|
}
|
|
259
284
|
|
|
285
|
+
@NonNull
|
|
286
|
+
static String toJsStringLiteral(@NonNull String value) {
|
|
287
|
+
return JSONObject.quote(value);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@NonNull
|
|
291
|
+
static String capWebViewLogPayload(@NonNull String payload) {
|
|
292
|
+
CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder()
|
|
293
|
+
.onMalformedInput(CodingErrorAction.REPLACE)
|
|
294
|
+
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
|
295
|
+
ByteBuffer bytes = ByteBuffer.allocate(MAX_WEBVIEW_LOG_PAYLOAD_CHARS);
|
|
296
|
+
CharBuffer chars = CharBuffer.wrap(payload);
|
|
297
|
+
CoderResult result = encoder.encode(chars, bytes, true);
|
|
298
|
+
if (result.isUnderflow()) {
|
|
299
|
+
result = encoder.flush(bytes);
|
|
300
|
+
}
|
|
301
|
+
if (result.isUnderflow()) {
|
|
302
|
+
return payload;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return new String(bytes.array(), 0, bytes.position(), StandardCharsets.UTF_8) + "...";
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
@Nullable
|
|
309
|
+
static String buildWebViewConsoleScript(@NonNull LogLevel level, @NonNull String tag, @NonNull String formattedMessage) {
|
|
310
|
+
String consoleMethod = consoleMethodForLevel(level);
|
|
311
|
+
if (consoleMethod == null) {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
String payload = capWebViewLogPayload("[" + tag + "] " + formattedMessage);
|
|
316
|
+
return "console." + consoleMethod + "(" + toJsStringLiteral(payload) + ")";
|
|
317
|
+
}
|
|
318
|
+
|
|
260
319
|
public void dir(Object value) {
|
|
261
320
|
String message;
|
|
262
321
|
|
|
@@ -96,7 +96,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
96
96
|
deinit {
|
|
97
97
|
implementation.shutdown()
|
|
98
98
|
}
|
|
99
|
-
private let pluginVersion: String = "8.51.
|
|
99
|
+
private let pluginVersion: String = "8.51.25"
|
|
100
100
|
private let launchStartedAtMs = Int64(Date().timeIntervalSince1970 * 1000)
|
|
101
101
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
102
102
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
@@ -3025,11 +3025,16 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
3025
3025
|
self.saveCallForAsyncHandling(call)
|
|
3026
3026
|
DispatchQueue.global(qos: .utility).async {
|
|
3027
3027
|
let configDefaultChannel = self.getConfig().getString("defaultChannel", "")!
|
|
3028
|
-
let res = self.implementation.unsetChannel(
|
|
3028
|
+
let res = self.implementation.unsetChannel(
|
|
3029
|
+
defaultChannelKey: self.defaultChannelDefaultsKey,
|
|
3030
|
+
configDefaultChannel: configDefaultChannel,
|
|
3031
|
+
allowSetDefaultChannel: self.allowSetDefaultChannel
|
|
3032
|
+
)
|
|
3029
3033
|
if res.error != "" {
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
"
|
|
3034
|
+
let message = res.message.isEmpty ? res.error : res.message
|
|
3035
|
+
self.rejectCall(call, message: message, code: "UNSETCHANNEL_FAILED", data: [
|
|
3036
|
+
"message": message,
|
|
3037
|
+
"error": res.error
|
|
3033
3038
|
])
|
|
3034
3039
|
} else {
|
|
3035
3040
|
guard self.persistDefaultChannelStateFromDefaults() else {
|
|
@@ -3077,9 +3082,20 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
3077
3082
|
"message": res.error
|
|
3078
3083
|
])
|
|
3079
3084
|
}
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3085
|
+
let message = res.message.isEmpty ? res.error : res.message
|
|
3086
|
+
let errorCode: String
|
|
3087
|
+
if res.error == "disabled_by_config" {
|
|
3088
|
+
errorCode = res.error
|
|
3089
|
+
} else if res.error.contains("cannot_update_via_private_channel") || res.error.contains("channel_self_set_not_allowed") {
|
|
3090
|
+
errorCode = "channel_private"
|
|
3091
|
+
} else if res.error.contains("Channel URL") {
|
|
3092
|
+
errorCode = "missing_config"
|
|
3093
|
+
} else {
|
|
3094
|
+
errorCode = "request_failed"
|
|
3095
|
+
}
|
|
3096
|
+
self.rejectCall(call, message: message, code: "SETCHANNEL_FAILED", data: [
|
|
3097
|
+
"message": message,
|
|
3098
|
+
"error": errorCode
|
|
3083
3099
|
])
|
|
3084
3100
|
} else {
|
|
3085
3101
|
guard self.persistDefaultChannelStateFromDefaults() else {
|
|
@@ -2937,9 +2937,16 @@ import UIKit
|
|
|
2937
2937
|
self.setBundleStatus(id: bundle.getId(), status: BundleStatus.ERROR)
|
|
2938
2938
|
}
|
|
2939
2939
|
|
|
2940
|
-
func unsetChannel(defaultChannelKey: String, configDefaultChannel: String) -> SetChannel {
|
|
2940
|
+
func unsetChannel(defaultChannelKey: String, configDefaultChannel: String, allowSetDefaultChannel: Bool) -> SetChannel {
|
|
2941
2941
|
let setChannel: SetChannel = SetChannel()
|
|
2942
2942
|
|
|
2943
|
+
if !allowSetDefaultChannel {
|
|
2944
|
+
logger.error("unsetChannel is disabled by allowSetDefaultChannel config")
|
|
2945
|
+
setChannel.message = "unsetChannel is disabled by configuration"
|
|
2946
|
+
setChannel.error = "disabled_by_config"
|
|
2947
|
+
return setChannel
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2943
2950
|
// Clear persisted defaultChannel and revert to config value
|
|
2944
2951
|
UserDefaults.standard.removeObject(forKey: defaultChannelKey)
|
|
2945
2952
|
UserDefaults.standard.synchronize()
|
|
@@ -4,6 +4,8 @@ import os.log
|
|
|
4
4
|
import WebKit
|
|
5
5
|
|
|
6
6
|
public class Logger {
|
|
7
|
+
static let maxWebViewLogPayloadChars = 4096
|
|
8
|
+
|
|
7
9
|
public enum LogLevel: Int {
|
|
8
10
|
case silent = 0
|
|
9
11
|
case error
|
|
@@ -199,17 +201,85 @@ public class Logger {
|
|
|
199
201
|
// This will never fail, but we have to keep swift happy
|
|
200
202
|
if let label = _labels[level] {
|
|
201
203
|
log(atLevel: level, label: label, tag: tag, message: message)
|
|
202
|
-
if let webView = self.webView {
|
|
204
|
+
if let webView = self.webView, let script = buildWebViewConsoleScript(level: level, label: label, tag: tag, message: message) {
|
|
203
205
|
DispatchQueue.main.async {
|
|
204
|
-
|
|
205
|
-
let jsArg = self.toJSStringLiteral(combined)
|
|
206
|
-
webView.evaluateJavaScript("console.\(level.asString())(\(jsArg))", completionHandler: nil)
|
|
206
|
+
webView.evaluateJavaScript(script, completionHandler: nil)
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
func consoleMethodName(for level: LogLevel) -> String? {
|
|
213
|
+
switch level {
|
|
214
|
+
case .error:
|
|
215
|
+
return "error"
|
|
216
|
+
case .warn:
|
|
217
|
+
return "warn"
|
|
218
|
+
case .info:
|
|
219
|
+
return "info"
|
|
220
|
+
case .debug:
|
|
221
|
+
return "debug"
|
|
222
|
+
case .silent:
|
|
223
|
+
return nil
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private func utf8SequenceLength(_ lead: UInt8) -> Int {
|
|
228
|
+
if (lead & 0x80) == 0 {
|
|
229
|
+
return 1
|
|
230
|
+
}
|
|
231
|
+
if (lead & 0xE0) == 0xC0 {
|
|
232
|
+
return 2
|
|
233
|
+
}
|
|
234
|
+
if (lead & 0xF0) == 0xE0 {
|
|
235
|
+
return 3
|
|
236
|
+
}
|
|
237
|
+
if (lead & 0xF8) == 0xF0 {
|
|
238
|
+
return 4
|
|
239
|
+
}
|
|
240
|
+
return 0
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
func capWebViewLogPayload(_ payload: String) -> String {
|
|
244
|
+
let suffix = "..."
|
|
245
|
+
let maxPayloadBytes = Logger.maxWebViewLogPayloadChars
|
|
246
|
+
let payloadBytes = Array(payload.utf8.prefix(maxPayloadBytes + 1))
|
|
247
|
+
if payloadBytes.count <= maxPayloadBytes {
|
|
248
|
+
return payload
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
var end = maxPayloadBytes
|
|
252
|
+
while end > 0 && (payloadBytes[end - 1] & 0xC0) == 0x80 {
|
|
253
|
+
end -= 1
|
|
254
|
+
}
|
|
255
|
+
while end > 0 {
|
|
256
|
+
let leadIndex = end - 1
|
|
257
|
+
let lead = payloadBytes[leadIndex]
|
|
258
|
+
let seqLength = utf8SequenceLength(lead)
|
|
259
|
+
if seqLength == 0 {
|
|
260
|
+
end -= 1
|
|
261
|
+
continue
|
|
262
|
+
}
|
|
263
|
+
if leadIndex + seqLength <= maxPayloadBytes {
|
|
264
|
+
end = leadIndex + seqLength
|
|
265
|
+
break
|
|
266
|
+
}
|
|
267
|
+
end -= 1
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return String(decoding: payloadBytes[0..<end], as: UTF8.self) + suffix
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
func buildWebViewConsoleScript(level: LogLevel, label: String, tag: String, message: String) -> String? {
|
|
274
|
+
guard let consoleMethod = consoleMethodName(for: level) else {
|
|
275
|
+
return nil
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
let payload = capWebViewLogPayload("\(label) \(tag) : \(message)")
|
|
279
|
+
return "console.\(consoleMethod)(\(toJSStringLiteral(payload)))"
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
func toJSStringLiteral(_ value: String) -> String {
|
|
213
283
|
// Prefer JSON encoding to produce a valid JS string literal
|
|
214
284
|
if let data = try? JSONEncoder().encode(value),
|
|
215
285
|
let encoded = String(data: data, encoding: .utf8) {
|