@capgo/capacitor-updater 8.45.10 → 8.45.11
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 +74 -28
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +147 -50
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +40 -16
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeMenu.java +27 -3
- package/dist/docs.json +163 -4
- package/dist/esm/definitions.d.ts +82 -19
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/BundleInfo.swift +2 -2
- package/ios/Sources/CapacitorUpdaterPlugin/BundleStatus.swift +78 -2
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +289 -94
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +672 -300
- package/ios/Sources/CapacitorUpdaterPlugin/InternalUtils.swift +31 -0
- package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +20 -3
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -462,6 +462,7 @@ export default config;
|
|
|
462
462
|
* [`removeAllListeners()`](#removealllisteners)
|
|
463
463
|
* [`addListener('download', ...)`](#addlistenerdownload-)
|
|
464
464
|
* [`addListener('noNeedUpdate', ...)`](#addlistenernoneedupdate-)
|
|
465
|
+
* [`addListener('updateCheckResult', ...)`](#addlistenerupdatecheckresult-)
|
|
465
466
|
* [`addListener('updateAvailable', ...)`](#addlistenerupdateavailable-)
|
|
466
467
|
* [`addListener('downloadComplete', ...)`](#addlistenerdownloadcomplete-)
|
|
467
468
|
* [`addListener('breakingAvailable', ...)`](#addlistenerbreakingavailable-)
|
|
@@ -929,27 +930,23 @@ After receiving the latest version info, you can:
|
|
|
929
930
|
2. Download it using {@link download}
|
|
930
931
|
3. Apply it using {@link next} or {@link set}
|
|
931
932
|
|
|
932
|
-
**Important:
|
|
933
|
+
**Important: Handling "no new version available"**
|
|
933
934
|
|
|
934
935
|
When the device's current version matches the latest version on the server (i.e., the device is already
|
|
935
936
|
up-to-date), the server returns a 200 response with `error: "no_new_version_available"` and
|
|
936
|
-
`message: "No new version available"`.
|
|
937
|
-
|
|
937
|
+
`message: "No new version available"`. This is a normal, expected condition and resolves with
|
|
938
|
+
`kind: "up_to_date"` when the backend provides that classification.
|
|
938
939
|
|
|
939
|
-
You should
|
|
940
|
+
You should check `kind` and `error` before attempting to download:
|
|
940
941
|
|
|
941
942
|
```typescript
|
|
942
|
-
|
|
943
|
-
|
|
943
|
+
const latest = await CapacitorUpdater.getLatest();
|
|
944
|
+
if (latest.kind === 'up_to_date') {
|
|
945
|
+
console.log('Already up to date');
|
|
946
|
+
} else if (latest.kind === 'blocked') {
|
|
947
|
+
console.log('Update is blocked:', latest.error);
|
|
948
|
+
} else if (latest.url) {
|
|
944
949
|
// New version is available, proceed with download
|
|
945
|
-
} catch (error) {
|
|
946
|
-
if (error.message === 'No new version available') {
|
|
947
|
-
// Device is already on the latest version - this is normal
|
|
948
|
-
console.log('Already up to date');
|
|
949
|
-
} else {
|
|
950
|
-
// Actual error occurred
|
|
951
|
-
console.error('Failed to check for updates:', error);
|
|
952
|
-
}
|
|
953
950
|
}
|
|
954
951
|
```
|
|
955
952
|
|
|
@@ -1251,6 +1248,7 @@ Remove all event listeners registered for this plugin.
|
|
|
1251
1248
|
This unregisters all listeners added via {@link addListener} for all event types:
|
|
1252
1249
|
- `download`
|
|
1253
1250
|
- `noNeedUpdate`
|
|
1251
|
+
- `updateCheckResult`
|
|
1254
1252
|
- `updateAvailable`
|
|
1255
1253
|
- `downloadComplete`
|
|
1256
1254
|
- `downloadFailed`
|
|
@@ -1308,6 +1306,31 @@ Listen for no need to update event, useful when you want force check every time
|
|
|
1308
1306
|
--------------------
|
|
1309
1307
|
|
|
1310
1308
|
|
|
1309
|
+
#### addListener('updateCheckResult', ...)
|
|
1310
|
+
|
|
1311
|
+
```typescript
|
|
1312
|
+
addListener(eventName: 'updateCheckResult', listenerFunc: (state: UpdateCheckResultEvent) => void) => Promise<PluginListenerHandle>
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
Listen for update check results before the updater decides whether to download.
|
|
1316
|
+
The backend can classify the <a href="#updatecheckresultevent">UpdateCheckResultEvent</a> payload as `up_to_date`, `blocked`, or `failed`.
|
|
1317
|
+
|
|
1318
|
+
This event is emitted alongside legacy events. For `up_to_date` and `blocked`, it is emitted before
|
|
1319
|
+
`noNeedUpdate` and does not emit `downloadFailed`. For `failed`, it is emitted before the legacy
|
|
1320
|
+
`downloadFailed` event and keeps the existing failure stats behavior.
|
|
1321
|
+
|
|
1322
|
+
| Param | Type |
|
|
1323
|
+
| ------------------ | --------------------------------------------------------------------------------------------- |
|
|
1324
|
+
| **`eventName`** | <code>'updateCheckResult'</code> |
|
|
1325
|
+
| **`listenerFunc`** | <code>(state: <a href="#updatecheckresultevent">UpdateCheckResultEvent</a>) => void</code> |
|
|
1326
|
+
|
|
1327
|
+
**Returns:** <code>Promise<<a href="#pluginlistenerhandle">PluginListenerHandle</a>></code>
|
|
1328
|
+
|
|
1329
|
+
**Since:** 8.45.11
|
|
1330
|
+
|
|
1331
|
+
--------------------
|
|
1332
|
+
|
|
1333
|
+
|
|
1311
1334
|
#### addListener('updateAvailable', ...)
|
|
1312
1335
|
|
|
1313
1336
|
```typescript
|
|
@@ -2094,20 +2117,22 @@ If you don't use backend, you need to provide the URL and version of the bundle.
|
|
|
2094
2117
|
|
|
2095
2118
|
##### LatestVersion
|
|
2096
2119
|
|
|
2097
|
-
| Prop | Type
|
|
2098
|
-
| ---------------- |
|
|
2099
|
-
| **`version`** | <code>string</code>
|
|
2100
|
-
| **`checksum`** | <code>string</code>
|
|
2101
|
-
| **`breaking`** | <code>boolean</code>
|
|
2102
|
-
| **`major`** | <code>boolean</code>
|
|
2103
|
-
| **`message`** | <code>string</code>
|
|
2104
|
-
| **`sessionKey`** | <code>string</code>
|
|
2105
|
-
| **`error`** | <code>string</code>
|
|
2106
|
-
| **`
|
|
2107
|
-
| **`
|
|
2108
|
-
| **`
|
|
2109
|
-
| **`
|
|
2110
|
-
| **`
|
|
2120
|
+
| Prop | Type | Description | Since |
|
|
2121
|
+
| ---------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------- |
|
|
2122
|
+
| **`version`** | <code>string</code> | Result of getLatest method | 4.0.0 |
|
|
2123
|
+
| **`checksum`** | <code>string</code> | | 6 |
|
|
2124
|
+
| **`breaking`** | <code>boolean</code> | Indicates whether the update was flagged as breaking by the backend. | 7.22.0 |
|
|
2125
|
+
| **`major`** | <code>boolean</code> | | |
|
|
2126
|
+
| **`message`** | <code>string</code> | Optional message from the server. When no new version is available, this will be "No new version available". | |
|
|
2127
|
+
| **`sessionKey`** | <code>string</code> | | |
|
|
2128
|
+
| **`error`** | <code>string</code> | Error code from the server, if any. Use `kind` for classification instead of parsing this value. | |
|
|
2129
|
+
| **`kind`** | <code><a href="#updateresponsekind">UpdateResponseKind</a></code> | Classification for this response, provided by the backend. | 8.45.11 |
|
|
2130
|
+
| **`statusCode`** | <code>number</code> | HTTP status code returned by the update server for classified update-check responses. | 8.45.11 |
|
|
2131
|
+
| **`old`** | <code>string</code> | The previous/current version name (provided for reference). | |
|
|
2132
|
+
| **`url`** | <code>string</code> | Download URL for the bundle (when a new version is available). | |
|
|
2133
|
+
| **`manifest`** | <code>ManifestEntry[]</code> | File list for delta updates (when using multi-file downloads). | 6.1 |
|
|
2134
|
+
| **`link`** | <code>string</code> | Optional link associated with this bundle version (e.g., release notes URL, changelog, GitHub release). | 7.35.0 |
|
|
2135
|
+
| **`comment`** | <code>string</code> | Optional comment or description for this bundle version. | 7.35.0 |
|
|
2111
2136
|
|
|
2112
2137
|
|
|
2113
2138
|
##### GetLatestOptions
|
|
@@ -2226,6 +2251,18 @@ If you don't use backend, you need to provide the URL and version of the bundle.
|
|
|
2226
2251
|
| **`bundle`** | <code><a href="#bundleinfo">BundleInfo</a></code> | Current status of download, between 0 and 100. | 4.0.0 |
|
|
2227
2252
|
|
|
2228
2253
|
|
|
2254
|
+
##### UpdateCheckResultEvent
|
|
2255
|
+
|
|
2256
|
+
| Prop | Type | Description | Since |
|
|
2257
|
+
| ---------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------- | ------- |
|
|
2258
|
+
| **`kind`** | <code><a href="#updateresponsekind">UpdateResponseKind</a></code> | Classification for the update check result, provided by the backend. | 8.45.11 |
|
|
2259
|
+
| **`error`** | <code>string</code> | Backend error code, when provided. | 8.45.11 |
|
|
2260
|
+
| **`message`** | <code>string</code> | Backend message, when provided. | 8.45.11 |
|
|
2261
|
+
| **`statusCode`** | <code>number</code> | HTTP status code returned by the update endpoint. | 8.45.11 |
|
|
2262
|
+
| **`version`** | <code>string</code> | Version referenced by the update check result. | 8.45.11 |
|
|
2263
|
+
| **`bundle`** | <code><a href="#bundleinfo">BundleInfo</a></code> | Current bundle on the device. | 8.45.11 |
|
|
2264
|
+
|
|
2265
|
+
|
|
2229
2266
|
##### UpdateAvailableEvent
|
|
2230
2267
|
|
|
2231
2268
|
| Prop | Type | Description | Since |
|
|
@@ -2417,6 +2454,15 @@ error: The bundle has failed to download.
|
|
|
2417
2454
|
<code>'background' | 'kill' | 'nativeVersion' | 'date'</code>
|
|
2418
2455
|
|
|
2419
2456
|
|
|
2457
|
+
##### UpdateResponseKind
|
|
2458
|
+
|
|
2459
|
+
Classification for update-check responses that do not provide a downloadable bundle.
|
|
2460
|
+
The update backend provides this field directly. Missing or unknown values are treated as
|
|
2461
|
+
failed by native clients.
|
|
2462
|
+
|
|
2463
|
+
<code>'up_to_date' | 'blocked' | 'failed'</code>
|
|
2464
|
+
|
|
2465
|
+
|
|
2420
2466
|
##### BreakingAvailableEvent
|
|
2421
2467
|
|
|
2422
2468
|
Payload emitted by {@link CapacitorUpdaterPlugin.addListener} with `breakingAvailable`.
|
|
@@ -50,7 +50,6 @@ import java.io.IOException;
|
|
|
50
50
|
import java.net.MalformedURLException;
|
|
51
51
|
import java.net.URL;
|
|
52
52
|
import java.util.ArrayList;
|
|
53
|
-
import java.util.Arrays;
|
|
54
53
|
import java.util.Date;
|
|
55
54
|
import java.util.HashSet;
|
|
56
55
|
import java.util.List;
|
|
@@ -90,7 +89,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
90
89
|
private static final int SPLASH_SCREEN_MAX_RETRIES = 20;
|
|
91
90
|
private static final long PENDING_BUNDLE_APP_READY_MIN_TIMEOUT_MS = 30000L;
|
|
92
91
|
|
|
93
|
-
private final String pluginVersion = "8.45.
|
|
92
|
+
private final String pluginVersion = "8.45.11";
|
|
94
93
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
95
94
|
|
|
96
95
|
private SharedPreferences.Editor editor;
|
|
@@ -1293,6 +1292,23 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1293
1292
|
startNewThread(() ->
|
|
1294
1293
|
CapacitorUpdaterPlugin.this.implementation.listChannels((res) -> {
|
|
1295
1294
|
JSObject jsRes = InternalUtils.mapToJSObject(res);
|
|
1295
|
+
Object channels = res.get("channels");
|
|
1296
|
+
if (channels instanceof List<?> channelsList) {
|
|
1297
|
+
JSArray channelsArray = new JSArray();
|
|
1298
|
+
for (Object channel : channelsList) {
|
|
1299
|
+
if (channel instanceof Map<?, ?> channelMap) {
|
|
1300
|
+
JSObject channelObject = new JSObject();
|
|
1301
|
+
for (Map.Entry<?, ?> entry : channelMap.entrySet()) {
|
|
1302
|
+
Object key = entry.getKey();
|
|
1303
|
+
if (key != null) {
|
|
1304
|
+
channelObject.put(key.toString(), entry.getValue());
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
channelsArray.put(channelObject);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
jsRes.put("channels", channelsArray);
|
|
1311
|
+
}
|
|
1296
1312
|
if (jsRes.has("error")) {
|
|
1297
1313
|
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : jsRes.getString("error");
|
|
1298
1314
|
String errorCode = jsRes.getString("error");
|
|
@@ -1582,23 +1598,25 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1582
1598
|
call.reject("Set called without id");
|
|
1583
1599
|
return;
|
|
1584
1600
|
}
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1601
|
+
startNewThread(() -> {
|
|
1602
|
+
try {
|
|
1603
|
+
logger.info("Setting active bundle " + id);
|
|
1604
|
+
if (!this.implementation.set(id)) {
|
|
1605
|
+
logger.info("No such bundle " + id);
|
|
1606
|
+
call.reject("Update failed, id " + id + " does not exist.");
|
|
1607
|
+
} else if (!this._reload()) {
|
|
1608
|
+
logger.error("Reload failed after setting bundle " + id);
|
|
1609
|
+
call.reject("Reload failed after setting bundle " + id);
|
|
1610
|
+
} else {
|
|
1611
|
+
logger.info("Bundle successfully set to " + id);
|
|
1612
|
+
this.notifyBundleSet(this.implementation.getBundleInfo(id));
|
|
1613
|
+
call.resolve();
|
|
1614
|
+
}
|
|
1615
|
+
} catch (final Exception e) {
|
|
1616
|
+
logger.error("Could not set id " + id + " " + e.getMessage());
|
|
1617
|
+
call.reject("Could not set id " + id, e);
|
|
1597
1618
|
}
|
|
1598
|
-
}
|
|
1599
|
-
logger.error("Could not set id " + id + " " + e.getMessage());
|
|
1600
|
-
call.reject("Could not set id " + id, e);
|
|
1601
|
-
}
|
|
1619
|
+
});
|
|
1602
1620
|
}
|
|
1603
1621
|
|
|
1604
1622
|
@PluginMethod
|
|
@@ -1685,11 +1703,21 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1685
1703
|
startNewThread(() ->
|
|
1686
1704
|
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, channel, (res) -> {
|
|
1687
1705
|
JSObject jsRes = InternalUtils.mapToJSObject(res);
|
|
1688
|
-
if (jsRes.has("error")) {
|
|
1689
|
-
String error = jsRes.getString("error");
|
|
1706
|
+
if (jsRes.has("error") || jsRes.has("kind")) {
|
|
1707
|
+
String error = jsRes.has("error") ? jsRes.getString("error") : "";
|
|
1690
1708
|
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : "server did not provide a message";
|
|
1691
|
-
|
|
1692
|
-
|
|
1709
|
+
String kind = CapacitorUpdaterPlugin.this.getUpdateResponseKind(jsRes.has("kind") ? jsRes.getString("kind") : null);
|
|
1710
|
+
jsRes.put("kind", kind);
|
|
1711
|
+
if ("failed".equals(kind)) {
|
|
1712
|
+
logger.error("getLatest failed with error: " + error + ", message: " + errorMessage);
|
|
1713
|
+
call.reject(error.isEmpty() ? errorMessage : error);
|
|
1714
|
+
} else {
|
|
1715
|
+
if (!jsRes.has("version") || jsRes.getString("version").isEmpty()) {
|
|
1716
|
+
jsRes.put("version", CapacitorUpdaterPlugin.this.implementation.getCurrentBundle().getVersionName());
|
|
1717
|
+
}
|
|
1718
|
+
logger.info("getLatest returned " + kind + ": " + errorMessage);
|
|
1719
|
+
call.resolve(jsRes);
|
|
1720
|
+
}
|
|
1693
1721
|
return;
|
|
1694
1722
|
} else if (jsRes.has("message")) {
|
|
1695
1723
|
call.reject(jsRes.getString("message"));
|
|
@@ -1774,19 +1802,21 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1774
1802
|
|
|
1775
1803
|
@PluginMethod
|
|
1776
1804
|
public void reset(final PluginCall call) {
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1805
|
+
startNewThread(() -> {
|
|
1806
|
+
try {
|
|
1807
|
+
final Boolean toLastSuccessful = call.getBoolean("toLastSuccessful", false);
|
|
1808
|
+
final Boolean usePendingBundle = call.getBoolean("usePendingBundle", false);
|
|
1809
|
+
if (this._reset(toLastSuccessful, usePendingBundle)) {
|
|
1810
|
+
call.resolve();
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1813
|
+
logger.error("Reset failed");
|
|
1814
|
+
call.reject("Reset failed");
|
|
1815
|
+
} catch (final Exception e) {
|
|
1816
|
+
logger.error("Reset failed " + e.getMessage());
|
|
1817
|
+
call.reject("Reset failed", e);
|
|
1783
1818
|
}
|
|
1784
|
-
|
|
1785
|
-
call.reject("Reset failed");
|
|
1786
|
-
} catch (final Exception e) {
|
|
1787
|
-
logger.error("Reset failed " + e.getMessage());
|
|
1788
|
-
call.reject("Reset failed", e);
|
|
1789
|
-
}
|
|
1819
|
+
});
|
|
1790
1820
|
}
|
|
1791
1821
|
|
|
1792
1822
|
@PluginMethod
|
|
@@ -1852,12 +1882,33 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1852
1882
|
try {
|
|
1853
1883
|
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, null, (res) -> {
|
|
1854
1884
|
JSObject jsRes = InternalUtils.mapToJSObject(res);
|
|
1855
|
-
if (jsRes.has("error")) {
|
|
1856
|
-
|
|
1885
|
+
if (jsRes.has("error") || jsRes.has("kind")) {
|
|
1886
|
+
final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
|
|
1887
|
+
String error = jsRes.has("error") ? jsRes.getString("error") : "";
|
|
1857
1888
|
String errorMessage = jsRes.has("message")
|
|
1858
1889
|
? jsRes.getString("message")
|
|
1859
1890
|
: "server did not provide a message";
|
|
1860
|
-
|
|
1891
|
+
int statusCode = jsRes.has("statusCode") ? jsRes.optInt("statusCode", 0) : 0;
|
|
1892
|
+
String kind = CapacitorUpdaterPlugin.this.getUpdateResponseKind(
|
|
1893
|
+
jsRes.has("kind") ? jsRes.getString("kind") : null
|
|
1894
|
+
);
|
|
1895
|
+
String latestVersion = jsRes.has("version") ? jsRes.getString("version") : current.getVersionName();
|
|
1896
|
+
CapacitorUpdaterPlugin.this.notifyUpdateCheckResult(
|
|
1897
|
+
kind,
|
|
1898
|
+
error,
|
|
1899
|
+
errorMessage,
|
|
1900
|
+
statusCode,
|
|
1901
|
+
latestVersion,
|
|
1902
|
+
current
|
|
1903
|
+
);
|
|
1904
|
+
|
|
1905
|
+
if ("failed".equals(kind)) {
|
|
1906
|
+
logger.error("getLatest failed with error: " + error + ", message: " + errorMessage);
|
|
1907
|
+
} else if ("blocked".equals(kind)) {
|
|
1908
|
+
logger.info("Update check blocked with error: " + error);
|
|
1909
|
+
} else {
|
|
1910
|
+
logger.info("No new version available");
|
|
1911
|
+
}
|
|
1861
1912
|
} else if (jsRes.has("version")) {
|
|
1862
1913
|
String newVersion = jsRes.getString("version");
|
|
1863
1914
|
String currentVersion = String.valueOf(CapacitorUpdaterPlugin.this.implementation.getCurrentBundle());
|
|
@@ -1982,10 +2033,22 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1982
2033
|
this.checkAppReady(this.resolveAppReadyCheckTimeoutMs());
|
|
1983
2034
|
}
|
|
1984
2035
|
|
|
2036
|
+
synchronized boolean shouldInterruptAppReadyCheck(final Thread existingCheck, final Thread currentThread) {
|
|
2037
|
+
return existingCheck != null && existingCheck != currentThread;
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
synchronized void clearAppReadyCheckIfCurrent(final Thread expectedThread) {
|
|
2041
|
+
if (this.appReadyCheck == expectedThread) {
|
|
2042
|
+
this.appReadyCheck = null;
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
|
|
1985
2046
|
private void checkAppReady(final long waitTimeMs) {
|
|
1986
2047
|
try {
|
|
1987
|
-
|
|
1988
|
-
|
|
2048
|
+
final Thread currentThread = Thread.currentThread();
|
|
2049
|
+
final Thread existingCheck = this.appReadyCheck;
|
|
2050
|
+
if (this.shouldInterruptAppReadyCheck(existingCheck, currentThread)) {
|
|
2051
|
+
existingCheck.interrupt();
|
|
1989
2052
|
}
|
|
1990
2053
|
this.appReadyCheck = startNewThread(new DeferredNotifyAppReadyCheck(waitTimeMs));
|
|
1991
2054
|
} catch (final Exception e) {
|
|
@@ -2002,6 +2065,31 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2002
2065
|
}
|
|
2003
2066
|
}
|
|
2004
2067
|
|
|
2068
|
+
private String getUpdateResponseKind(final String kind) {
|
|
2069
|
+
if ("up_to_date".equals(kind) || "blocked".equals(kind) || "failed".equals(kind)) {
|
|
2070
|
+
return kind;
|
|
2071
|
+
}
|
|
2072
|
+
return "failed";
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
private void notifyUpdateCheckResult(
|
|
2076
|
+
final String kind,
|
|
2077
|
+
final String error,
|
|
2078
|
+
final String message,
|
|
2079
|
+
final int statusCode,
|
|
2080
|
+
final String version,
|
|
2081
|
+
final BundleInfo current
|
|
2082
|
+
) {
|
|
2083
|
+
JSObject ret = new JSObject();
|
|
2084
|
+
ret.put("kind", kind);
|
|
2085
|
+
ret.put("error", error);
|
|
2086
|
+
ret.put("message", message);
|
|
2087
|
+
ret.put("statusCode", statusCode);
|
|
2088
|
+
ret.put("version", version);
|
|
2089
|
+
ret.put("bundle", InternalUtils.mapToJSObject(current.toJSONMap()));
|
|
2090
|
+
this.notifyListeners("updateCheckResult", ret);
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2005
2093
|
private void ensureBridgeSet() {
|
|
2006
2094
|
if (this.bridge != null && this.bridge.getWebView() != null) {
|
|
2007
2095
|
logger.setBridge(this.bridge);
|
|
@@ -2111,30 +2199,37 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2111
2199
|
final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
|
|
2112
2200
|
|
|
2113
2201
|
// Handle network errors and other failures first
|
|
2114
|
-
if (jsRes.has("error")) {
|
|
2115
|
-
String error = jsRes.getString("error");
|
|
2202
|
+
if (jsRes.has("error") || jsRes.has("kind")) {
|
|
2203
|
+
String error = jsRes.has("error") ? jsRes.getString("error") : "";
|
|
2116
2204
|
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : "server did not provide a message";
|
|
2117
2205
|
int statusCode = jsRes.has("statusCode") ? jsRes.optInt("statusCode", 0) : 0;
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
logger.error(
|
|
2121
|
-
"getLatest failed with error: " + error + ", message: " + errorMessage + ", statusCode: " + statusCode
|
|
2122
|
-
);
|
|
2206
|
+
String kind = CapacitorUpdaterPlugin.this.getUpdateResponseKind(jsRes.has("kind") ? jsRes.getString("kind") : null);
|
|
2123
2207
|
String latestVersion = jsRes.has("version") ? jsRes.getString("version") : current.getVersionName();
|
|
2208
|
+
CapacitorUpdaterPlugin.this.notifyUpdateCheckResult(kind, error, errorMessage, statusCode, latestVersion, current);
|
|
2124
2209
|
|
|
2210
|
+
if ("up_to_date".equals(kind)) {
|
|
2211
|
+
logger.info("No new version available");
|
|
2212
|
+
} else if ("blocked".equals(kind)) {
|
|
2213
|
+
logger.info("Update check blocked with error: " + error);
|
|
2214
|
+
} else {
|
|
2215
|
+
logger.error(
|
|
2216
|
+
"getLatest failed with error: " + error + ", message: " + errorMessage + ", statusCode: " + statusCode
|
|
2217
|
+
);
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
boolean isFailure = "failed".equals(kind);
|
|
2125
2221
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
2126
2222
|
errorMessage,
|
|
2127
2223
|
latestVersion,
|
|
2128
2224
|
current,
|
|
2129
|
-
|
|
2225
|
+
isFailure,
|
|
2130
2226
|
plannedDirectUpdate,
|
|
2131
2227
|
"download_fail",
|
|
2132
2228
|
"downloadFailed",
|
|
2133
|
-
|
|
2229
|
+
isFailure
|
|
2134
2230
|
);
|
|
2135
2231
|
return;
|
|
2136
2232
|
}
|
|
2137
|
-
|
|
2138
2233
|
try {
|
|
2139
2234
|
final String latestVersionName = jsRes.getString("version");
|
|
2140
2235
|
|
|
@@ -2440,12 +2535,14 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2440
2535
|
|
|
2441
2536
|
@Override
|
|
2442
2537
|
public void run() {
|
|
2538
|
+
final Thread currentThread = Thread.currentThread();
|
|
2443
2539
|
try {
|
|
2444
2540
|
logger.info("Wait for " + this.waitTimeMs + "ms, then check for notifyAppReady");
|
|
2445
2541
|
Thread.sleep(this.waitTimeMs);
|
|
2446
2542
|
CapacitorUpdaterPlugin.this.checkRevert();
|
|
2447
|
-
CapacitorUpdaterPlugin.this.
|
|
2543
|
+
CapacitorUpdaterPlugin.this.clearAppReadyCheckIfCurrent(currentThread);
|
|
2448
2544
|
} catch (final InterruptedException e) {
|
|
2545
|
+
CapacitorUpdaterPlugin.this.clearAppReadyCheckIfCurrent(currentThread);
|
|
2449
2546
|
logger.info(DeferredNotifyAppReadyCheck.class.getName() + " was interrupted.");
|
|
2450
2547
|
}
|
|
2451
2548
|
}
|
|
@@ -1229,6 +1229,7 @@ public class CapgoUpdater {
|
|
|
1229
1229
|
Map<String, Object> retError = new HashMap<>();
|
|
1230
1230
|
retError.put("message", "Request failed: " + e.getMessage());
|
|
1231
1231
|
retError.put("error", "network_error");
|
|
1232
|
+
retError.put("kind", "failed");
|
|
1232
1233
|
callback.callback(retError);
|
|
1233
1234
|
}
|
|
1234
1235
|
|
|
@@ -1236,11 +1237,46 @@ public class CapgoUpdater {
|
|
|
1236
1237
|
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
|
|
1237
1238
|
try (ResponseBody responseBody = response.body()) {
|
|
1238
1239
|
final int statusCode = response.code();
|
|
1240
|
+
final String responseData = responseBody != null ? responseBody.string() : "";
|
|
1241
|
+
JSONObject jsonResponse = null;
|
|
1242
|
+
if (!responseData.isEmpty()) {
|
|
1243
|
+
try {
|
|
1244
|
+
jsonResponse = new JSONObject(responseData);
|
|
1245
|
+
} catch (JSONException ignored) {
|
|
1246
|
+
// Non-JSON responses are handled as response or parse errors below.
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
if (jsonResponse != null && (jsonResponse.has("error") || jsonResponse.has("kind"))) {
|
|
1251
|
+
if (statusCode == 429) {
|
|
1252
|
+
checkAndHandleRateLimitResponse(response);
|
|
1253
|
+
}
|
|
1254
|
+
Map<String, Object> retError = new HashMap<>();
|
|
1255
|
+
if (jsonResponse.has("error") && !jsonResponse.isNull("error")) {
|
|
1256
|
+
retError.put("error", jsonResponse.getString("error"));
|
|
1257
|
+
}
|
|
1258
|
+
if (jsonResponse.has("kind") && !jsonResponse.isNull("kind")) {
|
|
1259
|
+
retError.put("kind", jsonResponse.getString("kind"));
|
|
1260
|
+
}
|
|
1261
|
+
if (jsonResponse.has("message") && !jsonResponse.isNull("message")) {
|
|
1262
|
+
retError.put("message", jsonResponse.getString("message"));
|
|
1263
|
+
} else {
|
|
1264
|
+
retError.put("message", "server did not provide a message");
|
|
1265
|
+
}
|
|
1266
|
+
if (jsonResponse.has("version") && !jsonResponse.isNull("version")) {
|
|
1267
|
+
retError.put("version", jsonResponse.getString("version"));
|
|
1268
|
+
}
|
|
1269
|
+
retError.put("statusCode", statusCode);
|
|
1270
|
+
callback.callback(retError);
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1239
1274
|
// Check for 429 rate limit
|
|
1240
1275
|
if (checkAndHandleRateLimitResponse(response)) {
|
|
1241
1276
|
Map<String, Object> retError = new HashMap<>();
|
|
1242
1277
|
retError.put("message", "Rate limit exceeded");
|
|
1243
1278
|
retError.put("error", "rate_limit_exceeded");
|
|
1279
|
+
retError.put("kind", "failed");
|
|
1244
1280
|
retError.put("statusCode", statusCode);
|
|
1245
1281
|
callback.callback(retError);
|
|
1246
1282
|
return;
|
|
@@ -1250,27 +1286,14 @@ public class CapgoUpdater {
|
|
|
1250
1286
|
Map<String, Object> retError = new HashMap<>();
|
|
1251
1287
|
retError.put("message", "Server error: " + response.code());
|
|
1252
1288
|
retError.put("error", "response_error");
|
|
1289
|
+
retError.put("kind", "failed");
|
|
1253
1290
|
retError.put("statusCode", statusCode);
|
|
1254
1291
|
callback.callback(retError);
|
|
1255
1292
|
return;
|
|
1256
1293
|
}
|
|
1257
1294
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
JSONObject jsonResponse = new JSONObject(responseData);
|
|
1261
|
-
|
|
1262
|
-
// Check for server-side errors first
|
|
1263
|
-
if (jsonResponse.has("error")) {
|
|
1264
|
-
Map<String, Object> retError = new HashMap<>();
|
|
1265
|
-
retError.put("error", jsonResponse.getString("error"));
|
|
1266
|
-
if (jsonResponse.has("message")) {
|
|
1267
|
-
retError.put("message", jsonResponse.getString("message"));
|
|
1268
|
-
} else {
|
|
1269
|
-
retError.put("message", "server did not provide a message");
|
|
1270
|
-
}
|
|
1271
|
-
retError.put("statusCode", statusCode);
|
|
1272
|
-
callback.callback(retError);
|
|
1273
|
-
return;
|
|
1295
|
+
if (jsonResponse == null) {
|
|
1296
|
+
throw new JSONException("Response is not a JSON object");
|
|
1274
1297
|
}
|
|
1275
1298
|
|
|
1276
1299
|
Map<String, Object> ret = new HashMap<>();
|
|
@@ -1292,6 +1315,7 @@ public class CapgoUpdater {
|
|
|
1292
1315
|
Map<String, Object> retError = new HashMap<>();
|
|
1293
1316
|
retError.put("message", "JSON parse error: " + e.getMessage());
|
|
1294
1317
|
retError.put("error", "parse_error");
|
|
1318
|
+
retError.put("kind", "failed");
|
|
1295
1319
|
callback.callback(retError);
|
|
1296
1320
|
}
|
|
1297
1321
|
}
|
|
@@ -448,12 +448,36 @@ public class ShakeMenu implements ShakeDetector.Listener {
|
|
|
448
448
|
}
|
|
449
449
|
|
|
450
450
|
String latestError = getString(latestRes, "error");
|
|
451
|
+
String latestKind = getString(latestRes, "kind");
|
|
452
|
+
String latestMessage = getString(latestRes, "message");
|
|
453
|
+
|
|
454
|
+
String detail = latestMessage != null && !latestMessage.isEmpty()
|
|
455
|
+
? latestMessage
|
|
456
|
+
: latestError != null && !latestError.isEmpty()
|
|
457
|
+
? latestError
|
|
458
|
+
: latestKind != null && !latestKind.isEmpty()
|
|
459
|
+
? latestKind
|
|
460
|
+
: "server did not provide a message";
|
|
451
461
|
|
|
452
462
|
// Handle update errors first (before "no new version" check)
|
|
453
|
-
if (
|
|
463
|
+
if (
|
|
464
|
+
"failed".equals(latestKind) ||
|
|
465
|
+
(latestError != null &&
|
|
466
|
+
!latestError.isEmpty() &&
|
|
467
|
+
!"up_to_date".equals(latestKind) &&
|
|
468
|
+
!"blocked".equals(latestKind))
|
|
469
|
+
) {
|
|
470
|
+
activity.runOnUiThread(() -> {
|
|
471
|
+
progressDialog.dismiss();
|
|
472
|
+
showError("Channel set to " + channelName + ". Update check failed: " + detail);
|
|
473
|
+
});
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if ("blocked".equals(latestKind)) {
|
|
454
478
|
activity.runOnUiThread(() -> {
|
|
455
479
|
progressDialog.dismiss();
|
|
456
|
-
showError("Channel set to " + channelName + ". Update check
|
|
480
|
+
showError("Channel set to " + channelName + ". Update check blocked: " + detail);
|
|
457
481
|
});
|
|
458
482
|
return;
|
|
459
483
|
}
|
|
@@ -461,7 +485,7 @@ public class ShakeMenu implements ShakeDetector.Listener {
|
|
|
461
485
|
String latestUrl = getString(latestRes, "url");
|
|
462
486
|
|
|
463
487
|
// Check if there's an actual update available
|
|
464
|
-
if ("
|
|
488
|
+
if ("up_to_date".equals(latestKind) || latestUrl == null || latestUrl.isEmpty()) {
|
|
465
489
|
activity.runOnUiThread(() -> {
|
|
466
490
|
progressDialog.dismiss();
|
|
467
491
|
showSuccess("Channel set to " + channelName + ". Already on latest version.");
|