@capgo/capacitor-updater 7.20.0 → 7.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -14
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +177 -31
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +61 -47
- package/dist/docs.json +146 -2
- package/dist/esm/definitions.d.ts +50 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/history.d.ts +1 -0
- package/dist/esm/history.js +283 -0
- package/dist/esm/history.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +3 -1
- package/dist/esm/web.js +8 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +290 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +290 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +143 -15
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +3 -0
- package/ios/Sources/CapacitorUpdaterPlugin/InternalUtils.swift +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -255,6 +255,7 @@ CapacitorUpdater can be configured with these options:
|
|
|
255
255
|
| **`localApiFiles`** | <code>string</code> | Configure the CLI to use a local file api for testing. | <code>undefined</code> | 6.3.3 |
|
|
256
256
|
| **`allowModifyUrl`** | <code>boolean</code> | Allow the plugin to modify the updateUrl, statsUrl and channelUrl dynamically from the JavaScript side. | <code>false</code> | 5.4.0 |
|
|
257
257
|
| **`allowModifyAppId`** | <code>boolean</code> | Allow the plugin to modify the appId dynamically from the JavaScript side. | <code>false</code> | 7.14.0 |
|
|
258
|
+
| **`allowManualBundleError`** | <code>boolean</code> | Allow marking bundles as errored from JavaScript while using manual update flows. When enabled, {@link CapacitorUpdaterPlugin.setBundleError} can change a bundle status to `error`. | <code>false</code> | 7.20.0 |
|
|
258
259
|
| **`persistCustomId`** | <code>boolean</code> | Persist the customId set through {@link CapacitorUpdaterPlugin.setCustomId} across app restarts. Only available for Android and iOS. | <code>false (will be true by default in a future major release v8.x.x)</code> | 7.17.3 |
|
|
259
260
|
| **`persistModifyUrl`** | <code>boolean</code> | Persist the updateUrl, statsUrl and channelUrl set through {@link CapacitorUpdaterPlugin.setUpdateUrl}, {@link CapacitorUpdaterPlugin.setStatsUrl} and {@link CapacitorUpdaterPlugin.setChannelUrl} across app restarts. Only available for Android and iOS. | <code>false</code> | 7.20.0 |
|
|
260
261
|
| **`defaultChannel`** | <code>string</code> | Set the default channel for the app in the config. Case sensitive. This will setting will override the default channel set in the cloud, but will still respect overrides made in the cloud. This requires the channel to allow devices to self dissociate/associate in the channel settings. https://capgo.app/docs/public-api/channels/#channel-configuration-options | <code>undefined</code> | 5.5.0 |
|
|
@@ -296,6 +297,7 @@ In `capacitor.config.json`:
|
|
|
296
297
|
"localApiFiles": undefined,
|
|
297
298
|
"allowModifyUrl": undefined,
|
|
298
299
|
"allowModifyAppId": undefined,
|
|
300
|
+
"allowManualBundleError": undefined,
|
|
299
301
|
"persistCustomId": undefined,
|
|
300
302
|
"persistModifyUrl": undefined,
|
|
301
303
|
"defaultChannel": undefined,
|
|
@@ -343,6 +345,7 @@ const config: CapacitorConfig = {
|
|
|
343
345
|
localApiFiles: undefined,
|
|
344
346
|
allowModifyUrl: undefined,
|
|
345
347
|
allowModifyAppId: undefined,
|
|
348
|
+
allowManualBundleError: undefined,
|
|
346
349
|
persistCustomId: undefined,
|
|
347
350
|
persistModifyUrl: undefined,
|
|
348
351
|
defaultChannel: undefined,
|
|
@@ -371,6 +374,7 @@ export default config;
|
|
|
371
374
|
* [`next(...)`](#next)
|
|
372
375
|
* [`set(...)`](#set)
|
|
373
376
|
* [`delete(...)`](#delete)
|
|
377
|
+
* [`setBundleError(...)`](#setbundleerror)
|
|
374
378
|
* [`list(...)`](#list)
|
|
375
379
|
* [`reset(...)`](#reset)
|
|
376
380
|
* [`current()`](#current)
|
|
@@ -392,6 +396,7 @@ export default config;
|
|
|
392
396
|
* [`addListener('noNeedUpdate', ...)`](#addlistenernoneedupdate-)
|
|
393
397
|
* [`addListener('updateAvailable', ...)`](#addlistenerupdateavailable-)
|
|
394
398
|
* [`addListener('downloadComplete', ...)`](#addlistenerdownloadcomplete-)
|
|
399
|
+
* [`addListener('breakingAvailable', ...)`](#addlistenerbreakingavailable-)
|
|
395
400
|
* [`addListener('majorAvailable', ...)`](#addlistenermajoravailable-)
|
|
396
401
|
* [`addListener('updateFailed', ...)`](#addlistenerupdatefailed-)
|
|
397
402
|
* [`addListener('downloadFailed', ...)`](#addlistenerdownloadfailed-)
|
|
@@ -399,6 +404,7 @@ export default config;
|
|
|
399
404
|
* [`addListener('appReady', ...)`](#addlistenerappready-)
|
|
400
405
|
* [`isAutoUpdateAvailable()`](#isautoupdateavailable)
|
|
401
406
|
* [`getNextBundle()`](#getnextbundle)
|
|
407
|
+
* [`getFailedUpdate()`](#getfailedupdate)
|
|
402
408
|
* [`setShakeMenu(...)`](#setshakemenu)
|
|
403
409
|
* [`isShakeMenuEnabled()`](#isshakemenuenabled)
|
|
404
410
|
* [`getAppId()`](#getappid)
|
|
@@ -541,6 +547,25 @@ Deletes the specified bundle from the native app storage. Use with {@link list}
|
|
|
541
547
|
--------------------
|
|
542
548
|
|
|
543
549
|
|
|
550
|
+
### setBundleError(...)
|
|
551
|
+
|
|
552
|
+
```typescript
|
|
553
|
+
setBundleError(options: BundleId) => Promise<BundleInfo>
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
Mark an installed bundle as errored. Only available when {@link PluginsConfig.CapacitorUpdater.allowManualBundleError} is true.
|
|
557
|
+
|
|
558
|
+
| Param | Type | Description |
|
|
559
|
+
| ------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
560
|
+
| **`options`** | <code><a href="#bundleid">BundleId</a></code> | A {@link <a href="#bundleid">BundleId</a>} object containing the bundle id to mark as errored. |
|
|
561
|
+
|
|
562
|
+
**Returns:** <code>Promise<<a href="#bundleinfo">BundleInfo</a>></code>
|
|
563
|
+
|
|
564
|
+
**Since:** 7.20.0
|
|
565
|
+
|
|
566
|
+
--------------------
|
|
567
|
+
|
|
568
|
+
|
|
544
569
|
### list(...)
|
|
545
570
|
|
|
546
571
|
```typescript
|
|
@@ -890,6 +915,27 @@ Listen for downloadComplete events.
|
|
|
890
915
|
--------------------
|
|
891
916
|
|
|
892
917
|
|
|
918
|
+
### addListener('breakingAvailable', ...)
|
|
919
|
+
|
|
920
|
+
```typescript
|
|
921
|
+
addListener(eventName: 'breakingAvailable', listenerFunc: (state: BreakingAvailableEvent) => void) => Promise<PluginListenerHandle>
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
Listen for breaking update events when the backend flags an update as incompatible with the current app.
|
|
925
|
+
Emits the same payload as the legacy `majorAvailable` listener.
|
|
926
|
+
|
|
927
|
+
| Param | Type |
|
|
928
|
+
| ------------------ | --------------------------------------------------------------------------------------- |
|
|
929
|
+
| **`eventName`** | <code>'breakingAvailable'</code> |
|
|
930
|
+
| **`listenerFunc`** | <code>(state: <a href="#majoravailableevent">MajorAvailableEvent</a>) => void</code> |
|
|
931
|
+
|
|
932
|
+
**Returns:** <code>Promise<<a href="#pluginlistenerhandle">PluginListenerHandle</a>></code>
|
|
933
|
+
|
|
934
|
+
**Since:** 7.22.0
|
|
935
|
+
|
|
936
|
+
--------------------
|
|
937
|
+
|
|
938
|
+
|
|
893
939
|
### addListener('majorAvailable', ...)
|
|
894
940
|
|
|
895
941
|
```typescript
|
|
@@ -1019,6 +1065,21 @@ Returns null if no next bundle is set.
|
|
|
1019
1065
|
--------------------
|
|
1020
1066
|
|
|
1021
1067
|
|
|
1068
|
+
### getFailedUpdate()
|
|
1069
|
+
|
|
1070
|
+
```typescript
|
|
1071
|
+
getFailedUpdate() => Promise<UpdateFailedEvent | null>
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
Get the most recent update that failed to install, if any. The stored value is cleared after it is retrieved once.
|
|
1075
|
+
|
|
1076
|
+
**Returns:** <code>Promise<<a href="#updatefailedevent">UpdateFailedEvent</a> | null></code>
|
|
1077
|
+
|
|
1078
|
+
**Since:** 7.22.0
|
|
1079
|
+
|
|
1080
|
+
--------------------
|
|
1081
|
+
|
|
1082
|
+
|
|
1022
1083
|
### setShakeMenu(...)
|
|
1023
1084
|
|
|
1024
1085
|
```typescript
|
|
@@ -1201,17 +1262,18 @@ If you don't use backend, you need to provide the URL and version of the bundle.
|
|
|
1201
1262
|
|
|
1202
1263
|
#### LatestVersion
|
|
1203
1264
|
|
|
1204
|
-
| Prop | Type | Description
|
|
1205
|
-
| ---------------- | ---------------------------- |
|
|
1206
|
-
| **`version`** | <code>string</code> | Result of getLatest method
|
|
1207
|
-
| **`checksum`** | <code>string</code> |
|
|
1208
|
-
| **`
|
|
1209
|
-
| **`
|
|
1210
|
-
| **`
|
|
1211
|
-
| **`
|
|
1212
|
-
| **`
|
|
1213
|
-
| **`
|
|
1214
|
-
| **`
|
|
1265
|
+
| Prop | Type | Description | Since |
|
|
1266
|
+
| ---------------- | ---------------------------- | -------------------------------------------------------------------- | ------ |
|
|
1267
|
+
| **`version`** | <code>string</code> | Result of getLatest method | 4.0.0 |
|
|
1268
|
+
| **`checksum`** | <code>string</code> | | 6 |
|
|
1269
|
+
| **`breaking`** | <code>boolean</code> | Indicates whether the update was flagged as breaking by the backend. | 7.22.0 |
|
|
1270
|
+
| **`major`** | <code>boolean</code> | | |
|
|
1271
|
+
| **`message`** | <code>string</code> | | |
|
|
1272
|
+
| **`sessionKey`** | <code>string</code> | | |
|
|
1273
|
+
| **`error`** | <code>string</code> | | |
|
|
1274
|
+
| **`old`** | <code>string</code> | | |
|
|
1275
|
+
| **`url`** | <code>string</code> | | |
|
|
1276
|
+
| **`manifest`** | <code>ManifestEntry[]</code> | | 6.1 |
|
|
1215
1277
|
|
|
1216
1278
|
|
|
1217
1279
|
#### GetLatestOptions
|
|
@@ -1346,9 +1408,9 @@ If you don't use backend, you need to provide the URL and version of the bundle.
|
|
|
1346
1408
|
|
|
1347
1409
|
#### MajorAvailableEvent
|
|
1348
1410
|
|
|
1349
|
-
| Prop | Type | Description
|
|
1350
|
-
| ------------- | ------------------- |
|
|
1351
|
-
| **`version`** | <code>string</code> | Emit when a
|
|
1411
|
+
| Prop | Type | Description | Since |
|
|
1412
|
+
| ------------- | ------------------- | ----------------------------------------- | ----- |
|
|
1413
|
+
| **`version`** | <code>string</code> | Emit when a breaking update is available. | 4.0.0 |
|
|
1352
1414
|
|
|
1353
1415
|
|
|
1354
1416
|
#### UpdateFailedEvent
|
|
@@ -1425,6 +1487,13 @@ error: The bundle has failed to download.
|
|
|
1425
1487
|
|
|
1426
1488
|
<code>'background' | 'kill' | 'nativeVersion' | 'date'</code>
|
|
1427
1489
|
|
|
1490
|
+
|
|
1491
|
+
#### BreakingAvailableEvent
|
|
1492
|
+
|
|
1493
|
+
Payload emitted by {@link CapacitorUpdaterPlugin.addListener} with `breakingAvailable`.
|
|
1494
|
+
|
|
1495
|
+
<code><a href="#majoravailableevent">MajorAvailableEvent</a></code>
|
|
1496
|
+
|
|
1428
1497
|
</docgen-api>
|
|
1429
1498
|
|
|
1430
1499
|
### Listen to download events
|
|
@@ -63,12 +63,15 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
63
63
|
private static final String updateUrlDefault = "https://plugin.capgo.app/updates";
|
|
64
64
|
private static final String statsUrlDefault = "https://plugin.capgo.app/stats";
|
|
65
65
|
private static final String channelUrlDefault = "https://plugin.capgo.app/channel_self";
|
|
66
|
+
private static final String KEEP_URL_FLAG_KEY = "__capgo_keep_url_path_after_reload";
|
|
66
67
|
private static final String CUSTOM_ID_PREF_KEY = "CapacitorUpdater.customId";
|
|
67
68
|
private static final String UPDATE_URL_PREF_KEY = "CapacitorUpdater.updateUrl";
|
|
68
69
|
private static final String STATS_URL_PREF_KEY = "CapacitorUpdater.statsUrl";
|
|
69
70
|
private static final String CHANNEL_URL_PREF_KEY = "CapacitorUpdater.channelUrl";
|
|
71
|
+
private static final String[] BREAKING_EVENT_NAMES = { "breakingAvailable", "majorAvailable" };
|
|
72
|
+
private static final String LAST_FAILED_BUNDLE_PREF_KEY = "CapacitorUpdater.lastFailedBundle";
|
|
70
73
|
|
|
71
|
-
private final String PLUGIN_VERSION = "7.
|
|
74
|
+
private final String PLUGIN_VERSION = "7.22.0";
|
|
72
75
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
73
76
|
|
|
74
77
|
private SharedPreferences.Editor editor;
|
|
@@ -96,6 +99,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
96
99
|
private String directUpdateMode = "false";
|
|
97
100
|
private Boolean wasRecentlyInstalledOrUpdated = false;
|
|
98
101
|
Boolean shakeMenuEnabled = false;
|
|
102
|
+
private Boolean allowManualBundleError = false;
|
|
99
103
|
|
|
100
104
|
private Boolean isPreviousMainActivity = true;
|
|
101
105
|
|
|
@@ -114,6 +118,17 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
114
118
|
private FrameLayout splashscreenLoaderOverlay;
|
|
115
119
|
private Runnable splashscreenTimeoutRunnable;
|
|
116
120
|
|
|
121
|
+
private void notifyBreakingEvents(final String version) {
|
|
122
|
+
if (version == null || version.isEmpty()) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
for (final String eventName : BREAKING_EVENT_NAMES) {
|
|
126
|
+
final JSObject payload = new JSObject();
|
|
127
|
+
payload.put("version", version);
|
|
128
|
+
CapacitorUpdaterPlugin.this.notifyListeners(eventName, payload);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
117
132
|
private JSObject mapToJSObject(Map<String, Object> map) {
|
|
118
133
|
JSObject jsObject = new JSObject();
|
|
119
134
|
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
|
@@ -122,6 +137,37 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
122
137
|
return jsObject;
|
|
123
138
|
}
|
|
124
139
|
|
|
140
|
+
private void persistLastFailedBundle(BundleInfo bundle) {
|
|
141
|
+
if (this.prefs == null) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
final SharedPreferences.Editor localEditor = this.prefs.edit();
|
|
145
|
+
if (bundle == null) {
|
|
146
|
+
localEditor.remove(LAST_FAILED_BUNDLE_PREF_KEY);
|
|
147
|
+
} else {
|
|
148
|
+
final JSONObject json = new JSONObject(bundle.toJSONMap());
|
|
149
|
+
localEditor.putString(LAST_FAILED_BUNDLE_PREF_KEY, json.toString());
|
|
150
|
+
}
|
|
151
|
+
localEditor.apply();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private BundleInfo readLastFailedBundle() {
|
|
155
|
+
if (this.prefs == null) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
final String raw = this.prefs.getString(LAST_FAILED_BUNDLE_PREF_KEY, null);
|
|
159
|
+
if (raw == null || raw.trim().isEmpty()) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
return BundleInfo.fromJSON(raw);
|
|
164
|
+
} catch (final JSONException e) {
|
|
165
|
+
logger.error("Failed to parse failed bundle info: " + e.getMessage());
|
|
166
|
+
this.persistLastFailedBundle(null);
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
125
171
|
public Thread startNewThread(final Runnable function, Number waitTime) {
|
|
126
172
|
Thread bgTask = new Thread(() -> {
|
|
127
173
|
try {
|
|
@@ -152,17 +198,23 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
152
198
|
this.implementation = new CapgoUpdater(logger) {
|
|
153
199
|
@Override
|
|
154
200
|
public void notifyDownload(final String id, final int percent) {
|
|
155
|
-
|
|
201
|
+
activity.runOnUiThread(() -> {
|
|
202
|
+
CapacitorUpdaterPlugin.this.notifyDownload(id, percent);
|
|
203
|
+
});
|
|
156
204
|
}
|
|
157
205
|
|
|
158
206
|
@Override
|
|
159
207
|
public void directUpdateFinish(final BundleInfo latest) {
|
|
160
|
-
|
|
208
|
+
activity.runOnUiThread(() -> {
|
|
209
|
+
CapacitorUpdaterPlugin.this.directUpdateFinish(latest);
|
|
210
|
+
});
|
|
161
211
|
}
|
|
162
212
|
|
|
163
213
|
@Override
|
|
164
214
|
public void notifyListeners(final String id, final Map<String, Object> res) {
|
|
165
|
-
|
|
215
|
+
activity.runOnUiThread(() -> {
|
|
216
|
+
CapacitorUpdaterPlugin.this.notifyListeners(id, CapacitorUpdaterPlugin.this.mapToJSObject(res));
|
|
217
|
+
});
|
|
166
218
|
}
|
|
167
219
|
};
|
|
168
220
|
final PackageInfo pInfo = this.getContext().getPackageManager().getPackageInfo(this.getContext().getPackageName(), 0);
|
|
@@ -288,6 +340,8 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
288
340
|
this.autoUpdate = this.getConfig().getBoolean("autoUpdate", true);
|
|
289
341
|
this.appReadyTimeout = this.getConfig().getInt("appReadyTimeout", 10000);
|
|
290
342
|
this.keepUrlPathAfterReload = this.getConfig().getBoolean("keepUrlPathAfterReload", false);
|
|
343
|
+
this.syncKeepUrlPathFlag(this.keepUrlPathAfterReload);
|
|
344
|
+
this.allowManualBundleError = this.getConfig().getBoolean("allowManualBundleError", false);
|
|
291
345
|
this.autoSplashscreen = this.getConfig().getBoolean("autoSplashscreen", false);
|
|
292
346
|
this.autoSplashscreenLoader = this.getConfig().getBoolean("autoSplashscreenLoader", false);
|
|
293
347
|
int splashscreenTimeoutValue = this.getConfig().getInt("autoSplashscreenTimeout", 10000);
|
|
@@ -584,30 +638,36 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
584
638
|
}
|
|
585
639
|
|
|
586
640
|
private void cleanupObsoleteVersions() {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
641
|
+
startNewThread(() -> {
|
|
642
|
+
try {
|
|
643
|
+
final String previous = this.prefs.getString("LatestNativeBuildVersion", "");
|
|
644
|
+
if (!"".equals(previous) && !Objects.equals(this.currentBuildVersion, previous)) {
|
|
645
|
+
logger.info("New native build version detected: " + this.currentBuildVersion);
|
|
646
|
+
this.implementation.reset(true);
|
|
647
|
+
final List<BundleInfo> installed = this.implementation.list(false);
|
|
648
|
+
for (final BundleInfo bundle : installed) {
|
|
649
|
+
try {
|
|
650
|
+
logger.info("Deleting obsolete bundle: " + bundle.getId());
|
|
651
|
+
this.implementation.delete(bundle.getId());
|
|
652
|
+
} catch (final Exception e) {
|
|
653
|
+
logger.error("Failed to delete: " + bundle.getId() + " " + e.getMessage());
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
final List<BundleInfo> storedBundles = this.implementation.list(true);
|
|
657
|
+
final Set<String> allowedIds = new HashSet<>();
|
|
658
|
+
for (final BundleInfo info : storedBundles) {
|
|
659
|
+
if (info != null && info.getId() != null && !info.getId().isEmpty()) {
|
|
660
|
+
allowedIds.add(info.getId());
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
this.implementation.cleanupDownloadDirectories(allowedIds);
|
|
605
664
|
}
|
|
665
|
+
this.editor.putString("LatestNativeBuildVersion", this.currentBuildVersion);
|
|
666
|
+
this.editor.apply();
|
|
667
|
+
} catch (Exception e) {
|
|
668
|
+
logger.error("Error during cleanupObsoleteVersions: " + e.getMessage());
|
|
606
669
|
}
|
|
607
|
-
|
|
608
|
-
}
|
|
609
|
-
this.editor.putString("LatestNativeBuildVersion", this.currentBuildVersion);
|
|
610
|
-
this.editor.apply();
|
|
670
|
+
});
|
|
611
671
|
}
|
|
612
672
|
|
|
613
673
|
public void notifyDownload(final String id, final int percent) {
|
|
@@ -943,8 +1003,25 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
943
1003
|
}
|
|
944
1004
|
}
|
|
945
1005
|
|
|
1006
|
+
private void syncKeepUrlPathFlag(final boolean enabled) {
|
|
1007
|
+
if (this.bridge == null || this.bridge.getWebView() == null) {
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
final String script = enabled
|
|
1011
|
+
? "(function(){try{localStorage.setItem('" +
|
|
1012
|
+
KEEP_URL_FLAG_KEY +
|
|
1013
|
+
"','1');}catch(e){}window.__capgoKeepUrlPathAfterReload=true;var evt;try{evt=new CustomEvent('CapacitorUpdaterKeepUrlPathAfterReload',{detail:{enabled:true}});}catch(err){evt=document.createEvent('CustomEvent');evt.initCustomEvent('CapacitorUpdaterKeepUrlPathAfterReload',false,false,{enabled:true});}window.dispatchEvent(evt);})();"
|
|
1014
|
+
: "(function(){try{localStorage.removeItem('" +
|
|
1015
|
+
KEEP_URL_FLAG_KEY +
|
|
1016
|
+
"');}catch(e){}delete window.__capgoKeepUrlPathAfterReload;var evt;try{evt=new CustomEvent('CapacitorUpdaterKeepUrlPathAfterReload',{detail:{enabled:false}});}catch(err){evt=document.createEvent('CustomEvent');evt.initCustomEvent('CapacitorUpdaterKeepUrlPathAfterReload',false,false,{enabled:false});}window.dispatchEvent(evt);})();";
|
|
1017
|
+
this.bridge.getWebView().post(() -> this.bridge.getWebView().evaluateJavascript(script, null));
|
|
1018
|
+
}
|
|
1019
|
+
|
|
946
1020
|
protected boolean _reload() {
|
|
947
1021
|
final String path = this.implementation.getCurrentBundlePath();
|
|
1022
|
+
if (this.keepUrlPathAfterReload) {
|
|
1023
|
+
this.syncKeepUrlPathFlag(true);
|
|
1024
|
+
}
|
|
948
1025
|
this.semaphoreUp();
|
|
949
1026
|
logger.info("Reloading: " + path);
|
|
950
1027
|
|
|
@@ -1003,7 +1080,9 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1003
1080
|
URL finalUrl1 = finalUrl;
|
|
1004
1081
|
this.bridge.getWebView().post(() -> {
|
|
1005
1082
|
this.bridge.getWebView().loadUrl(finalUrl1.toString());
|
|
1006
|
-
this.
|
|
1083
|
+
if (!this.keepUrlPathAfterReload) {
|
|
1084
|
+
this.bridge.getWebView().clearHistory();
|
|
1085
|
+
}
|
|
1007
1086
|
});
|
|
1008
1087
|
} catch (MalformedURLException e) {
|
|
1009
1088
|
logger.error("Cannot get finalUrl from capacitor bridge " + e.getMessage());
|
|
@@ -1020,6 +1099,16 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1020
1099
|
} else {
|
|
1021
1100
|
this.bridge.setServerBasePath(path);
|
|
1022
1101
|
}
|
|
1102
|
+
if (this.bridge != null && this.bridge.getWebView() != null) {
|
|
1103
|
+
this.bridge.getWebView().post(() -> {
|
|
1104
|
+
if (this.bridge.getWebView() != null) {
|
|
1105
|
+
this.bridge.getWebView().loadUrl(this.bridge.getAppUrl());
|
|
1106
|
+
if (!this.keepUrlPathAfterReload) {
|
|
1107
|
+
this.bridge.getWebView().clearHistory();
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1023
1112
|
}
|
|
1024
1113
|
|
|
1025
1114
|
this.checkAppReady();
|
|
@@ -1119,6 +1208,44 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1119
1208
|
}
|
|
1120
1209
|
}
|
|
1121
1210
|
|
|
1211
|
+
@PluginMethod
|
|
1212
|
+
public void setBundleError(final PluginCall call) {
|
|
1213
|
+
if (!Boolean.TRUE.equals(this.allowManualBundleError)) {
|
|
1214
|
+
logger.error("setBundleError called without allowManualBundleError");
|
|
1215
|
+
call.reject("setBundleError not allowed. Set allowManualBundleError to true in your config to enable it.");
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
final String id = call.getString("id");
|
|
1219
|
+
if (id == null) {
|
|
1220
|
+
logger.error("setBundleError called without id");
|
|
1221
|
+
call.reject("setBundleError called without id");
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
try {
|
|
1225
|
+
final BundleInfo bundle = this.implementation.getBundleInfo(id);
|
|
1226
|
+
if (bundle == null || bundle.isUnknown()) {
|
|
1227
|
+
logger.error("setBundleError called with unknown bundle " + id);
|
|
1228
|
+
call.reject("Bundle " + id + " does not exist");
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
if (bundle.isBuiltin()) {
|
|
1232
|
+
logger.error("setBundleError called on builtin bundle");
|
|
1233
|
+
call.reject("Cannot set builtin bundle to error state");
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
if (Boolean.TRUE.equals(this.autoUpdate)) {
|
|
1237
|
+
logger.warn("setBundleError used while autoUpdate is enabled; this method is intended for manual mode");
|
|
1238
|
+
}
|
|
1239
|
+
this.implementation.setError(bundle);
|
|
1240
|
+
final JSObject ret = new JSObject();
|
|
1241
|
+
ret.put("bundle", mapToJSObject(this.implementation.getBundleInfo(id).toJSONMap()));
|
|
1242
|
+
call.resolve(ret);
|
|
1243
|
+
} catch (final Exception e) {
|
|
1244
|
+
logger.error("Could not set bundle error for id " + id + " " + e.getMessage());
|
|
1245
|
+
call.reject("Could not set bundle error for id " + id, e);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1122
1249
|
@PluginMethod
|
|
1123
1250
|
public void list(final PluginCall call) {
|
|
1124
1251
|
try {
|
|
@@ -1215,6 +1342,26 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1215
1342
|
}
|
|
1216
1343
|
}
|
|
1217
1344
|
|
|
1345
|
+
@PluginMethod
|
|
1346
|
+
public void getFailedUpdate(final PluginCall call) {
|
|
1347
|
+
try {
|
|
1348
|
+
final BundleInfo bundle = this.readLastFailedBundle();
|
|
1349
|
+
if (bundle == null || bundle.isUnknown()) {
|
|
1350
|
+
call.resolve(null);
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
this.persistLastFailedBundle(null);
|
|
1355
|
+
|
|
1356
|
+
final JSObject ret = new JSObject();
|
|
1357
|
+
ret.put("bundle", mapToJSObject(bundle.toJSONMap()));
|
|
1358
|
+
call.resolve(ret);
|
|
1359
|
+
} catch (final Exception e) {
|
|
1360
|
+
logger.error("Could not get failed update " + e.getMessage());
|
|
1361
|
+
call.reject("Could not get failed update", e);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1218
1365
|
public void checkForUpdateAfterDelay() {
|
|
1219
1366
|
if (this.periodCheckDelay == 0 || !this._isAutoUpdateEnabled()) {
|
|
1220
1367
|
return;
|
|
@@ -1459,10 +1606,8 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1459
1606
|
try {
|
|
1460
1607
|
if (jsRes.has("message")) {
|
|
1461
1608
|
logger.info("API message: " + jsRes.get("message"));
|
|
1462
|
-
if (jsRes.has("
|
|
1463
|
-
|
|
1464
|
-
majorAvailable.put("version", jsRes.getString("version"));
|
|
1465
|
-
CapacitorUpdaterPlugin.this.notifyListeners("majorAvailable", majorAvailable);
|
|
1609
|
+
if (jsRes.has("version") && (jsRes.has("breaking") || jsRes.has("major"))) {
|
|
1610
|
+
CapacitorUpdaterPlugin.this.notifyBreakingEvents(jsRes.getString("version"));
|
|
1466
1611
|
}
|
|
1467
1612
|
String latestVersion = jsRes.has("version") ? jsRes.getString("version") : current.getVersionName();
|
|
1468
1613
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
@@ -1717,6 +1862,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1717
1862
|
logger.info("Did you forget to call 'notifyAppReady()' in your Capacitor App code?");
|
|
1718
1863
|
final JSObject ret = new JSObject();
|
|
1719
1864
|
ret.put("bundle", mapToJSObject(current.toJSONMap()));
|
|
1865
|
+
this.persistLastFailedBundle(current);
|
|
1720
1866
|
this.notifyListeners("updateFailed", ret);
|
|
1721
1867
|
this.implementation.sendStats("update_fail", current.getVersionName());
|
|
1722
1868
|
this.implementation.setError(current);
|
|
@@ -35,6 +35,8 @@ import java.util.Objects;
|
|
|
35
35
|
import java.util.Set;
|
|
36
36
|
import java.util.concurrent.CompletableFuture;
|
|
37
37
|
import java.util.concurrent.ConcurrentHashMap;
|
|
38
|
+
import java.util.concurrent.ExecutorService;
|
|
39
|
+
import java.util.concurrent.Executors;
|
|
38
40
|
import java.util.concurrent.TimeUnit;
|
|
39
41
|
import java.util.zip.ZipEntry;
|
|
40
42
|
import java.util.zip.ZipInputStream;
|
|
@@ -80,6 +82,7 @@ public class CapgoUpdater {
|
|
|
80
82
|
public int timeout = 20000;
|
|
81
83
|
|
|
82
84
|
private final Map<String, CompletableFuture<BundleInfo>> downloadFutures = new ConcurrentHashMap<>();
|
|
85
|
+
private final ExecutorService io = Executors.newSingleThreadExecutor();
|
|
83
86
|
|
|
84
87
|
public CapgoUpdater(Logger logger) {
|
|
85
88
|
this.logger = logger;
|
|
@@ -245,60 +248,71 @@ public class CapgoUpdater {
|
|
|
245
248
|
String checksum = outputData.getString(DownloadService.CHECKSUM);
|
|
246
249
|
boolean isManifest = outputData.getBoolean(DownloadService.IS_MANIFEST, false);
|
|
247
250
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
251
|
+
io.execute(() -> {
|
|
252
|
+
boolean success = finishDownload(id, dest, version, sessionKey, checksum, true, isManifest);
|
|
253
|
+
BundleInfo resultBundle;
|
|
254
|
+
if (!success) {
|
|
255
|
+
logger.error("Finish download failed: " + version);
|
|
256
|
+
resultBundle = new BundleInfo(
|
|
257
|
+
id,
|
|
258
|
+
version,
|
|
259
|
+
BundleStatus.ERROR,
|
|
260
|
+
new Date(System.currentTimeMillis()),
|
|
261
|
+
""
|
|
262
|
+
);
|
|
263
|
+
saveBundleInfo(id, resultBundle);
|
|
264
|
+
// Cleanup download tracking
|
|
265
|
+
DownloadWorkerManager.cancelBundleDownload(activity, id, version);
|
|
266
|
+
Map<String, Object> ret = new HashMap<>();
|
|
267
|
+
ret.put("version", version);
|
|
268
|
+
ret.put("error", "finish_download_fail");
|
|
269
|
+
sendStats("finish_download_fail", version);
|
|
270
|
+
notifyListeners("downloadFailed", ret);
|
|
271
|
+
} else {
|
|
272
|
+
// Successful download - cleanup tracking
|
|
273
|
+
DownloadWorkerManager.cancelBundleDownload(activity, id, version);
|
|
274
|
+
resultBundle = getBundleInfo(id);
|
|
275
|
+
}
|
|
266
276
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
277
|
+
// Complete the future if it exists
|
|
278
|
+
CompletableFuture<BundleInfo> future = downloadFutures.remove(id);
|
|
279
|
+
if (future != null) {
|
|
280
|
+
future.complete(resultBundle);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
272
283
|
break;
|
|
273
284
|
case FAILED:
|
|
274
285
|
Data failedData = workInfo.getOutputData();
|
|
275
286
|
String error = failedData.getString(DownloadService.ERROR);
|
|
276
287
|
logger.error("Download failed: " + error + " " + workInfo.getState());
|
|
277
288
|
String failedVersion = failedData.getString(DownloadService.VERSION);
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
failedFuture.
|
|
301
|
-
|
|
289
|
+
|
|
290
|
+
io.execute(() -> {
|
|
291
|
+
BundleInfo failedBundle = new BundleInfo(
|
|
292
|
+
id,
|
|
293
|
+
failedVersion,
|
|
294
|
+
BundleStatus.ERROR,
|
|
295
|
+
new Date(System.currentTimeMillis()),
|
|
296
|
+
""
|
|
297
|
+
);
|
|
298
|
+
saveBundleInfo(id, failedBundle);
|
|
299
|
+
// Cleanup download tracking for failed downloads
|
|
300
|
+
DownloadWorkerManager.cancelBundleDownload(activity, id, failedVersion);
|
|
301
|
+
Map<String, Object> ret = new HashMap<>();
|
|
302
|
+
ret.put("version", failedVersion);
|
|
303
|
+
if ("low_mem_fail".equals(error)) {
|
|
304
|
+
sendStats("low_mem_fail", failedVersion);
|
|
305
|
+
}
|
|
306
|
+
ret.put("error", error != null ? error : "download_fail");
|
|
307
|
+
sendStats("download_fail", failedVersion);
|
|
308
|
+
notifyListeners("downloadFailed", ret);
|
|
309
|
+
|
|
310
|
+
// Complete the future with error status
|
|
311
|
+
CompletableFuture<BundleInfo> failedFuture = downloadFutures.remove(id);
|
|
312
|
+
if (failedFuture != null) {
|
|
313
|
+
failedFuture.complete(failedBundle);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
302
316
|
break;
|
|
303
317
|
}
|
|
304
318
|
});
|