@capgo/capacitor-updater 7.3.3 → 7.5.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/README.md +101 -16
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +140 -6
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +135 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeDetector.java +72 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeMenu.java +169 -0
- package/dist/docs.json +260 -30
- package/dist/esm/definitions.d.ts +78 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +4 -1
- package/dist/esm/web.js +14 -2
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +14 -2
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +14 -2
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +76 -8
- package/ios/Plugin/CapgoUpdater.swift +96 -39
- package/ios/Plugin/InternalUtils.swift +45 -0
- package/ios/Plugin/Logger.swift +1 -1
- package/ios/Plugin/ShakeMenu.swift +112 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -252,6 +252,7 @@ CapacitorUpdater can be configured with these options:
|
|
|
252
252
|
| **`appId`** | <code>string</code> | Configure the app id for the app in the config. | <code>undefined</code> | 6.0.0 |
|
|
253
253
|
| **`keepUrlPathAfterReload`** | <code>boolean</code> | Configure the plugin to keep the URL path after a reload. WARNING: When a reload is triggered, 'window.history' will be cleared. | <code>false</code> | 6.8.0 |
|
|
254
254
|
| **`disableJSLogging`** | <code>boolean</code> | Disable the JavaScript logging of the plugin. if true, the plugin will not log to the JavaScript console. only the native log will be done | <code>false</code> | 7.3.0 |
|
|
255
|
+
| **`shakeMenu`** | <code>boolean</code> | Enable shake gesture to show update menu for debugging/testing purposes | <code>false</code> | 7.5.0 |
|
|
255
256
|
|
|
256
257
|
### Examples
|
|
257
258
|
|
|
@@ -285,7 +286,8 @@ In `capacitor.config.json`:
|
|
|
285
286
|
"defaultChannel": undefined,
|
|
286
287
|
"appId": undefined,
|
|
287
288
|
"keepUrlPathAfterReload": undefined,
|
|
288
|
-
"disableJSLogging": undefined
|
|
289
|
+
"disableJSLogging": undefined,
|
|
290
|
+
"shakeMenu": undefined
|
|
289
291
|
}
|
|
290
292
|
}
|
|
291
293
|
}
|
|
@@ -326,6 +328,7 @@ const config: CapacitorConfig = {
|
|
|
326
328
|
appId: undefined,
|
|
327
329
|
keepUrlPathAfterReload: undefined,
|
|
328
330
|
disableJSLogging: undefined,
|
|
331
|
+
shakeMenu: undefined,
|
|
329
332
|
},
|
|
330
333
|
},
|
|
331
334
|
};
|
|
@@ -357,6 +360,7 @@ export default config;
|
|
|
357
360
|
* [`setChannel(...)`](#setchannel)
|
|
358
361
|
* [`unsetChannel(...)`](#unsetchannel)
|
|
359
362
|
* [`getChannel()`](#getchannel)
|
|
363
|
+
* [`listChannels()`](#listchannels)
|
|
360
364
|
* [`setCustomId(...)`](#setcustomid)
|
|
361
365
|
* [`getBuiltinVersion()`](#getbuiltinversion)
|
|
362
366
|
* [`getDeviceId()`](#getdeviceid)
|
|
@@ -374,6 +378,8 @@ export default config;
|
|
|
374
378
|
* [`addListener('appReady', ...)`](#addlistenerappready-)
|
|
375
379
|
* [`isAutoUpdateAvailable()`](#isautoupdateavailable)
|
|
376
380
|
* [`getNextBundle()`](#getnextbundle)
|
|
381
|
+
* [`setShakeMenu(...)`](#setshakemenu)
|
|
382
|
+
* [`isShakeMenuEnabled()`](#isshakemenuenabled)
|
|
377
383
|
* [Interfaces](#interfaces)
|
|
378
384
|
* [Type Aliases](#type-aliases)
|
|
379
385
|
|
|
@@ -678,6 +684,21 @@ Get the channel for this device
|
|
|
678
684
|
--------------------
|
|
679
685
|
|
|
680
686
|
|
|
687
|
+
### listChannels()
|
|
688
|
+
|
|
689
|
+
```typescript
|
|
690
|
+
listChannels() => Promise<ListChannelsResult>
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
List all channels available for this device that allow self-assignment
|
|
694
|
+
|
|
695
|
+
**Returns:** <code>Promise<<a href="#listchannelsresult">ListChannelsResult</a>></code>
|
|
696
|
+
|
|
697
|
+
**Since:** 7.5.0
|
|
698
|
+
|
|
699
|
+
--------------------
|
|
700
|
+
|
|
701
|
+
|
|
681
702
|
### setCustomId(...)
|
|
682
703
|
|
|
683
704
|
```typescript
|
|
@@ -972,6 +993,38 @@ Returns null if no next bundle is set.
|
|
|
972
993
|
--------------------
|
|
973
994
|
|
|
974
995
|
|
|
996
|
+
### setShakeMenu(...)
|
|
997
|
+
|
|
998
|
+
```typescript
|
|
999
|
+
setShakeMenu(options: SetShakeMenuOptions) => Promise<void>
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
Enable or disable the shake menu for debugging/testing purposes
|
|
1003
|
+
|
|
1004
|
+
| Param | Type | Description |
|
|
1005
|
+
| ------------- | ------------------------------------------------------------------- | -------------------------------------------------------- |
|
|
1006
|
+
| **`options`** | <code><a href="#setshakemenuoptions">SetShakeMenuOptions</a></code> | Contains enabled boolean to enable or disable shake menu |
|
|
1007
|
+
|
|
1008
|
+
**Since:** 7.5.0
|
|
1009
|
+
|
|
1010
|
+
--------------------
|
|
1011
|
+
|
|
1012
|
+
|
|
1013
|
+
### isShakeMenuEnabled()
|
|
1014
|
+
|
|
1015
|
+
```typescript
|
|
1016
|
+
isShakeMenuEnabled() => Promise<ShakeMenuEnabled>
|
|
1017
|
+
```
|
|
1018
|
+
|
|
1019
|
+
Get the current state of the shake menu
|
|
1020
|
+
|
|
1021
|
+
**Returns:** <code>Promise<<a href="#shakemenuenabled">ShakeMenuEnabled</a>></code>
|
|
1022
|
+
|
|
1023
|
+
**Since:** 7.5.0
|
|
1024
|
+
|
|
1025
|
+
--------------------
|
|
1026
|
+
|
|
1027
|
+
|
|
975
1028
|
### Interfaces
|
|
976
1029
|
|
|
977
1030
|
|
|
@@ -1019,12 +1072,22 @@ Returns null if no next bundle is set.
|
|
|
1019
1072
|
This URL and versions are used to download the bundle from the server, If you use backend all information will be gived by the method getLatest.
|
|
1020
1073
|
If you don't use backend, you need to provide the URL and version of the bundle. Checksum and sessionKey are required if you encrypted the bundle with the CLI command encrypt, you should receive them as result of the command.
|
|
1021
1074
|
|
|
1022
|
-
| Prop | Type
|
|
1023
|
-
| ---------------- |
|
|
1024
|
-
| **`url`** | <code>string</code>
|
|
1025
|
-
| **`version`** | <code>string</code>
|
|
1026
|
-
| **`sessionKey`** | <code>string</code>
|
|
1027
|
-
| **`checksum`** | <code>string</code>
|
|
1075
|
+
| Prop | Type | Description | Default | Since |
|
|
1076
|
+
| ---------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ----- |
|
|
1077
|
+
| **`url`** | <code>string</code> | The URL of the bundle zip file (e.g: dist.zip) to be downloaded. (This can be any URL. E.g: Amazon S3, a GitHub tag, any other place you've hosted your bundle.) | | |
|
|
1078
|
+
| **`version`** | <code>string</code> | The version code/name of this bundle/version | | |
|
|
1079
|
+
| **`sessionKey`** | <code>string</code> | The session key for the update, when the bundle is encrypted with a session key | <code>undefined</code> | 4.0.0 |
|
|
1080
|
+
| **`checksum`** | <code>string</code> | The checksum for the update, it should be in sha256 and encrypted with private key if the bundle is encrypted | <code>undefined</code> | 4.0.0 |
|
|
1081
|
+
| **`manifest`** | <code>ManifestEntry[]</code> | The manifest for multi-file downloads | <code>undefined</code> | 6.1.0 |
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
#### ManifestEntry
|
|
1085
|
+
|
|
1086
|
+
| Prop | Type |
|
|
1087
|
+
| ------------------ | --------------------------- |
|
|
1088
|
+
| **`file_name`** | <code>string \| null</code> |
|
|
1089
|
+
| **`file_hash`** | <code>string \| null</code> |
|
|
1090
|
+
| **`download_url`** | <code>string \| null</code> |
|
|
1028
1091
|
|
|
1029
1092
|
|
|
1030
1093
|
#### BundleId
|
|
@@ -1093,15 +1156,6 @@ If you don't use backend, you need to provide the URL and version of the bundle.
|
|
|
1093
1156
|
| **`manifest`** | <code>ManifestEntry[]</code> | | 6.1 |
|
|
1094
1157
|
|
|
1095
1158
|
|
|
1096
|
-
#### ManifestEntry
|
|
1097
|
-
|
|
1098
|
-
| Prop | Type |
|
|
1099
|
-
| ------------------ | --------------------------- |
|
|
1100
|
-
| **`file_name`** | <code>string \| null</code> |
|
|
1101
|
-
| **`file_hash`** | <code>string \| null</code> |
|
|
1102
|
-
| **`download_url`** | <code>string \| null</code> |
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
1159
|
#### GetLatestOptions
|
|
1106
1160
|
|
|
1107
1161
|
| Prop | Type | Description | Default | Since |
|
|
@@ -1144,6 +1198,23 @@ If you don't use backend, you need to provide the URL and version of the bundle.
|
|
|
1144
1198
|
| **`allowSet`** | <code>boolean</code> | | |
|
|
1145
1199
|
|
|
1146
1200
|
|
|
1201
|
+
#### ListChannelsResult
|
|
1202
|
+
|
|
1203
|
+
| Prop | Type | Description | Since |
|
|
1204
|
+
| -------------- | -------------------------- | -------------------------- | ----- |
|
|
1205
|
+
| **`channels`** | <code>ChannelInfo[]</code> | List of available channels | 7.5.0 |
|
|
1206
|
+
|
|
1207
|
+
|
|
1208
|
+
#### ChannelInfo
|
|
1209
|
+
|
|
1210
|
+
| Prop | Type | Description | Since |
|
|
1211
|
+
| -------------------- | -------------------- | ----------------------------------------------- | ----- |
|
|
1212
|
+
| **`id`** | <code>string</code> | The channel ID | 7.5.0 |
|
|
1213
|
+
| **`name`** | <code>string</code> | The channel name | 7.5.0 |
|
|
1214
|
+
| **`public`** | <code>boolean</code> | Whether this is a public channel | 7.5.0 |
|
|
1215
|
+
| **`allow_self_set`** | <code>boolean</code> | Whether devices can self-assign to this channel | 7.5.0 |
|
|
1216
|
+
|
|
1217
|
+
|
|
1147
1218
|
#### SetCustomIdOptions
|
|
1148
1219
|
|
|
1149
1220
|
| Prop | Type |
|
|
@@ -1251,6 +1322,20 @@ If you don't use backend, you need to provide the URL and version of the bundle.
|
|
|
1251
1322
|
| **`available`** | <code>boolean</code> |
|
|
1252
1323
|
|
|
1253
1324
|
|
|
1325
|
+
#### SetShakeMenuOptions
|
|
1326
|
+
|
|
1327
|
+
| Prop | Type |
|
|
1328
|
+
| ------------- | -------------------- |
|
|
1329
|
+
| **`enabled`** | <code>boolean</code> |
|
|
1330
|
+
|
|
1331
|
+
|
|
1332
|
+
#### ShakeMenuEnabled
|
|
1333
|
+
|
|
1334
|
+
| Prop | Type |
|
|
1335
|
+
| ------------- | -------------------- |
|
|
1336
|
+
| **`enabled`** | <code>boolean</code> |
|
|
1337
|
+
|
|
1338
|
+
|
|
1254
1339
|
### Type Aliases
|
|
1255
1340
|
|
|
1256
1341
|
|
|
@@ -60,7 +60,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
60
60
|
private static final String statsUrlDefault = "https://plugin.capgo.app/stats";
|
|
61
61
|
private static final String channelUrlDefault = "https://plugin.capgo.app/channel_self";
|
|
62
62
|
|
|
63
|
-
private final String PLUGIN_VERSION = "7.
|
|
63
|
+
private final String PLUGIN_VERSION = "7.5.1";
|
|
64
64
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
65
65
|
|
|
66
66
|
private SharedPreferences.Editor editor;
|
|
@@ -78,6 +78,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
78
78
|
private Thread backgroundTask;
|
|
79
79
|
private Boolean taskRunning = false;
|
|
80
80
|
private Boolean keepUrlPathAfterReload = false;
|
|
81
|
+
Boolean shakeMenuEnabled = false;
|
|
81
82
|
|
|
82
83
|
private Boolean isPreviousMainActivity = true;
|
|
83
84
|
|
|
@@ -91,6 +92,8 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
91
92
|
|
|
92
93
|
private DelayUpdateUtils delayUpdateUtils;
|
|
93
94
|
|
|
95
|
+
private ShakeMenu shakeMenu;
|
|
96
|
+
|
|
94
97
|
private JSObject mapToJSObject(Map<String, Object> map) {
|
|
95
98
|
JSObject jsObject = new JSObject();
|
|
96
99
|
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
|
@@ -224,6 +227,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
224
227
|
this.appReadyTimeout = this.getConfig().getInt("appReadyTimeout", 10000);
|
|
225
228
|
this.keepUrlPathAfterReload = this.getConfig().getBoolean("keepUrlPathAfterReload", false);
|
|
226
229
|
this.implementation.timeout = this.getConfig().getInt("responseTimeout", 20) * 1000;
|
|
230
|
+
this.shakeMenuEnabled = this.getConfig().getBoolean("shakeMenu", false);
|
|
227
231
|
boolean resetWhenUpdate = this.getConfig().getBoolean("resetWhenUpdate", true);
|
|
228
232
|
|
|
229
233
|
this.implementation.autoReset();
|
|
@@ -438,7 +442,14 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
438
442
|
CapacitorUpdaterPlugin.this.implementation.unsetChannel(res -> {
|
|
439
443
|
JSObject jsRes = mapToJSObject(res);
|
|
440
444
|
if (jsRes.has("error")) {
|
|
441
|
-
|
|
445
|
+
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : jsRes.getString("error");
|
|
446
|
+
String errorCode = jsRes.getString("error");
|
|
447
|
+
|
|
448
|
+
JSObject errorObj = new JSObject();
|
|
449
|
+
errorObj.put("message", errorMessage);
|
|
450
|
+
errorObj.put("error", errorCode);
|
|
451
|
+
|
|
452
|
+
call.reject(errorMessage, "UNSETCHANNEL_FAILED", null, errorObj);
|
|
442
453
|
} else {
|
|
443
454
|
if (CapacitorUpdaterPlugin.this._isAutoUpdateEnabled() && Boolean.TRUE.equals(triggerAutoUpdate)) {
|
|
444
455
|
logger.info("Calling autoupdater after channel change!");
|
|
@@ -461,7 +472,10 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
461
472
|
|
|
462
473
|
if (channel == null) {
|
|
463
474
|
logger.error("setChannel called without channel");
|
|
464
|
-
|
|
475
|
+
JSObject errorObj = new JSObject();
|
|
476
|
+
errorObj.put("message", "setChannel called without channel");
|
|
477
|
+
errorObj.put("error", "missing_parameter");
|
|
478
|
+
call.reject("setChannel called without channel", "SETCHANNEL_INVALID_PARAMS", null, errorObj);
|
|
465
479
|
return;
|
|
466
480
|
}
|
|
467
481
|
try {
|
|
@@ -470,7 +484,14 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
470
484
|
CapacitorUpdaterPlugin.this.implementation.setChannel(channel, res -> {
|
|
471
485
|
JSObject jsRes = mapToJSObject(res);
|
|
472
486
|
if (jsRes.has("error")) {
|
|
473
|
-
|
|
487
|
+
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : jsRes.getString("error");
|
|
488
|
+
String errorCode = jsRes.getString("error");
|
|
489
|
+
|
|
490
|
+
JSObject errorObj = new JSObject();
|
|
491
|
+
errorObj.put("message", errorMessage);
|
|
492
|
+
errorObj.put("error", errorCode);
|
|
493
|
+
|
|
494
|
+
call.reject(errorMessage, "SETCHANNEL_FAILED", null, errorObj);
|
|
474
495
|
} else {
|
|
475
496
|
if (CapacitorUpdaterPlugin.this._isAutoUpdateEnabled() && Boolean.TRUE.equals(triggerAutoUpdate)) {
|
|
476
497
|
logger.info("Calling autoupdater after channel change!");
|
|
@@ -494,7 +515,14 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
494
515
|
CapacitorUpdaterPlugin.this.implementation.getChannel(res -> {
|
|
495
516
|
JSObject jsRes = mapToJSObject(res);
|
|
496
517
|
if (jsRes.has("error")) {
|
|
497
|
-
|
|
518
|
+
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : jsRes.getString("error");
|
|
519
|
+
String errorCode = jsRes.getString("error");
|
|
520
|
+
|
|
521
|
+
JSObject errorObj = new JSObject();
|
|
522
|
+
errorObj.put("message", errorMessage);
|
|
523
|
+
errorObj.put("error", errorCode);
|
|
524
|
+
|
|
525
|
+
call.reject(errorMessage, "GETCHANNEL_FAILED", null, errorObj);
|
|
498
526
|
} else {
|
|
499
527
|
call.resolve(jsRes);
|
|
500
528
|
}
|
|
@@ -506,12 +534,40 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
506
534
|
}
|
|
507
535
|
}
|
|
508
536
|
|
|
537
|
+
@PluginMethod
|
|
538
|
+
public void listChannels(final PluginCall call) {
|
|
539
|
+
try {
|
|
540
|
+
logger.info("listChannels");
|
|
541
|
+
startNewThread(() ->
|
|
542
|
+
CapacitorUpdaterPlugin.this.implementation.listChannels(res -> {
|
|
543
|
+
JSObject jsRes = mapToJSObject(res);
|
|
544
|
+
if (jsRes.has("error")) {
|
|
545
|
+
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : jsRes.getString("error");
|
|
546
|
+
String errorCode = jsRes.getString("error");
|
|
547
|
+
|
|
548
|
+
JSObject errorObj = new JSObject();
|
|
549
|
+
errorObj.put("message", errorMessage);
|
|
550
|
+
errorObj.put("error", errorCode);
|
|
551
|
+
|
|
552
|
+
call.reject(errorMessage, "LISTCHANNELS_FAILED", null, errorObj);
|
|
553
|
+
} else {
|
|
554
|
+
call.resolve(jsRes);
|
|
555
|
+
}
|
|
556
|
+
})
|
|
557
|
+
);
|
|
558
|
+
} catch (final Exception e) {
|
|
559
|
+
logger.error("Failed to listChannels: " + e.getMessage());
|
|
560
|
+
call.reject("Failed to listChannels", e);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
509
564
|
@PluginMethod
|
|
510
565
|
public void download(final PluginCall call) {
|
|
511
566
|
final String url = call.getString("url");
|
|
512
567
|
final String version = call.getString("version");
|
|
513
568
|
final String sessionKey = call.getString("sessionKey", "");
|
|
514
569
|
final String checksum = call.getString("checksum", "");
|
|
570
|
+
final JSONArray manifest = call.getData().optJSONArray("manifest");
|
|
515
571
|
if (url == null) {
|
|
516
572
|
logger.error("Download called without url");
|
|
517
573
|
call.reject("Download called without url");
|
|
@@ -526,7 +582,19 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
526
582
|
logger.info("Downloading " + url);
|
|
527
583
|
startNewThread(() -> {
|
|
528
584
|
try {
|
|
529
|
-
final BundleInfo downloaded
|
|
585
|
+
final BundleInfo downloaded;
|
|
586
|
+
if (manifest != null) {
|
|
587
|
+
// For manifest downloads, we need to handle this asynchronously
|
|
588
|
+
// since there's no synchronous downloadManifest method in Java
|
|
589
|
+
CapacitorUpdaterPlugin.this.implementation.downloadBackground(url, version, sessionKey, checksum, manifest);
|
|
590
|
+
// Return immediately with a pending status - the actual result will come via listeners
|
|
591
|
+
final String id = CapacitorUpdaterPlugin.this.implementation.randomString();
|
|
592
|
+
downloaded = new BundleInfo(id, version, BundleStatus.DOWNLOADING, new Date(System.currentTimeMillis()), "");
|
|
593
|
+
call.resolve(mapToJSObject(downloaded.toJSONMap()));
|
|
594
|
+
return;
|
|
595
|
+
} else {
|
|
596
|
+
downloaded = CapacitorUpdaterPlugin.this.implementation.download(url, version, sessionKey, checksum);
|
|
597
|
+
}
|
|
530
598
|
if (downloaded.isErrorStatus()) {
|
|
531
599
|
throw new RuntimeException("Download failed: " + downloaded.getStatus());
|
|
532
600
|
} else {
|
|
@@ -1315,6 +1383,16 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1315
1383
|
}
|
|
1316
1384
|
logger.info("onActivityStarted " + getActivity().getClass().getName());
|
|
1317
1385
|
isPreviousMainActivity = true;
|
|
1386
|
+
|
|
1387
|
+
// Initialize shake menu if enabled and activity is BridgeActivity
|
|
1388
|
+
if (shakeMenuEnabled && getActivity() instanceof com.getcapacitor.BridgeActivity && shakeMenu == null) {
|
|
1389
|
+
try {
|
|
1390
|
+
shakeMenu = new ShakeMenu(this, (com.getcapacitor.BridgeActivity) getActivity(), logger);
|
|
1391
|
+
logger.info("Shake menu initialized");
|
|
1392
|
+
} catch (Exception e) {
|
|
1393
|
+
logger.error("Failed to initialize shake menu: " + e.getMessage());
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1318
1396
|
}
|
|
1319
1397
|
|
|
1320
1398
|
@Override
|
|
@@ -1346,5 +1424,61 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1346
1424
|
if (counterActivityCreate == 0) {
|
|
1347
1425
|
this.appKilled();
|
|
1348
1426
|
}
|
|
1427
|
+
|
|
1428
|
+
// Clean up shake menu
|
|
1429
|
+
if (shakeMenu != null) {
|
|
1430
|
+
try {
|
|
1431
|
+
shakeMenu.stop();
|
|
1432
|
+
shakeMenu = null;
|
|
1433
|
+
logger.info("Shake menu cleaned up");
|
|
1434
|
+
} catch (Exception e) {
|
|
1435
|
+
logger.error("Failed to clean up shake menu: " + e.getMessage());
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
@PluginMethod
|
|
1441
|
+
public void setShakeMenu(final PluginCall call) {
|
|
1442
|
+
final Boolean enabled = call.getBoolean("enabled");
|
|
1443
|
+
if (enabled == null) {
|
|
1444
|
+
logger.error("setShakeMenu called without enabled parameter");
|
|
1445
|
+
call.reject("setShakeMenu called without enabled parameter");
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
this.shakeMenuEnabled = enabled;
|
|
1450
|
+
logger.info("Shake menu " + (enabled ? "enabled" : "disabled"));
|
|
1451
|
+
|
|
1452
|
+
// Manage shake menu instance based on enabled state
|
|
1453
|
+
if (enabled && getActivity() instanceof com.getcapacitor.BridgeActivity && shakeMenu == null) {
|
|
1454
|
+
try {
|
|
1455
|
+
shakeMenu = new ShakeMenu(this, (com.getcapacitor.BridgeActivity) getActivity(), logger);
|
|
1456
|
+
logger.info("Shake menu initialized");
|
|
1457
|
+
} catch (Exception e) {
|
|
1458
|
+
logger.error("Failed to initialize shake menu: " + e.getMessage());
|
|
1459
|
+
}
|
|
1460
|
+
} else if (!enabled && shakeMenu != null) {
|
|
1461
|
+
try {
|
|
1462
|
+
shakeMenu.stop();
|
|
1463
|
+
shakeMenu = null;
|
|
1464
|
+
logger.info("Shake menu stopped");
|
|
1465
|
+
} catch (Exception e) {
|
|
1466
|
+
logger.error("Failed to stop shake menu: " + e.getMessage());
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
call.resolve();
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
@PluginMethod
|
|
1474
|
+
public void isShakeMenuEnabled(final PluginCall call) {
|
|
1475
|
+
try {
|
|
1476
|
+
final JSObject ret = new JSObject();
|
|
1477
|
+
ret.put("enabled", this.shakeMenuEnabled);
|
|
1478
|
+
call.resolve(ret);
|
|
1479
|
+
} catch (final Exception e) {
|
|
1480
|
+
logger.error("Could not get shake menu status " + e.getMessage());
|
|
1481
|
+
call.reject("Could not get shake menu status", e);
|
|
1482
|
+
}
|
|
1349
1483
|
}
|
|
1350
1484
|
}
|
|
@@ -35,6 +35,7 @@ import java.util.Objects;
|
|
|
35
35
|
import java.util.zip.ZipEntry;
|
|
36
36
|
import java.util.zip.ZipInputStream;
|
|
37
37
|
import okhttp3.*;
|
|
38
|
+
import okhttp3.HttpUrl;
|
|
38
39
|
import org.json.JSONArray;
|
|
39
40
|
import org.json.JSONException;
|
|
40
41
|
import org.json.JSONObject;
|
|
@@ -125,7 +126,7 @@ public class CapgoUpdater {
|
|
|
125
126
|
|
|
126
127
|
void notifyListeners(final String id, final Map<String, Object> res) {}
|
|
127
128
|
|
|
128
|
-
|
|
129
|
+
public String randomString() {
|
|
129
130
|
final StringBuilder sb = new StringBuilder(10);
|
|
130
131
|
for (int i = 0; i < 10; i++) sb.append(AB.charAt(rnd.nextInt(AB.length())));
|
|
131
132
|
return sb.toString();
|
|
@@ -699,6 +700,16 @@ public class CapgoUpdater {
|
|
|
699
700
|
assert responseBody != null;
|
|
700
701
|
String responseData = responseBody.string();
|
|
701
702
|
JSONObject jsonResponse = new JSONObject(responseData);
|
|
703
|
+
|
|
704
|
+
// Check for server-side errors first
|
|
705
|
+
if (jsonResponse.has("error")) {
|
|
706
|
+
Map<String, Object> retError = new HashMap<>();
|
|
707
|
+
retError.put("message", jsonResponse.getString("error"));
|
|
708
|
+
retError.put("error", "server_error");
|
|
709
|
+
callback.callback(retError);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
|
|
702
713
|
Map<String, Object> ret = new HashMap<>();
|
|
703
714
|
|
|
704
715
|
Iterator<String> keys = jsonResponse.keys();
|
|
@@ -798,6 +809,16 @@ public class CapgoUpdater {
|
|
|
798
809
|
assert responseBody != null;
|
|
799
810
|
String responseData = responseBody.string();
|
|
800
811
|
JSONObject jsonResponse = new JSONObject(responseData);
|
|
812
|
+
|
|
813
|
+
// Check for server-side errors first
|
|
814
|
+
if (jsonResponse.has("error")) {
|
|
815
|
+
Map<String, Object> retError = new HashMap<>();
|
|
816
|
+
retError.put("message", jsonResponse.getString("error"));
|
|
817
|
+
retError.put("error", "server_error");
|
|
818
|
+
callback.callback(retError);
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
|
|
801
822
|
Map<String, Object> ret = new HashMap<>();
|
|
802
823
|
|
|
803
824
|
Iterator<String> keys = jsonResponse.keys();
|
|
@@ -912,6 +933,16 @@ public class CapgoUpdater {
|
|
|
912
933
|
assert responseBody != null;
|
|
913
934
|
String responseData = responseBody.string();
|
|
914
935
|
JSONObject jsonResponse = new JSONObject(responseData);
|
|
936
|
+
|
|
937
|
+
// Check for server-side errors first
|
|
938
|
+
if (jsonResponse.has("error")) {
|
|
939
|
+
Map<String, Object> retError = new HashMap<>();
|
|
940
|
+
retError.put("message", jsonResponse.getString("error"));
|
|
941
|
+
retError.put("error", "server_error");
|
|
942
|
+
callback.callback(retError);
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
|
|
915
946
|
Map<String, Object> ret = new HashMap<>();
|
|
916
947
|
|
|
917
948
|
Iterator<String> keys = jsonResponse.keys();
|
|
@@ -934,6 +965,109 @@ public class CapgoUpdater {
|
|
|
934
965
|
);
|
|
935
966
|
}
|
|
936
967
|
|
|
968
|
+
public void listChannels(final Callback callback) {
|
|
969
|
+
String channelUrl = this.channelUrl;
|
|
970
|
+
if (channelUrl == null || channelUrl.isEmpty()) {
|
|
971
|
+
logger.error("Channel URL is not set");
|
|
972
|
+
final Map<String, Object> retError = new HashMap<>();
|
|
973
|
+
retError.put("message", "Channel URL is not set");
|
|
974
|
+
retError.put("error", "missing_config");
|
|
975
|
+
callback.callback(retError);
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// Auto-detect values
|
|
980
|
+
String appId = this.appId;
|
|
981
|
+
String platform = "android";
|
|
982
|
+
boolean isEmulator = this.isEmulator();
|
|
983
|
+
boolean isProd = this.isProd();
|
|
984
|
+
|
|
985
|
+
// Build URL with query parameters
|
|
986
|
+
HttpUrl.Builder urlBuilder = HttpUrl.parse(channelUrl).newBuilder();
|
|
987
|
+
urlBuilder.addQueryParameter("app_id", appId);
|
|
988
|
+
urlBuilder.addQueryParameter("platform", platform);
|
|
989
|
+
urlBuilder.addQueryParameter("is_emulator", String.valueOf(isEmulator));
|
|
990
|
+
urlBuilder.addQueryParameter("is_prod", String.valueOf(isProd));
|
|
991
|
+
|
|
992
|
+
Request request = new Request.Builder().url(urlBuilder.build()).get().build();
|
|
993
|
+
|
|
994
|
+
client
|
|
995
|
+
.newCall(request)
|
|
996
|
+
.enqueue(
|
|
997
|
+
new okhttp3.Callback() {
|
|
998
|
+
@Override
|
|
999
|
+
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
|
1000
|
+
Map<String, Object> retError = new HashMap<>();
|
|
1001
|
+
retError.put("message", "Request failed: " + e.getMessage());
|
|
1002
|
+
retError.put("error", "network_error");
|
|
1003
|
+
callback.callback(retError);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
@Override
|
|
1007
|
+
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
|
|
1008
|
+
try (ResponseBody responseBody = response.body()) {
|
|
1009
|
+
if (!response.isSuccessful()) {
|
|
1010
|
+
Map<String, Object> retError = new HashMap<>();
|
|
1011
|
+
retError.put("message", "Server error: " + response.code());
|
|
1012
|
+
retError.put("error", "response_error");
|
|
1013
|
+
callback.callback(retError);
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
assert responseBody != null;
|
|
1018
|
+
String data = responseBody.string();
|
|
1019
|
+
|
|
1020
|
+
try {
|
|
1021
|
+
Map<String, Object> ret = new HashMap<>();
|
|
1022
|
+
|
|
1023
|
+
try {
|
|
1024
|
+
// Try to parse as direct array first
|
|
1025
|
+
JSONArray channelsJson = new JSONArray(data);
|
|
1026
|
+
List<Map<String, Object>> channelsList = new ArrayList<>();
|
|
1027
|
+
|
|
1028
|
+
for (int i = 0; i < channelsJson.length(); i++) {
|
|
1029
|
+
JSONObject channelJson = channelsJson.getJSONObject(i);
|
|
1030
|
+
Map<String, Object> channel = new HashMap<>();
|
|
1031
|
+
channel.put("id", channelJson.optString("id", ""));
|
|
1032
|
+
channel.put("name", channelJson.optString("name", ""));
|
|
1033
|
+
channel.put("public", channelJson.optBoolean("public", false));
|
|
1034
|
+
channel.put("allow_self_set", channelJson.optBoolean("allow_self_set", false));
|
|
1035
|
+
channelsList.add(channel);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// Wrap in channels object for JS API
|
|
1039
|
+
ret.put("channels", channelsList);
|
|
1040
|
+
|
|
1041
|
+
logger.info("Channels listed successfully");
|
|
1042
|
+
callback.callback(ret);
|
|
1043
|
+
} catch (JSONException arrayException) {
|
|
1044
|
+
// If not an array, try to parse as error object
|
|
1045
|
+
try {
|
|
1046
|
+
JSONObject json = new JSONObject(data);
|
|
1047
|
+
if (json.has("error")) {
|
|
1048
|
+
Map<String, Object> retError = new HashMap<>();
|
|
1049
|
+
retError.put("message", json.getString("error"));
|
|
1050
|
+
retError.put("error", "server_error");
|
|
1051
|
+
callback.callback(retError);
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
} catch (JSONException objException) {
|
|
1055
|
+
// If neither array nor object, throw parse error
|
|
1056
|
+
throw arrayException;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
} catch (JSONException e) {
|
|
1060
|
+
Map<String, Object> retError = new HashMap<>();
|
|
1061
|
+
retError.put("message", "JSON parse error: " + e.getMessage());
|
|
1062
|
+
retError.put("error", "parse_error");
|
|
1063
|
+
callback.callback(retError);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
937
1071
|
public void sendStats(final String action) {
|
|
938
1072
|
this.sendStats(action, this.getCurrentBundle().getVersionName());
|
|
939
1073
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
package ee.forgr.capacitor_updater;
|
|
8
|
+
|
|
9
|
+
import android.hardware.Sensor;
|
|
10
|
+
import android.hardware.SensorEvent;
|
|
11
|
+
import android.hardware.SensorEventListener;
|
|
12
|
+
import android.hardware.SensorManager;
|
|
13
|
+
|
|
14
|
+
public class ShakeDetector implements SensorEventListener {
|
|
15
|
+
|
|
16
|
+
public interface Listener {
|
|
17
|
+
void onShakeDetected();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private static final float SHAKE_THRESHOLD = 12.0f; // Acceleration threshold for shake detection
|
|
21
|
+
private static final int SHAKE_TIMEOUT = 500; // Minimum time between shake events (ms)
|
|
22
|
+
|
|
23
|
+
private Listener listener;
|
|
24
|
+
private SensorManager sensorManager;
|
|
25
|
+
private Sensor accelerometer;
|
|
26
|
+
private long lastShakeTime = 0;
|
|
27
|
+
|
|
28
|
+
public ShakeDetector(Listener listener) {
|
|
29
|
+
this.listener = listener;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public void start(SensorManager sensorManager) {
|
|
33
|
+
this.sensorManager = sensorManager;
|
|
34
|
+
this.accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
|
35
|
+
|
|
36
|
+
if (accelerometer != null) {
|
|
37
|
+
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public void stop() {
|
|
42
|
+
if (sensorManager != null) {
|
|
43
|
+
sensorManager.unregisterListener(this);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@Override
|
|
48
|
+
public void onSensorChanged(SensorEvent event) {
|
|
49
|
+
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
|
50
|
+
float x = event.values[0];
|
|
51
|
+
float y = event.values[1];
|
|
52
|
+
float z = event.values[2];
|
|
53
|
+
|
|
54
|
+
// Calculate the acceleration magnitude (excluding gravity)
|
|
55
|
+
float acceleration = (float) Math.sqrt(x * x + y * y + z * z) - SensorManager.GRAVITY_EARTH;
|
|
56
|
+
|
|
57
|
+
// Check if acceleration exceeds threshold and enough time has passed
|
|
58
|
+
long currentTime = System.currentTimeMillis();
|
|
59
|
+
if (Math.abs(acceleration) > SHAKE_THRESHOLD && currentTime - lastShakeTime > SHAKE_TIMEOUT) {
|
|
60
|
+
lastShakeTime = currentTime;
|
|
61
|
+
if (listener != null) {
|
|
62
|
+
listener.onShakeDetected();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@Override
|
|
69
|
+
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
|
70
|
+
// Not needed for shake detection
|
|
71
|
+
}
|
|
72
|
+
}
|