@capgo/capacitor-updater 7.8.1 → 7.8.2

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 CHANGED
@@ -225,35 +225,35 @@ Capacitor Updater works by unzipping a compiled app bundle to the native device
225
225
 
226
226
  CapacitorUpdater can be configured with these options:
227
227
 
228
- | Prop | Type | Description | Default | Since |
229
- | ---------------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ------- |
230
- | **`appReadyTimeout`** | <code>number</code> | Configure the number of milliseconds the native plugin should wait before considering an update 'failed'. Only available for Android and iOS. | <code>10000 // (10 seconds)</code> | |
231
- | **`responseTimeout`** | <code>number</code> | Configure the number of milliseconds the native plugin should wait before considering API timeout. Only available for Android and iOS. | <code>20 // (20 second)</code> | |
232
- | **`autoDeleteFailed`** | <code>boolean</code> | Configure whether the plugin should use automatically delete failed bundles. Only available for Android and iOS. | <code>true</code> | |
233
- | **`autoDeletePrevious`** | <code>boolean</code> | Configure whether the plugin should use automatically delete previous bundles after a successful update. Only available for Android and iOS. | <code>true</code> | |
234
- | **`autoUpdate`** | <code>boolean</code> | Configure whether the plugin should use Auto Update via an update server. Only available for Android and iOS. | <code>true</code> | |
235
- | **`resetWhenUpdate`** | <code>boolean</code> | Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device. Only available for Android and iOS. | <code>true</code> | |
236
- | **`updateUrl`** | <code>string</code> | Configure the URL / endpoint to which update checks are sent. Only available for Android and iOS. | <code>https://plugin.capgo.app/updates</code> | |
237
- | **`channelUrl`** | <code>string</code> | Configure the URL / endpoint for channel operations. Only available for Android and iOS. | <code>https://plugin.capgo.app/channel_self</code> | |
238
- | **`statsUrl`** | <code>string</code> | Configure the URL / endpoint to which update statistics are sent. Only available for Android and iOS. Set to "" to disable stats reporting. | <code>https://plugin.capgo.app/stats</code> | |
239
- | **`publicKey`** | <code>string</code> | Configure the public key for end to end live update encryption Version 2 Only available for Android and iOS. | <code>undefined</code> | 6.2.0 |
240
- | **`version`** | <code>string</code> | Configure the current version of the app. This will be used for the first update request. If not set, the plugin will get the version from the native code. Only available for Android and iOS. | <code>undefined</code> | 4.17.48 |
241
- | **`directUpdate`** | <code>boolean \| 'always' \| 'atInstall'</code> | Configure when the plugin should direct install updates. Only for autoUpdate mode. - false: Never do direct updates (default behavior) - atInstall: Direct update only when app is installed/updated from store, otherwise use normal background update - always: Always do direct updates immediately when available - true: (deprecated) Same as "always" for backward compatibility Only available for Android and iOS. | <code>false</code> | 5.1.0 |
242
- | **`autoSplashscreen`** | <code>boolean</code> | Automatically handle splashscreen hiding when using directUpdate. When enabled, the plugin will automatically hide the splashscreen after updates are applied or when no update is needed. This removes the need to manually listen for appReady events and call SplashScreen.hide(). Only works when directUpdate is set to "atInstall", "always", or true. Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false. Only available for Android and iOS. | <code>false</code> | 7.6.0 |
243
- | **`periodCheckDelay`** | <code>number</code> | Configure the delay period for period update check. the unit is in seconds. Only available for Android and iOS. Cannot be less than 600 seconds (10 minutes). | <code>600 // (10 minutes)</code> | |
244
- | **`localS3`** | <code>boolean</code> | Configure the CLI to use a local server for testing or self-hosted update server. | <code>undefined</code> | 4.17.48 |
245
- | **`localHost`** | <code>string</code> | Configure the CLI to use a local server for testing or self-hosted update server. | <code>undefined</code> | 4.17.48 |
246
- | **`localWebHost`** | <code>string</code> | Configure the CLI to use a local server for testing or self-hosted update server. | <code>undefined</code> | 4.17.48 |
247
- | **`localSupa`** | <code>string</code> | Configure the CLI to use a local server for testing or self-hosted update server. | <code>undefined</code> | 4.17.48 |
248
- | **`localSupaAnon`** | <code>string</code> | Configure the CLI to use a local server for testing. | <code>undefined</code> | 4.17.48 |
249
- | **`localApi`** | <code>string</code> | Configure the CLI to use a local api for testing. | <code>undefined</code> | 6.3.3 |
250
- | **`localApiFiles`** | <code>string</code> | Configure the CLI to use a local file api for testing. | <code>undefined</code> | 6.3.3 |
251
- | **`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 |
252
- | **`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. | <code>undefined</code> | 5.5.0 |
253
- | **`appId`** | <code>string</code> | Configure the app id for the app in the config. | <code>undefined</code> | 6.0.0 |
254
- | **`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 |
255
- | **`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 |
256
- | **`shakeMenu`** | <code>boolean</code> | Enable shake gesture to show update menu for debugging/testing purposes | <code>false</code> | 7.5.0 |
228
+ | Prop | Type | Description | Default | Since |
229
+ | ---------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------- | ------- |
230
+ | **`appReadyTimeout`** | <code>number</code> | Configure the number of milliseconds the native plugin should wait before considering an update 'failed'. Only available for Android and iOS. | <code>10000 // (10 seconds)</code> | |
231
+ | **`responseTimeout`** | <code>number</code> | Configure the number of milliseconds the native plugin should wait before considering API timeout. Only available for Android and iOS. | <code>20 // (20 second)</code> | |
232
+ | **`autoDeleteFailed`** | <code>boolean</code> | Configure whether the plugin should use automatically delete failed bundles. Only available for Android and iOS. | <code>true</code> | |
233
+ | **`autoDeletePrevious`** | <code>boolean</code> | Configure whether the plugin should use automatically delete previous bundles after a successful update. Only available for Android and iOS. | <code>true</code> | |
234
+ | **`autoUpdate`** | <code>boolean</code> | Configure whether the plugin should use Auto Update via an update server. Only available for Android and iOS. | <code>true</code> | |
235
+ | **`resetWhenUpdate`** | <code>boolean</code> | Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device. Only available for Android and iOS. | <code>true</code> | |
236
+ | **`updateUrl`** | <code>string</code> | Configure the URL / endpoint to which update checks are sent. Only available for Android and iOS. | <code>https://plugin.capgo.app/updates</code> | |
237
+ | **`channelUrl`** | <code>string</code> | Configure the URL / endpoint for channel operations. Only available for Android and iOS. | <code>https://plugin.capgo.app/channel_self</code> | |
238
+ | **`statsUrl`** | <code>string</code> | Configure the URL / endpoint to which update statistics are sent. Only available for Android and iOS. Set to "" to disable stats reporting. | <code>https://plugin.capgo.app/stats</code> | |
239
+ | **`publicKey`** | <code>string</code> | Configure the public key for end to end live update encryption Version 2 Only available for Android and iOS. | <code>undefined</code> | 6.2.0 |
240
+ | **`version`** | <code>string</code> | Configure the current version of the app. This will be used for the first update request. If not set, the plugin will get the version from the native code. Only available for Android and iOS. | <code>undefined</code> | 4.17.48 |
241
+ | **`directUpdate`** | <code>boolean \| 'always' \| 'atInstall'</code> | Configure when the plugin should direct install updates. Only for autoUpdate mode. - false: Never do direct updates (default behavior) - atInstall: Direct update only when app is installed/updated from store, otherwise use normal background update - always: Always do direct updates immediately when available - true: (deprecated) Same as "always" for backward compatibility Only available for Android and iOS. | <code>false</code> | 5.1.0 |
242
+ | **`autoSplashscreen`** | <code>boolean</code> | Automatically handle splashscreen hiding when using directUpdate. When enabled, the plugin will automatically hide the splashscreen after updates are applied or when no update is needed. This removes the need to manually listen for appReady events and call SplashScreen.hide(). Only works when directUpdate is set to "atInstall", "always", or true. Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false. Requires autoUpdate and directUpdate to be enabled. Only available for Android and iOS. | <code>false</code> | 7.6.0 |
243
+ | **`periodCheckDelay`** | <code>number</code> | Configure the delay period for period update check. the unit is in seconds. Only available for Android and iOS. Cannot be less than 600 seconds (10 minutes). | <code>600 // (10 minutes)</code> | |
244
+ | **`localS3`** | <code>boolean</code> | Configure the CLI to use a local server for testing or self-hosted update server. | <code>undefined</code> | 4.17.48 |
245
+ | **`localHost`** | <code>string</code> | Configure the CLI to use a local server for testing or self-hosted update server. | <code>undefined</code> | 4.17.48 |
246
+ | **`localWebHost`** | <code>string</code> | Configure the CLI to use a local server for testing or self-hosted update server. | <code>undefined</code> | 4.17.48 |
247
+ | **`localSupa`** | <code>string</code> | Configure the CLI to use a local server for testing or self-hosted update server. | <code>undefined</code> | 4.17.48 |
248
+ | **`localSupaAnon`** | <code>string</code> | Configure the CLI to use a local server for testing. | <code>undefined</code> | 4.17.48 |
249
+ | **`localApi`** | <code>string</code> | Configure the CLI to use a local api for testing. | <code>undefined</code> | 6.3.3 |
250
+ | **`localApiFiles`** | <code>string</code> | Configure the CLI to use a local file api for testing. | <code>undefined</code> | 6.3.3 |
251
+ | **`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 |
252
+ | **`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. | <code>undefined</code> | 5.5.0 |
253
+ | **`appId`** | <code>string</code> | Configure the app id for the app in the config. | <code>undefined</code> | 6.0.0 |
254
+ | **`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 |
255
+ | **`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 |
256
+ | **`shakeMenu`** | <code>boolean</code> | Enable shake gesture to show update menu for debugging/testing purposes | <code>false</code> | 7.5.0 |
257
257
 
258
258
  ### Examples
259
259
 
@@ -19,6 +19,7 @@ import com.getcapacitor.JSArray;
19
19
  import com.getcapacitor.JSObject;
20
20
  import com.getcapacitor.Plugin;
21
21
  import com.getcapacitor.PluginCall;
22
+ import com.getcapacitor.PluginHandle;
22
23
  import com.getcapacitor.PluginMethod;
23
24
  import com.getcapacitor.annotation.CapacitorPlugin;
24
25
  import com.getcapacitor.plugin.WebView;
@@ -29,11 +30,9 @@ import java.io.IOException;
29
30
  import java.lang.reflect.Type;
30
31
  import java.net.MalformedURLException;
31
32
  import java.net.URL;
32
- import java.text.SimpleDateFormat;
33
33
  import java.util.ArrayList;
34
34
  import java.util.Arrays;
35
35
  import java.util.Date;
36
- import java.util.Iterator;
37
36
  import java.util.List;
38
37
  import java.util.Map;
39
38
  import java.util.Objects;
@@ -60,7 +59,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
60
59
  private static final String statsUrlDefault = "https://plugin.capgo.app/stats";
61
60
  private static final String channelUrlDefault = "https://plugin.capgo.app/channel_self";
62
61
 
63
- private final String PLUGIN_VERSION = "7.8.1";
62
+ private final String PLUGIN_VERSION = "7.8.2";
64
63
  private static final String DELAY_CONDITION_PREFERENCES = "";
65
64
 
66
65
  private SharedPreferences.Editor editor;
@@ -294,57 +293,98 @@ public class CapacitorUpdaterPlugin extends Plugin {
294
293
  // No need to wait for semaphore anymore since _reload() has already waited
295
294
  this.notifyListeners("appReady", ret);
296
295
 
297
- // Auto hide splashscreen if enabled and this is a direct update
298
- if (this.autoSplashscreen && isDirectUpdate) {
296
+ // Auto hide splashscreen if enabled
297
+ // We show it on background when conditions are met, so we should hide it on foreground regardless of update outcome
298
+ if (this.autoSplashscreen) {
299
299
  this.hideSplashscreen();
300
300
  }
301
301
  }
302
302
 
303
303
  private void hideSplashscreen() {
304
304
  try {
305
- // Use JavaScript evaluation to hide the splashscreen - simpler and more reliable
306
- getBridge()
307
- .getWebView()
308
- .post(() -> {
309
- getBridge()
310
- .eval(
311
- """
312
- (function() {
313
- if (window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.SplashScreen) {
314
- window.Capacitor.Plugins.SplashScreen.hide();
315
- }
316
- })()
317
- """,
318
- null
319
- );
320
- });
321
- logger.info("Splashscreen hidden automatically via JavaScript");
305
+ // Try to call the SplashScreen plugin directly through the bridge
306
+ PluginHandle splashScreenPlugin = getBridge().getPlugin("SplashScreen");
307
+ if (splashScreenPlugin != null) {
308
+ try {
309
+ // Create a plugin call for the hide method using reflection to access private msgHandler
310
+ JSObject options = new JSObject();
311
+ java.lang.reflect.Field msgHandlerField = getBridge().getClass().getDeclaredField("msgHandler");
312
+ msgHandlerField.setAccessible(true);
313
+ Object msgHandler = msgHandlerField.get(getBridge());
314
+
315
+ PluginCall call = new PluginCall(
316
+ (com.getcapacitor.MessageHandler) msgHandler,
317
+ "autoHideSplashscreen",
318
+ PluginCall.CALLBACK_ID_DANGLING,
319
+ "hide",
320
+ options
321
+ );
322
+
323
+ // Call the hide method directly
324
+ splashScreenPlugin.invoke("hide", call);
325
+ logger.info("Splashscreen hidden automatically via direct plugin call");
326
+ } catch (Exception e) {
327
+ logger.error("Failed to call SplashScreen hide method: " + e.getMessage());
328
+ }
329
+ } else {
330
+ logger.warn("autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin.");
331
+ }
322
332
  } catch (Exception e) {
323
- logger.error("Error hiding splashscreen: " + e.getMessage());
333
+ logger.error(
334
+ "Error hiding splashscreen with autoSplashscreen: " +
335
+ e.getMessage() +
336
+ ". Make sure @capacitor/splash-screen plugin is installed and configured."
337
+ );
324
338
  }
325
339
  }
326
340
 
327
341
  private void showSplashscreen() {
328
342
  try {
329
- // Use JavaScript evaluation to show the splashscreen - simpler and more reliable
330
- getBridge()
331
- .getWebView()
332
- .post(() -> {
333
- getBridge()
334
- .eval(
335
- """
336
- (function() {
337
- if (window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.SplashScreen) {
338
- window.Capacitor.Plugins.SplashScreen.show();
339
- }
340
- })()
341
- """,
342
- null
343
- );
344
- });
345
- logger.info("Splashscreen shown automatically via JavaScript");
343
+ // Check if bridge is ready
344
+ if (getBridge() == null) {
345
+ logger.warn("Bridge not ready for showing splashscreen with autoSplashscreen. Retrying in 100ms.");
346
+ new android.os.Handler(android.os.Looper.getMainLooper()).postDelayed(
347
+ () -> {
348
+ showSplashscreen(); // Retry once
349
+ },
350
+ 100
351
+ );
352
+ return;
353
+ }
354
+
355
+ // Try to call the SplashScreen plugin directly through the bridge
356
+ PluginHandle splashScreenPlugin = getBridge().getPlugin("SplashScreen");
357
+ if (splashScreenPlugin != null) {
358
+ try {
359
+ // Create a plugin call for the show method using reflection to access private msgHandler
360
+ JSObject options = new JSObject();
361
+ java.lang.reflect.Field msgHandlerField = getBridge().getClass().getDeclaredField("msgHandler");
362
+ msgHandlerField.setAccessible(true);
363
+ Object msgHandler = msgHandlerField.get(getBridge());
364
+
365
+ PluginCall call = new PluginCall(
366
+ (com.getcapacitor.MessageHandler) msgHandler,
367
+ "autoShowSplashscreen",
368
+ PluginCall.CALLBACK_ID_DANGLING,
369
+ "show",
370
+ options
371
+ );
372
+
373
+ // Call the show method directly
374
+ splashScreenPlugin.invoke("show", call);
375
+ logger.info("Splashscreen shown automatically via direct plugin call");
376
+ } catch (Exception e) {
377
+ logger.error("Failed to call SplashScreen show method: " + e.getMessage());
378
+ }
379
+ } else {
380
+ logger.warn("autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin.");
381
+ }
346
382
  } catch (Exception e) {
347
- logger.error("Error showing splashscreen: " + e.getMessage());
383
+ logger.error(
384
+ "Error showing splashscreen with autoSplashscreen: " +
385
+ e.getMessage() +
386
+ ". Make sure @capacitor/splash-screen plugin is installed and configured."
387
+ );
348
388
  }
349
389
  }
350
390
 
@@ -1176,19 +1216,17 @@ public class CapacitorUpdaterPlugin extends Plugin {
1176
1216
  String messageUpdate = shouldDirectUpdate ? "Update will occur now." : "Update will occur next time app moves to background.";
1177
1217
  return startNewThread(() -> {
1178
1218
  logger.info("Check for update via: " + CapacitorUpdaterPlugin.this.updateUrl);
1179
- CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, null, res -> {
1180
- JSObject jsRes = mapToJSObject(res);
1181
- final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
1182
- try {
1183
- if (jsRes.has("message")) {
1184
- logger.info("API message: " + jsRes.get("message"));
1185
- if (jsRes.has("major") && jsRes.getBoolean("major") && jsRes.has("version")) {
1186
- final JSObject majorAvailable = new JSObject();
1187
- majorAvailable.put("version", jsRes.getString("version"));
1188
- CapacitorUpdaterPlugin.this.notifyListeners("majorAvailable", majorAvailable);
1189
- }
1219
+ try {
1220
+ CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, null, res -> {
1221
+ JSObject jsRes = mapToJSObject(res);
1222
+ final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
1223
+
1224
+ // Handle network errors and other failures first
1225
+ if (jsRes.has("error")) {
1226
+ String error = jsRes.getString("error");
1227
+ logger.error("getLatest failed with error: " + error);
1190
1228
  CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1191
- jsRes.getString("message"),
1229
+ "Network error: " + error,
1192
1230
  current.getVersionName(),
1193
1231
  current,
1194
1232
  true,
@@ -1197,175 +1235,216 @@ public class CapacitorUpdaterPlugin extends Plugin {
1197
1235
  return;
1198
1236
  }
1199
1237
 
1200
- final String latestVersionName = jsRes.getString("version");
1201
-
1202
- if ("builtin".equals(latestVersionName)) {
1203
- logger.info("Latest version is builtin");
1204
- if (shouldDirectUpdate) {
1205
- logger.info("Direct update to builtin version");
1206
- this._reset(false);
1207
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1208
- "Updated to builtin version",
1209
- latestVersionName,
1210
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle(),
1211
- false,
1212
- true
1213
- );
1214
- } else {
1215
- logger.info("Setting next bundle to builtin");
1216
- CapacitorUpdaterPlugin.this.implementation.setNextBundle(BundleInfo.ID_BUILTIN);
1238
+ try {
1239
+ if (jsRes.has("message")) {
1240
+ logger.info("API message: " + jsRes.get("message"));
1241
+ if (jsRes.has("major") && jsRes.getBoolean("major") && jsRes.has("version")) {
1242
+ final JSObject majorAvailable = new JSObject();
1243
+ majorAvailable.put("version", jsRes.getString("version"));
1244
+ CapacitorUpdaterPlugin.this.notifyListeners("majorAvailable", majorAvailable);
1245
+ }
1217
1246
  CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1218
- "Next update will be to builtin version",
1219
- latestVersionName,
1247
+ jsRes.getString("message"),
1248
+ current.getVersionName(),
1220
1249
  current,
1221
- false
1250
+ true,
1251
+ shouldDirectUpdate
1222
1252
  );
1253
+ return;
1223
1254
  }
1224
- return;
1225
- }
1226
1255
 
1227
- if (!jsRes.has("url") || !CapacitorUpdaterPlugin.this.isValidURL(jsRes.getString("url"))) {
1228
- logger.error("Error no url or wrong format");
1229
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1230
- "Error no url or wrong format",
1231
- current.getVersionName(),
1232
- current,
1233
- true,
1234
- shouldDirectUpdate
1235
- );
1236
- return;
1237
- }
1256
+ final String latestVersionName = jsRes.getString("version");
1238
1257
 
1239
- if (
1240
- latestVersionName != null && !latestVersionName.isEmpty() && !current.getVersionName().equals(latestVersionName)
1241
- ) {
1242
- final BundleInfo latest = CapacitorUpdaterPlugin.this.implementation.getBundleInfoByName(latestVersionName);
1243
- if (latest != null) {
1244
- final JSObject ret = new JSObject();
1245
- ret.put("bundle", mapToJSObject(latest.toJSONMap()));
1246
- if (latest.isErrorStatus()) {
1247
- logger.error("Latest bundle already exists, and is in error state. Aborting update.");
1258
+ if ("builtin".equals(latestVersionName)) {
1259
+ logger.info("Latest version is builtin");
1260
+ if (shouldDirectUpdate) {
1261
+ logger.info("Direct update to builtin version");
1262
+ this._reset(false);
1248
1263
  CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1249
- "Latest bundle already exists, and is in error state. Aborting update.",
1264
+ "Updated to builtin version",
1265
+ latestVersionName,
1266
+ CapacitorUpdaterPlugin.this.implementation.getCurrentBundle(),
1267
+ false,
1268
+ true
1269
+ );
1270
+ } else {
1271
+ logger.info("Setting next bundle to builtin");
1272
+ CapacitorUpdaterPlugin.this.implementation.setNextBundle(BundleInfo.ID_BUILTIN);
1273
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1274
+ "Next update will be to builtin version",
1250
1275
  latestVersionName,
1251
1276
  current,
1252
- true,
1253
- shouldDirectUpdate
1277
+ false
1254
1278
  );
1255
- return;
1256
1279
  }
1257
- if (latest.isDownloaded()) {
1258
- logger.info("Latest bundle already exists and download is NOT required. " + messageUpdate);
1259
- if (shouldDirectUpdate) {
1260
- Gson gson = new Gson();
1261
- String delayUpdatePreferences = prefs.getString(DelayUpdateUtils.DELAY_CONDITION_PREFERENCES, "[]");
1262
- Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
1263
- ArrayList<DelayCondition> delayConditionList = gson.fromJson(delayUpdatePreferences, type);
1264
- if (delayConditionList != null && !delayConditionList.isEmpty()) {
1265
- logger.info("Update delayed until delay conditions met");
1280
+ return;
1281
+ }
1282
+
1283
+ if (!jsRes.has("url") || !CapacitorUpdaterPlugin.this.isValidURL(jsRes.getString("url"))) {
1284
+ logger.error("Error no url or wrong format");
1285
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1286
+ "Error no url or wrong format",
1287
+ current.getVersionName(),
1288
+ current,
1289
+ true,
1290
+ shouldDirectUpdate
1291
+ );
1292
+ return;
1293
+ }
1294
+
1295
+ if (
1296
+ latestVersionName != null &&
1297
+ !latestVersionName.isEmpty() &&
1298
+ !current.getVersionName().equals(latestVersionName)
1299
+ ) {
1300
+ final BundleInfo latest = CapacitorUpdaterPlugin.this.implementation.getBundleInfoByName(latestVersionName);
1301
+ if (latest != null) {
1302
+ final JSObject ret = new JSObject();
1303
+ ret.put("bundle", mapToJSObject(latest.toJSONMap()));
1304
+ if (latest.isErrorStatus()) {
1305
+ logger.error("Latest bundle already exists, and is in error state. Aborting update.");
1306
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1307
+ "Latest bundle already exists, and is in error state. Aborting update.",
1308
+ latestVersionName,
1309
+ current,
1310
+ true,
1311
+ shouldDirectUpdate
1312
+ );
1313
+ return;
1314
+ }
1315
+ if (latest.isDownloaded()) {
1316
+ logger.info("Latest bundle already exists and download is NOT required. " + messageUpdate);
1317
+ if (shouldDirectUpdate) {
1318
+ Gson gson = new Gson();
1319
+ String delayUpdatePreferences = prefs.getString(
1320
+ DelayUpdateUtils.DELAY_CONDITION_PREFERENCES,
1321
+ "[]"
1322
+ );
1323
+ Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
1324
+ ArrayList<DelayCondition> delayConditionList = gson.fromJson(delayUpdatePreferences, type);
1325
+ if (delayConditionList != null && !delayConditionList.isEmpty()) {
1326
+ logger.info("Update delayed until delay conditions met");
1327
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1328
+ "Update delayed until delay conditions met",
1329
+ latestVersionName,
1330
+ latest,
1331
+ false,
1332
+ shouldDirectUpdate
1333
+ );
1334
+ return;
1335
+ }
1336
+ CapacitorUpdaterPlugin.this.implementation.set(latest);
1337
+ CapacitorUpdaterPlugin.this._reload();
1266
1338
  CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1267
- "Update delayed until delay conditions met",
1339
+ "Update installed",
1268
1340
  latestVersionName,
1269
1341
  latest,
1270
1342
  false,
1271
- shouldDirectUpdate
1343
+ true
1344
+ );
1345
+ } else {
1346
+ CapacitorUpdaterPlugin.this.notifyListeners("updateAvailable", ret);
1347
+ CapacitorUpdaterPlugin.this.implementation.setNextBundle(latest.getId());
1348
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1349
+ "update downloaded, will install next background",
1350
+ latestVersionName,
1351
+ latest,
1352
+ false
1272
1353
  );
1273
- return;
1274
1354
  }
1275
- CapacitorUpdaterPlugin.this.implementation.set(latest);
1276
- CapacitorUpdaterPlugin.this._reload();
1277
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1278
- "Update installed",
1279
- latestVersionName,
1280
- latest,
1281
- false,
1282
- true
1283
- );
1284
- } else {
1285
- CapacitorUpdaterPlugin.this.notifyListeners("updateAvailable", ret);
1286
- CapacitorUpdaterPlugin.this.implementation.setNextBundle(latest.getId());
1287
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1288
- "update downloaded, will install next background",
1289
- latestVersionName,
1290
- latest,
1291
- false
1292
- );
1355
+ return;
1293
1356
  }
1294
- return;
1295
- }
1296
- if (latest.isDeleted()) {
1297
- logger.info("Latest bundle already exists and will be deleted, download will overwrite it.");
1298
- try {
1299
- final Boolean deleted = CapacitorUpdaterPlugin.this.implementation.delete(latest.getId(), true);
1300
- if (deleted) {
1301
- logger.info("Failed bundle deleted: " + latest.getVersionName());
1357
+ if (latest.isDeleted()) {
1358
+ logger.info("Latest bundle already exists and will be deleted, download will overwrite it.");
1359
+ try {
1360
+ final Boolean deleted = CapacitorUpdaterPlugin.this.implementation.delete(latest.getId(), true);
1361
+ if (deleted) {
1362
+ logger.info("Failed bundle deleted: " + latest.getVersionName());
1363
+ }
1364
+ } catch (final IOException e) {
1365
+ logger.error(
1366
+ "Failed to delete failed bundle: " + latest.getVersionName() + " " + e.getMessage()
1367
+ );
1302
1368
  }
1303
- } catch (final IOException e) {
1304
- logger.error("Failed to delete failed bundle: " + latest.getVersionName() + " " + e.getMessage());
1305
1369
  }
1306
1370
  }
1307
- }
1308
- startNewThread(() -> {
1309
- try {
1310
- logger.info(
1311
- "New bundle: " +
1312
- latestVersionName +
1313
- " found. Current is: " +
1314
- current.getVersionName() +
1315
- ". " +
1316
- messageUpdate
1317
- );
1371
+ startNewThread(() -> {
1372
+ try {
1373
+ logger.info(
1374
+ "New bundle: " +
1375
+ latestVersionName +
1376
+ " found. Current is: " +
1377
+ current.getVersionName() +
1378
+ ". " +
1379
+ messageUpdate
1380
+ );
1318
1381
 
1319
- final String url = jsRes.getString("url");
1320
- final String sessionKey = jsRes.has("sessionKey") ? jsRes.getString("sessionKey") : "";
1321
- final String checksum = jsRes.has("checksum") ? jsRes.getString("checksum") : "";
1382
+ final String url = jsRes.getString("url");
1383
+ final String sessionKey = jsRes.has("sessionKey") ? jsRes.getString("sessionKey") : "";
1384
+ final String checksum = jsRes.has("checksum") ? jsRes.getString("checksum") : "";
1322
1385
 
1323
- if (jsRes.has("manifest")) {
1324
- // Handle manifest-based download
1325
- JSONArray manifest = jsRes.getJSONArray("manifest");
1326
- CapacitorUpdaterPlugin.this.implementation.downloadBackground(
1327
- url,
1328
- latestVersionName,
1329
- sessionKey,
1330
- checksum,
1331
- manifest
1332
- );
1333
- } else {
1334
- // Handle single file download (existing code)
1335
- CapacitorUpdaterPlugin.this.implementation.downloadBackground(
1336
- url,
1386
+ if (jsRes.has("manifest")) {
1387
+ // Handle manifest-based download
1388
+ JSONArray manifest = jsRes.getJSONArray("manifest");
1389
+ CapacitorUpdaterPlugin.this.implementation.downloadBackground(
1390
+ url,
1391
+ latestVersionName,
1392
+ sessionKey,
1393
+ checksum,
1394
+ manifest
1395
+ );
1396
+ } else {
1397
+ // Handle single file download (existing code)
1398
+ CapacitorUpdaterPlugin.this.implementation.downloadBackground(
1399
+ url,
1400
+ latestVersionName,
1401
+ sessionKey,
1402
+ checksum,
1403
+ null
1404
+ );
1405
+ }
1406
+ } catch (final Exception e) {
1407
+ logger.error("error downloading file " + e.getMessage());
1408
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1409
+ "Error downloading file",
1337
1410
  latestVersionName,
1338
- sessionKey,
1339
- checksum,
1340
- null
1411
+ CapacitorUpdaterPlugin.this.implementation.getCurrentBundle(),
1412
+ true,
1413
+ shouldDirectUpdate
1341
1414
  );
1342
1415
  }
1343
- } catch (final Exception e) {
1344
- logger.error("error downloading file " + e.getMessage());
1345
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1346
- "Error downloading file",
1347
- latestVersionName,
1348
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle(),
1349
- true,
1350
- shouldDirectUpdate
1351
- );
1352
- }
1353
- });
1354
- } else {
1355
- logger.info("No need to update, " + current.getId() + " is the latest bundle.");
1356
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif("No need to update", latestVersionName, current, false);
1416
+ });
1417
+ } else {
1418
+ logger.info("No need to update, " + current.getId() + " is the latest bundle.");
1419
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1420
+ "No need to update",
1421
+ latestVersionName,
1422
+ current,
1423
+ false
1424
+ );
1425
+ }
1426
+ } catch (final JSONException e) {
1427
+ logger.error("error parsing JSON " + e.getMessage());
1428
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1429
+ "Error parsing JSON",
1430
+ current.getVersionName(),
1431
+ current,
1432
+ true,
1433
+ shouldDirectUpdate
1434
+ );
1357
1435
  }
1358
- } catch (final JSONException e) {
1359
- logger.error("error parsing JSON " + e.getMessage());
1360
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1361
- "Error parsing JSON",
1362
- current.getVersionName(),
1363
- current,
1364
- true,
1365
- shouldDirectUpdate
1366
- );
1367
- }
1368
- });
1436
+ });
1437
+ } catch (final Exception e) {
1438
+ logger.error("getLatest call failed: " + e.getMessage());
1439
+ final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
1440
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1441
+ "Network connection failed",
1442
+ current.getVersionName(),
1443
+ current,
1444
+ true,
1445
+ shouldDirectUpdate
1446
+ );
1447
+ }
1369
1448
  });
1370
1449
  }
1371
1450
 
@@ -1469,9 +1548,27 @@ public class CapacitorUpdaterPlugin extends Plugin {
1469
1548
  CapacitorUpdaterPlugin.this.implementation.sendStats("app_moved_to_background", current.getVersionName());
1470
1549
  logger.info("Checking for pending update");
1471
1550
 
1472
- // Show splashscreen if autoSplashscreen is enabled
1551
+ // Show splashscreen only if autoSplashscreen is enabled AND autoUpdate is enabled AND directUpdate would be used
1473
1552
  if (this.autoSplashscreen) {
1474
- this.showSplashscreen();
1553
+ boolean canShowSplashscreen = true;
1554
+
1555
+ if (!this._isAutoUpdateEnabled()) {
1556
+ logger.warn(
1557
+ "autoSplashscreen is enabled but autoUpdate is disabled. Splashscreen will not be shown. Enable autoUpdate or disable autoSplashscreen."
1558
+ );
1559
+ canShowSplashscreen = false;
1560
+ }
1561
+
1562
+ if (!this.shouldUseDirectUpdate()) {
1563
+ logger.warn(
1564
+ "autoSplashscreen is enabled but directUpdate is not configured for immediate updates. Set directUpdate to 'always' or 'atInstall', or disable autoSplashscreen."
1565
+ );
1566
+ canShowSplashscreen = false;
1567
+ }
1568
+
1569
+ if (canShowSplashscreen) {
1570
+ this.showSplashscreen();
1571
+ }
1475
1572
  }
1476
1573
 
1477
1574
  try {
package/dist/docs.json CHANGED
@@ -2356,7 +2356,7 @@
2356
2356
  "name": "since"
2357
2357
  }
2358
2358
  ],
2359
- "docs": "Automatically handle splashscreen hiding when using directUpdate. When enabled, the plugin will automatically hide the splashscreen after updates are applied or when no update is needed.\nThis removes the need to manually listen for appReady events and call SplashScreen.hide().\nOnly works when directUpdate is set to \"atInstall\", \"always\", or true.\nRequires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false.\n\nOnly available for Android and iOS.",
2359
+ "docs": "Automatically handle splashscreen hiding when using directUpdate. When enabled, the plugin will automatically hide the splashscreen after updates are applied or when no update is needed.\nThis removes the need to manually listen for appReady events and call SplashScreen.hide().\nOnly works when directUpdate is set to \"atInstall\", \"always\", or true.\nRequires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false.\nRequires autoUpdate and directUpdate to be enabled.\n\nOnly available for Android and iOS.",
2360
2360
  "complexTypes": [],
2361
2361
  "type": "boolean | undefined"
2362
2362
  },
@@ -123,6 +123,7 @@ declare module '@capacitor/cli' {
123
123
  * This removes the need to manually listen for appReady events and call SplashScreen.hide().
124
124
  * Only works when directUpdate is set to "atInstall", "always", or true.
125
125
  * Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false.
126
+ * Requires autoUpdate and directUpdate to be enabled.
126
127
  *
127
128
  * Only available for Android and iOS.
128
129
  *
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA;;;;GAIG","sourcesContent":["/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n/// <reference types=\"@capacitor/cli\" />\n\nimport type { PluginListenerHandle } from '@capacitor/core';\n\ndeclare module '@capacitor/cli' {\n export interface PluginsConfig {\n /**\n * CapacitorUpdater can be configured with these options:\n */\n CapacitorUpdater?: {\n /**\n * Configure the number of milliseconds the native plugin should wait before considering an update 'failed'.\n *\n * Only available for Android and iOS.\n *\n * @default 10000 // (10 seconds)\n * @example 1000 // (1 second)\n */\n appReadyTimeout?: number;\n /**\n * Configure the number of milliseconds the native plugin should wait before considering API timeout.\n *\n * Only available for Android and iOS.\n *\n * @default 20 // (20 second)\n * @example 10 // (10 second)\n */\n responseTimeout?: number;\n /**\n * Configure whether the plugin should use automatically delete failed bundles.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n autoDeleteFailed?: boolean;\n\n /**\n * Configure whether the plugin should use automatically delete previous bundles after a successful update.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n autoDeletePrevious?: boolean;\n\n /**\n * Configure whether the plugin should use Auto Update via an update server.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n autoUpdate?: boolean;\n\n /**\n * Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n resetWhenUpdate?: boolean;\n\n /**\n * Configure the URL / endpoint to which update checks are sent.\n *\n * Only available for Android and iOS.\n *\n * @default https://plugin.capgo.app/updates\n * @example https://example.com/api/auto_update\n */\n updateUrl?: string;\n\n /**\n * Configure the URL / endpoint for channel operations.\n *\n * Only available for Android and iOS.\n *\n * @default https://plugin.capgo.app/channel_self\n * @example https://example.com/api/channel\n */\n channelUrl?: string;\n\n /**\n * Configure the URL / endpoint to which update statistics are sent.\n *\n * Only available for Android and iOS. Set to \"\" to disable stats reporting.\n *\n * @default https://plugin.capgo.app/stats\n * @example https://example.com/api/stats\n */\n statsUrl?: string;\n /**\n * Configure the public key for end to end live update encryption Version 2\n *\n * Only available for Android and iOS.\n *\n * @default undefined\n * @since 6.2.0\n */\n publicKey?: string;\n\n /**\n * Configure the current version of the app. This will be used for the first update request.\n * If not set, the plugin will get the version from the native code.\n *\n * Only available for Android and iOS.\n *\n * @default undefined\n * @since 4.17.48\n */\n version?: string;\n /**\n * Configure when the plugin should direct install updates. Only for autoUpdate mode.\n * - false: Never do direct updates (default behavior)\n * - atInstall: Direct update only when app is installed/updated from store, otherwise use normal background update\n * - always: Always do direct updates immediately when available\n * - true: (deprecated) Same as \"always\" for backward compatibility\n *\n * Only available for Android and iOS.\n *\n * @default false\n * @since 5.1.0\n */\n directUpdate?: boolean | 'atInstall' | 'always';\n\n /**\n * Automatically handle splashscreen hiding when using directUpdate. When enabled, the plugin will automatically hide the splashscreen after updates are applied or when no update is needed.\n * This removes the need to manually listen for appReady events and call SplashScreen.hide().\n * Only works when directUpdate is set to \"atInstall\", \"always\", or true.\n * Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false.\n *\n * Only available for Android and iOS.\n *\n * @default false\n * @since 7.6.0\n */\n autoSplashscreen?: boolean;\n\n /**\n * Configure the delay period for period update check. the unit is in seconds.\n *\n * Only available for Android and iOS.\n * Cannot be less than 600 seconds (10 minutes).\n *\n * @default 600 // (10 minutes)\n */\n periodCheckDelay?: number;\n\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localS3?: boolean;\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localHost?: string;\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localWebHost?: string;\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localSupa?: string;\n /**\n * Configure the CLI to use a local server for testing.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localSupaAnon?: string;\n /**\n * Configure the CLI to use a local api for testing.\n *\n *\n * @default undefined\n * @since 6.3.3\n */\n localApi?: string;\n /**\n * Configure the CLI to use a local file api for testing.\n *\n *\n * @default undefined\n * @since 6.3.3\n */\n localApiFiles?: string;\n /**\n * Allow the plugin to modify the updateUrl, statsUrl and channelUrl dynamically from the JavaScript side.\n *\n *\n * @default false\n * @since 5.4.0\n */\n allowModifyUrl?: boolean;\n\n /**\n * Set the default channel for the app in the config. Case sensitive.\n * This will setting will override the default channel set in the cloud, but will still respect overrides made in the cloud.\n *\n *\n * @default undefined\n * @since 5.5.0\n */\n defaultChannel?: string;\n /**\n * Configure the app id for the app in the config.\n *\n * @default undefined\n * @since 6.0.0\n */\n appId?: string;\n\n /**\n * Configure the plugin to keep the URL path after a reload.\n * WARNING: When a reload is triggered, 'window.history' will be cleared.\n *\n * @default false\n * @since 6.8.0\n */\n keepUrlPathAfterReload?: boolean;\n /**\n * 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\n *\n * @default false\n * @since 7.3.0\n */\n disableJSLogging?: boolean;\n /**\n * Enable shake gesture to show update menu for debugging/testing purposes\n *\n * @default false\n * @since 7.5.0\n */\n shakeMenu?: boolean;\n };\n }\n}\n\nexport interface CapacitorUpdaterPlugin {\n /**\n * Notify Capacitor Updater that the current bundle is working (a rollback will occur if this method is not called on every app launch)\n * By default this method should be called in the first 10 sec after app launch, otherwise a rollback will occur.\n * Change this behaviour with {@link appReadyTimeout}\n *\n * @returns {Promise<AppReadyResult>} an Promise resolved directly\n * @throws {Error}\n */\n notifyAppReady(): Promise<AppReadyResult>;\n\n /**\n * Set the updateUrl for the app, this will be used to check for updates.\n *\n * @param options contains the URL to use for checking for updates.\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 5.4.0\n */\n setUpdateUrl(options: UpdateUrl): Promise<void>;\n\n /**\n * Set the statsUrl for the app, this will be used to send statistics. Passing an empty string will disable statistics gathering.\n *\n * @param options contains the URL to use for sending statistics.\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 5.4.0\n */\n setStatsUrl(options: StatsUrl): Promise<void>;\n\n /**\n * Set the channelUrl for the app, this will be used to set the channel.\n *\n * @param options contains the URL to use for setting the channel.\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 5.4.0\n */\n setChannelUrl(options: ChannelUrl): Promise<void>;\n\n /**\n * Download a new bundle from the provided URL, it should be a zip file, with files inside or with a unique id inside with all your files\n *\n * @example const bundle = await CapacitorUpdater.download({ url: `https://example.com/versions/${version}/dist.zip`, version });\n * @returns {Promise<BundleInfo>} The {@link BundleInfo} for the specified bundle.\n * @param options The {@link DownloadOptions} for downloading a new bundle zip.\n */\n download(options: DownloadOptions): Promise<BundleInfo>;\n\n /**\n * Set the next bundle to be used when the app is reloaded.\n *\n * @param options Contains the ID of the next Bundle to set on next app launch. {@link BundleInfo.id}\n * @returns {Promise<BundleInfo>} The {@link BundleInfo} for the specified bundle id.\n * @throws {Error} When there is no index.html file inside the bundle folder.\n */\n next(options: BundleId): Promise<BundleInfo>;\n\n /**\n * Set the current bundle and immediately reloads the app.\n *\n * @param options A {@link BundleId} object containing the new bundle id to set as current.\n * @returns {Promise<void>}\n * @throws {Error} When there are is no index.html file inside the bundle folder.\n */\n set(options: BundleId): Promise<void>;\n\n /**\n * Deletes the specified bundle from the native app storage. Use with {@link list} to get the stored Bundle IDs.\n *\n * @param options A {@link BundleId} object containing the ID of a bundle to delete (note, this is the bundle id, NOT the version name)\n * @returns {Promise<void>} When the bundle is deleted\n * @throws {Error}\n */\n delete(options: BundleId): Promise<void>;\n\n /**\n * Get all locally downloaded bundles in your app\n *\n * @returns {Promise<BundleListResult>} A Promise containing the {@link BundleListResult.bundles}\n * @param options The {@link ListOptions} for listing bundles\n * @throws {Error}\n */\n list(options?: ListOptions): Promise<BundleListResult>;\n\n /**\n * Reset the app to the `builtin` bundle (the one sent to Apple App Store / Google Play Store ) or the last successfully loaded bundle.\n *\n * @param options Containing {@link ResetOptions.toLastSuccessful}, `true` resets to the builtin bundle and `false` will reset to the last successfully loaded bundle.\n * @returns {Promise<void>}\n * @throws {Error}\n */\n reset(options?: ResetOptions): Promise<void>;\n\n /**\n * Get the current bundle, if none are set it returns `builtin`. currentNative is the original bundle installed on the device\n *\n * @returns {Promise<CurrentBundleResult>} A Promise evaluating to the {@link CurrentBundleResult}\n * @throws {Error}\n */\n current(): Promise<CurrentBundleResult>;\n\n /**\n * Reload the view\n *\n * @returns {Promise<void>} A Promise which is resolved when the view is reloaded\n * @throws {Error}\n */\n reload(): Promise<void>;\n\n /**\n * Sets a {@link DelayCondition} array containing conditions that the Plugin will use to delay the update.\n * After all conditions are met, the update process will run start again as usual, so update will be installed after a backgrounding or killing the app.\n * For the `date` kind, the value should be an iso8601 date string.\n * For the `background` kind, the value should be a number in milliseconds.\n * For the `nativeVersion` kind, the value should be the version number.\n * For the `kill` kind, the value is not used.\n * The function has unconsistent behavior the option kill do trigger the update after the first kill and not after the next background like other options. This will be fixed in a future major release.\n *\n * @example\n * // Delay the update after the user kills the app or after a background of 300000 ms (5 minutes)\n * await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'kill' }, { kind: 'background', value: '300000' }] })\n * @example\n * // Delay the update after the specific iso8601 date is expired\n * await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'date', value: '2022-09-14T06:14:11.920Z' }] })\n * @example\n * // Delay the update after the first background (default behaviour without setting delay)\n * await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'background' }] })\n * @param options Containing the {@link MultiDelayConditions} array of conditions to set\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 4.3.0\n */\n setMultiDelay(options: MultiDelayConditions): Promise<void>;\n\n /**\n * Cancels a {@link DelayCondition} to process an update immediately.\n *\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 4.0.0\n */\n cancelDelay(): Promise<void>;\n\n /**\n * Get Latest bundle available from update Url\n *\n * @returns {Promise<LatestVersion>} A Promise resolved when url is loaded\n * @throws {Error}\n * @since 4.0.0\n */\n getLatest(options?: GetLatestOptions): Promise<LatestVersion>;\n\n /**\n * Sets the channel for this device. The channel has to allow for self assignment for this to work.\n * Do not use this method to set the channel at boot.\n * This method is to set the channel after the app is ready, and user interacted.\n * If you want to set the channel at boot, use the {@link PluginsConfig} to set the default channel.\n * This methods send to Capgo backend a request to link the device ID to the channel. Capgo can accept or refuse depending of the setting of your channel.\n *\n *\n *\n * @param options Is the {@link SetChannelOptions} channel to set\n * @returns {Promise<ChannelRes>} A Promise which is resolved when the new channel is set\n * @throws {Error}\n * @since 4.7.0\n */\n setChannel(options: SetChannelOptions): Promise<ChannelRes>;\n\n /**\n * Unset the channel for this device. The device will then return to the default channel\n *\n * @returns {Promise<ChannelRes>} A Promise resolved when channel is set\n * @throws {Error}\n * @since 4.7.0\n */\n unsetChannel(options: UnsetChannelOptions): Promise<void>;\n\n /**\n * Get the channel for this device\n *\n * @returns {Promise<ChannelRes>} A Promise that resolves with the channel info\n * @throws {Error}\n * @since 4.8.0\n */\n getChannel(): Promise<GetChannelRes>;\n\n /**\n * List all channels available for this device that allow self-assignment\n *\n * @returns {Promise<ListChannelsResult>} A Promise that resolves with the available channels\n * @throws {Error}\n * @since 7.5.0\n */\n listChannels(): Promise<ListChannelsResult>;\n\n /**\n * Set a custom ID for this device\n *\n * @param options is the {@link SetCustomIdOptions} customId to set\n * @returns {Promise<void>} an Promise resolved instantly\n * @throws {Error}\n * @since 4.9.0\n */\n setCustomId(options: SetCustomIdOptions): Promise<void>;\n\n /**\n * Get the native app version or the builtin version if set in config\n *\n * @returns {Promise<BuiltinVersion>} A Promise with version for this device\n * @since 5.2.0\n */\n getBuiltinVersion(): Promise<BuiltinVersion>;\n\n /**\n * Get unique ID used to identify device (sent to auto update server)\n *\n * @returns {Promise<DeviceId>} A Promise with id for this device\n * @throws {Error}\n */\n getDeviceId(): Promise<DeviceId>;\n\n /**\n * Get the native Capacitor Updater plugin version (sent to auto update server)\n *\n * @returns {Promise<PluginVersion>} A Promise with Plugin version\n * @throws {Error}\n */\n getPluginVersion(): Promise<PluginVersion>;\n\n /**\n * Get the state of auto update config.\n *\n * @returns {Promise<AutoUpdateEnabled>} The status for auto update. Evaluates to `false` in manual mode.\n * @throws {Error}\n */\n isAutoUpdateEnabled(): Promise<AutoUpdateEnabled>;\n\n /**\n * Remove all listeners for this plugin.\n *\n * @since 1.0.0\n */\n removeAllListeners(): Promise<void>;\n\n /**\n * Listen for bundle download event in the App. Fires once a download has started, during downloading and when finished.\n * This will return you all download percent during the download\n *\n * @since 2.0.11\n */\n addListener(eventName: 'download', listenerFunc: (state: DownloadEvent) => void): Promise<PluginListenerHandle>;\n\n /**\n * Listen for no need to update event, useful when you want force check every time the app is launched\n *\n * @since 4.0.0\n */\n addListener(eventName: 'noNeedUpdate', listenerFunc: (state: NoNeedEvent) => void): Promise<PluginListenerHandle>;\n\n /**\n * Listen for available update event, useful when you want to force check every time the app is launched\n *\n * @since 4.0.0\n */\n addListener(\n eventName: 'updateAvailable',\n listenerFunc: (state: UpdateAvailableEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for downloadComplete events.\n *\n * @since 4.0.0\n */\n addListener(\n eventName: 'downloadComplete',\n listenerFunc: (state: DownloadCompleteEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for Major update event in the App, let you know when major update is blocked by setting disableAutoUpdateBreaking\n *\n * @since 2.3.0\n */\n addListener(\n eventName: 'majorAvailable',\n listenerFunc: (state: MajorAvailableEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for update fail event in the App, let you know when update has fail to install at next app start\n *\n * @since 2.3.0\n */\n addListener(\n eventName: 'updateFailed',\n listenerFunc: (state: UpdateFailedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for download fail event in the App, let you know when a bundle download has failed\n *\n * @since 4.0.0\n */\n addListener(\n eventName: 'downloadFailed',\n listenerFunc: (state: DownloadFailedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for reload event in the App, let you know when reload has happened\n *\n * @since 4.3.0\n */\n addListener(eventName: 'appReloaded', listenerFunc: () => void): Promise<PluginListenerHandle>;\n\n /**\n * Listen for app ready event in the App, let you know when app is ready to use\n *\n * @since 5.1.0\n */\n addListener(eventName: 'appReady', listenerFunc: (state: AppReadyEvent) => void): Promise<PluginListenerHandle>;\n\n /**\n * Get if auto update is available (not disabled by serverUrl).\n *\n * @returns {Promise<AutoUpdateAvailable>} The availability status for auto update. Evaluates to `false` when serverUrl is set.\n * @throws {Error}\n */\n isAutoUpdateAvailable(): Promise<AutoUpdateAvailable>;\n\n /**\n * Get the next bundle that will be used when the app reloads.\n * Returns null if no next bundle is set.\n *\n * @returns {Promise<BundleInfo | null>} A Promise that resolves with the next bundle information or null\n * @throws {Error}\n * @since 6.8.0\n */\n getNextBundle(): Promise<BundleInfo | null>;\n\n /**\n * Enable or disable the shake menu for debugging/testing purposes\n *\n * @param options Contains enabled boolean to enable or disable shake menu\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 7.5.0\n */\n setShakeMenu(options: SetShakeMenuOptions): Promise<void>;\n\n /**\n * Get the current state of the shake menu\n *\n * @returns {Promise<ShakeMenuEnabled>} The current state of shake menu\n * @throws {Error}\n * @since 7.5.0\n */\n isShakeMenuEnabled(): Promise<ShakeMenuEnabled>;\n}\n\n/**\n * pending: The bundle is pending to be **SET** as the next bundle.\n * downloading: The bundle is being downloaded.\n * success: The bundle has been downloaded and is ready to be **SET** as the next bundle.\n * error: The bundle has failed to download.\n */\nexport type BundleStatus = 'success' | 'error' | 'pending' | 'downloading';\n\nexport type DelayUntilNext = 'background' | 'kill' | 'nativeVersion' | 'date';\n\nexport interface NoNeedEvent {\n /**\n * Current status of download, between 0 and 100.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface UpdateAvailableEvent {\n /**\n * Current status of download, between 0 and 100.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface ChannelRes {\n /**\n * Current status of set channel\n *\n * @since 4.7.0\n */\n status: string;\n error?: string;\n message?: string;\n}\n\nexport interface GetChannelRes {\n /**\n * Current status of get channel\n *\n * @since 4.8.0\n */\n channel?: string;\n error?: string;\n message?: string;\n status?: string;\n allowSet?: boolean;\n}\n\nexport interface ChannelInfo {\n /**\n * The channel ID\n *\n * @since 7.5.0\n */\n id: string;\n /**\n * The channel name\n *\n * @since 7.5.0\n */\n name: string;\n /**\n * Whether this is a public channel\n *\n * @since 7.5.0\n */\n public: boolean;\n /**\n * Whether devices can self-assign to this channel\n *\n * @since 7.5.0\n */\n allow_self_set: boolean;\n}\n\nexport interface ListChannelsResult {\n /**\n * List of available channels\n *\n * @since 7.5.0\n */\n channels: ChannelInfo[];\n}\n\nexport interface DownloadEvent {\n /**\n * Current status of download, between 0 and 100.\n *\n * @since 4.0.0\n */\n percent: number;\n bundle: BundleInfo;\n}\n\nexport interface MajorAvailableEvent {\n /**\n * Emit when a new major bundle is available.\n *\n * @since 4.0.0\n */\n version: string;\n}\n\nexport interface DownloadFailedEvent {\n /**\n * Emit when a download fail.\n *\n * @since 4.0.0\n */\n version: string;\n}\n\nexport interface DownloadCompleteEvent {\n /**\n * Emit when a new update is available.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface UpdateFailedEvent {\n /**\n * Emit when a update failed to install.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface AppReadyEvent {\n /**\n * Emitted when the app is ready to use.\n *\n * @since 5.2.0\n */\n bundle: BundleInfo;\n status: string;\n}\n\nexport interface ManifestEntry {\n file_name: string | null;\n file_hash: string | null;\n download_url: string | null;\n}\n\nexport interface LatestVersion {\n /**\n * Result of getLatest method\n *\n * @since 4.0.0\n */\n version: string;\n /**\n * @since 6\n */\n checksum?: string;\n major?: boolean;\n message?: string;\n sessionKey?: string;\n error?: string;\n old?: string;\n url?: string;\n /**\n * @since 6.1\n */\n manifest?: ManifestEntry[];\n}\n\nexport interface BundleInfo {\n id: string;\n version: string;\n downloaded: string;\n checksum: string;\n status: BundleStatus;\n}\n\nexport interface SetChannelOptions {\n channel: string;\n triggerAutoUpdate?: boolean;\n}\n\nexport interface UnsetChannelOptions {\n triggerAutoUpdate?: boolean;\n}\n\nexport interface SetCustomIdOptions {\n customId: string;\n}\n\nexport interface DelayCondition {\n /**\n * Set up delay conditions in setMultiDelay\n * @param value is useless for @param kind \"kill\", optional for \"background\" (default value: \"0\") and required for \"nativeVersion\" and \"date\"\n */\n kind: DelayUntilNext;\n value?: string;\n}\n\nexport interface GetLatestOptions {\n /**\n * The channel to get the latest version for\n * The channel must allow 'self_assign' for this to work\n * @since 6.8.0\n * @default undefined\n */\n channel?: string;\n}\n\nexport interface AppReadyResult {\n bundle: BundleInfo;\n}\n\nexport interface UpdateUrl {\n url: string;\n}\n\nexport interface StatsUrl {\n url: string;\n}\n\nexport interface ChannelUrl {\n url: string;\n}\n\n/**\n * 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.\n * 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.\n */\nexport interface DownloadOptions {\n /**\n * 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.)\n */\n url: string;\n /**\n * The version code/name of this bundle/version\n */\n version: string;\n /**\n * The session key for the update, when the bundle is encrypted with a session key\n * @since 4.0.0\n * @default undefined\n */\n sessionKey?: string;\n /**\n * The checksum for the update, it should be in sha256 and encrypted with private key if the bundle is encrypted\n * @since 4.0.0\n * @default undefined\n */\n checksum?: string;\n /**\n * The manifest for multi-file downloads\n * @since 6.1.0\n * @default undefined\n */\n manifest?: ManifestEntry[];\n}\n\nexport interface BundleId {\n id: string;\n}\n\nexport interface BundleListResult {\n bundles: BundleInfo[];\n}\n\nexport interface ResetOptions {\n toLastSuccessful: boolean;\n}\n\nexport interface ListOptions {\n /**\n * Whether to return the raw bundle list or the manifest. If true, the list will attempt to read the internal database instead of files on disk.\n * @since 6.14.0\n * @default false\n */\n raw?: boolean;\n}\n\nexport interface CurrentBundleResult {\n bundle: BundleInfo;\n native: string;\n}\n\nexport interface MultiDelayConditions {\n delayConditions: DelayCondition[];\n}\n\nexport interface BuiltinVersion {\n version: string;\n}\n\nexport interface DeviceId {\n deviceId: string;\n}\n\nexport interface PluginVersion {\n version: string;\n}\n\nexport interface AutoUpdateEnabled {\n enabled: boolean;\n}\n\nexport interface AutoUpdateAvailable {\n available: boolean;\n}\n\nexport interface SetShakeMenuOptions {\n enabled: boolean;\n}\n\nexport interface ShakeMenuEnabled {\n enabled: boolean;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA;;;;GAIG","sourcesContent":["/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n/// <reference types=\"@capacitor/cli\" />\n\nimport type { PluginListenerHandle } from '@capacitor/core';\n\ndeclare module '@capacitor/cli' {\n export interface PluginsConfig {\n /**\n * CapacitorUpdater can be configured with these options:\n */\n CapacitorUpdater?: {\n /**\n * Configure the number of milliseconds the native plugin should wait before considering an update 'failed'.\n *\n * Only available for Android and iOS.\n *\n * @default 10000 // (10 seconds)\n * @example 1000 // (1 second)\n */\n appReadyTimeout?: number;\n /**\n * Configure the number of milliseconds the native plugin should wait before considering API timeout.\n *\n * Only available for Android and iOS.\n *\n * @default 20 // (20 second)\n * @example 10 // (10 second)\n */\n responseTimeout?: number;\n /**\n * Configure whether the plugin should use automatically delete failed bundles.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n autoDeleteFailed?: boolean;\n\n /**\n * Configure whether the plugin should use automatically delete previous bundles after a successful update.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n autoDeletePrevious?: boolean;\n\n /**\n * Configure whether the plugin should use Auto Update via an update server.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n autoUpdate?: boolean;\n\n /**\n * Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n resetWhenUpdate?: boolean;\n\n /**\n * Configure the URL / endpoint to which update checks are sent.\n *\n * Only available for Android and iOS.\n *\n * @default https://plugin.capgo.app/updates\n * @example https://example.com/api/auto_update\n */\n updateUrl?: string;\n\n /**\n * Configure the URL / endpoint for channel operations.\n *\n * Only available for Android and iOS.\n *\n * @default https://plugin.capgo.app/channel_self\n * @example https://example.com/api/channel\n */\n channelUrl?: string;\n\n /**\n * Configure the URL / endpoint to which update statistics are sent.\n *\n * Only available for Android and iOS. Set to \"\" to disable stats reporting.\n *\n * @default https://plugin.capgo.app/stats\n * @example https://example.com/api/stats\n */\n statsUrl?: string;\n /**\n * Configure the public key for end to end live update encryption Version 2\n *\n * Only available for Android and iOS.\n *\n * @default undefined\n * @since 6.2.0\n */\n publicKey?: string;\n\n /**\n * Configure the current version of the app. This will be used for the first update request.\n * If not set, the plugin will get the version from the native code.\n *\n * Only available for Android and iOS.\n *\n * @default undefined\n * @since 4.17.48\n */\n version?: string;\n /**\n * Configure when the plugin should direct install updates. Only for autoUpdate mode.\n * - false: Never do direct updates (default behavior)\n * - atInstall: Direct update only when app is installed/updated from store, otherwise use normal background update\n * - always: Always do direct updates immediately when available\n * - true: (deprecated) Same as \"always\" for backward compatibility\n *\n * Only available for Android and iOS.\n *\n * @default false\n * @since 5.1.0\n */\n directUpdate?: boolean | 'atInstall' | 'always';\n\n /**\n * Automatically handle splashscreen hiding when using directUpdate. When enabled, the plugin will automatically hide the splashscreen after updates are applied or when no update is needed.\n * This removes the need to manually listen for appReady events and call SplashScreen.hide().\n * Only works when directUpdate is set to \"atInstall\", \"always\", or true.\n * Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false.\n * Requires autoUpdate and directUpdate to be enabled.\n *\n * Only available for Android and iOS.\n *\n * @default false\n * @since 7.6.0\n */\n autoSplashscreen?: boolean;\n\n /**\n * Configure the delay period for period update check. the unit is in seconds.\n *\n * Only available for Android and iOS.\n * Cannot be less than 600 seconds (10 minutes).\n *\n * @default 600 // (10 minutes)\n */\n periodCheckDelay?: number;\n\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localS3?: boolean;\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localHost?: string;\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localWebHost?: string;\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localSupa?: string;\n /**\n * Configure the CLI to use a local server for testing.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localSupaAnon?: string;\n /**\n * Configure the CLI to use a local api for testing.\n *\n *\n * @default undefined\n * @since 6.3.3\n */\n localApi?: string;\n /**\n * Configure the CLI to use a local file api for testing.\n *\n *\n * @default undefined\n * @since 6.3.3\n */\n localApiFiles?: string;\n /**\n * Allow the plugin to modify the updateUrl, statsUrl and channelUrl dynamically from the JavaScript side.\n *\n *\n * @default false\n * @since 5.4.0\n */\n allowModifyUrl?: boolean;\n\n /**\n * Set the default channel for the app in the config. Case sensitive.\n * This will setting will override the default channel set in the cloud, but will still respect overrides made in the cloud.\n *\n *\n * @default undefined\n * @since 5.5.0\n */\n defaultChannel?: string;\n /**\n * Configure the app id for the app in the config.\n *\n * @default undefined\n * @since 6.0.0\n */\n appId?: string;\n\n /**\n * Configure the plugin to keep the URL path after a reload.\n * WARNING: When a reload is triggered, 'window.history' will be cleared.\n *\n * @default false\n * @since 6.8.0\n */\n keepUrlPathAfterReload?: boolean;\n /**\n * 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\n *\n * @default false\n * @since 7.3.0\n */\n disableJSLogging?: boolean;\n /**\n * Enable shake gesture to show update menu for debugging/testing purposes\n *\n * @default false\n * @since 7.5.0\n */\n shakeMenu?: boolean;\n };\n }\n}\n\nexport interface CapacitorUpdaterPlugin {\n /**\n * Notify Capacitor Updater that the current bundle is working (a rollback will occur if this method is not called on every app launch)\n * By default this method should be called in the first 10 sec after app launch, otherwise a rollback will occur.\n * Change this behaviour with {@link appReadyTimeout}\n *\n * @returns {Promise<AppReadyResult>} an Promise resolved directly\n * @throws {Error}\n */\n notifyAppReady(): Promise<AppReadyResult>;\n\n /**\n * Set the updateUrl for the app, this will be used to check for updates.\n *\n * @param options contains the URL to use for checking for updates.\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 5.4.0\n */\n setUpdateUrl(options: UpdateUrl): Promise<void>;\n\n /**\n * Set the statsUrl for the app, this will be used to send statistics. Passing an empty string will disable statistics gathering.\n *\n * @param options contains the URL to use for sending statistics.\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 5.4.0\n */\n setStatsUrl(options: StatsUrl): Promise<void>;\n\n /**\n * Set the channelUrl for the app, this will be used to set the channel.\n *\n * @param options contains the URL to use for setting the channel.\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 5.4.0\n */\n setChannelUrl(options: ChannelUrl): Promise<void>;\n\n /**\n * Download a new bundle from the provided URL, it should be a zip file, with files inside or with a unique id inside with all your files\n *\n * @example const bundle = await CapacitorUpdater.download({ url: `https://example.com/versions/${version}/dist.zip`, version });\n * @returns {Promise<BundleInfo>} The {@link BundleInfo} for the specified bundle.\n * @param options The {@link DownloadOptions} for downloading a new bundle zip.\n */\n download(options: DownloadOptions): Promise<BundleInfo>;\n\n /**\n * Set the next bundle to be used when the app is reloaded.\n *\n * @param options Contains the ID of the next Bundle to set on next app launch. {@link BundleInfo.id}\n * @returns {Promise<BundleInfo>} The {@link BundleInfo} for the specified bundle id.\n * @throws {Error} When there is no index.html file inside the bundle folder.\n */\n next(options: BundleId): Promise<BundleInfo>;\n\n /**\n * Set the current bundle and immediately reloads the app.\n *\n * @param options A {@link BundleId} object containing the new bundle id to set as current.\n * @returns {Promise<void>}\n * @throws {Error} When there are is no index.html file inside the bundle folder.\n */\n set(options: BundleId): Promise<void>;\n\n /**\n * Deletes the specified bundle from the native app storage. Use with {@link list} to get the stored Bundle IDs.\n *\n * @param options A {@link BundleId} object containing the ID of a bundle to delete (note, this is the bundle id, NOT the version name)\n * @returns {Promise<void>} When the bundle is deleted\n * @throws {Error}\n */\n delete(options: BundleId): Promise<void>;\n\n /**\n * Get all locally downloaded bundles in your app\n *\n * @returns {Promise<BundleListResult>} A Promise containing the {@link BundleListResult.bundles}\n * @param options The {@link ListOptions} for listing bundles\n * @throws {Error}\n */\n list(options?: ListOptions): Promise<BundleListResult>;\n\n /**\n * Reset the app to the `builtin` bundle (the one sent to Apple App Store / Google Play Store ) or the last successfully loaded bundle.\n *\n * @param options Containing {@link ResetOptions.toLastSuccessful}, `true` resets to the builtin bundle and `false` will reset to the last successfully loaded bundle.\n * @returns {Promise<void>}\n * @throws {Error}\n */\n reset(options?: ResetOptions): Promise<void>;\n\n /**\n * Get the current bundle, if none are set it returns `builtin`. currentNative is the original bundle installed on the device\n *\n * @returns {Promise<CurrentBundleResult>} A Promise evaluating to the {@link CurrentBundleResult}\n * @throws {Error}\n */\n current(): Promise<CurrentBundleResult>;\n\n /**\n * Reload the view\n *\n * @returns {Promise<void>} A Promise which is resolved when the view is reloaded\n * @throws {Error}\n */\n reload(): Promise<void>;\n\n /**\n * Sets a {@link DelayCondition} array containing conditions that the Plugin will use to delay the update.\n * After all conditions are met, the update process will run start again as usual, so update will be installed after a backgrounding or killing the app.\n * For the `date` kind, the value should be an iso8601 date string.\n * For the `background` kind, the value should be a number in milliseconds.\n * For the `nativeVersion` kind, the value should be the version number.\n * For the `kill` kind, the value is not used.\n * The function has unconsistent behavior the option kill do trigger the update after the first kill and not after the next background like other options. This will be fixed in a future major release.\n *\n * @example\n * // Delay the update after the user kills the app or after a background of 300000 ms (5 minutes)\n * await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'kill' }, { kind: 'background', value: '300000' }] })\n * @example\n * // Delay the update after the specific iso8601 date is expired\n * await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'date', value: '2022-09-14T06:14:11.920Z' }] })\n * @example\n * // Delay the update after the first background (default behaviour without setting delay)\n * await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'background' }] })\n * @param options Containing the {@link MultiDelayConditions} array of conditions to set\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 4.3.0\n */\n setMultiDelay(options: MultiDelayConditions): Promise<void>;\n\n /**\n * Cancels a {@link DelayCondition} to process an update immediately.\n *\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 4.0.0\n */\n cancelDelay(): Promise<void>;\n\n /**\n * Get Latest bundle available from update Url\n *\n * @returns {Promise<LatestVersion>} A Promise resolved when url is loaded\n * @throws {Error}\n * @since 4.0.0\n */\n getLatest(options?: GetLatestOptions): Promise<LatestVersion>;\n\n /**\n * Sets the channel for this device. The channel has to allow for self assignment for this to work.\n * Do not use this method to set the channel at boot.\n * This method is to set the channel after the app is ready, and user interacted.\n * If you want to set the channel at boot, use the {@link PluginsConfig} to set the default channel.\n * This methods send to Capgo backend a request to link the device ID to the channel. Capgo can accept or refuse depending of the setting of your channel.\n *\n *\n *\n * @param options Is the {@link SetChannelOptions} channel to set\n * @returns {Promise<ChannelRes>} A Promise which is resolved when the new channel is set\n * @throws {Error}\n * @since 4.7.0\n */\n setChannel(options: SetChannelOptions): Promise<ChannelRes>;\n\n /**\n * Unset the channel for this device. The device will then return to the default channel\n *\n * @returns {Promise<ChannelRes>} A Promise resolved when channel is set\n * @throws {Error}\n * @since 4.7.0\n */\n unsetChannel(options: UnsetChannelOptions): Promise<void>;\n\n /**\n * Get the channel for this device\n *\n * @returns {Promise<ChannelRes>} A Promise that resolves with the channel info\n * @throws {Error}\n * @since 4.8.0\n */\n getChannel(): Promise<GetChannelRes>;\n\n /**\n * List all channels available for this device that allow self-assignment\n *\n * @returns {Promise<ListChannelsResult>} A Promise that resolves with the available channels\n * @throws {Error}\n * @since 7.5.0\n */\n listChannels(): Promise<ListChannelsResult>;\n\n /**\n * Set a custom ID for this device\n *\n * @param options is the {@link SetCustomIdOptions} customId to set\n * @returns {Promise<void>} an Promise resolved instantly\n * @throws {Error}\n * @since 4.9.0\n */\n setCustomId(options: SetCustomIdOptions): Promise<void>;\n\n /**\n * Get the native app version or the builtin version if set in config\n *\n * @returns {Promise<BuiltinVersion>} A Promise with version for this device\n * @since 5.2.0\n */\n getBuiltinVersion(): Promise<BuiltinVersion>;\n\n /**\n * Get unique ID used to identify device (sent to auto update server)\n *\n * @returns {Promise<DeviceId>} A Promise with id for this device\n * @throws {Error}\n */\n getDeviceId(): Promise<DeviceId>;\n\n /**\n * Get the native Capacitor Updater plugin version (sent to auto update server)\n *\n * @returns {Promise<PluginVersion>} A Promise with Plugin version\n * @throws {Error}\n */\n getPluginVersion(): Promise<PluginVersion>;\n\n /**\n * Get the state of auto update config.\n *\n * @returns {Promise<AutoUpdateEnabled>} The status for auto update. Evaluates to `false` in manual mode.\n * @throws {Error}\n */\n isAutoUpdateEnabled(): Promise<AutoUpdateEnabled>;\n\n /**\n * Remove all listeners for this plugin.\n *\n * @since 1.0.0\n */\n removeAllListeners(): Promise<void>;\n\n /**\n * Listen for bundle download event in the App. Fires once a download has started, during downloading and when finished.\n * This will return you all download percent during the download\n *\n * @since 2.0.11\n */\n addListener(eventName: 'download', listenerFunc: (state: DownloadEvent) => void): Promise<PluginListenerHandle>;\n\n /**\n * Listen for no need to update event, useful when you want force check every time the app is launched\n *\n * @since 4.0.0\n */\n addListener(eventName: 'noNeedUpdate', listenerFunc: (state: NoNeedEvent) => void): Promise<PluginListenerHandle>;\n\n /**\n * Listen for available update event, useful when you want to force check every time the app is launched\n *\n * @since 4.0.0\n */\n addListener(\n eventName: 'updateAvailable',\n listenerFunc: (state: UpdateAvailableEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for downloadComplete events.\n *\n * @since 4.0.0\n */\n addListener(\n eventName: 'downloadComplete',\n listenerFunc: (state: DownloadCompleteEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for Major update event in the App, let you know when major update is blocked by setting disableAutoUpdateBreaking\n *\n * @since 2.3.0\n */\n addListener(\n eventName: 'majorAvailable',\n listenerFunc: (state: MajorAvailableEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for update fail event in the App, let you know when update has fail to install at next app start\n *\n * @since 2.3.0\n */\n addListener(\n eventName: 'updateFailed',\n listenerFunc: (state: UpdateFailedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for download fail event in the App, let you know when a bundle download has failed\n *\n * @since 4.0.0\n */\n addListener(\n eventName: 'downloadFailed',\n listenerFunc: (state: DownloadFailedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for reload event in the App, let you know when reload has happened\n *\n * @since 4.3.0\n */\n addListener(eventName: 'appReloaded', listenerFunc: () => void): Promise<PluginListenerHandle>;\n\n /**\n * Listen for app ready event in the App, let you know when app is ready to use\n *\n * @since 5.1.0\n */\n addListener(eventName: 'appReady', listenerFunc: (state: AppReadyEvent) => void): Promise<PluginListenerHandle>;\n\n /**\n * Get if auto update is available (not disabled by serverUrl).\n *\n * @returns {Promise<AutoUpdateAvailable>} The availability status for auto update. Evaluates to `false` when serverUrl is set.\n * @throws {Error}\n */\n isAutoUpdateAvailable(): Promise<AutoUpdateAvailable>;\n\n /**\n * Get the next bundle that will be used when the app reloads.\n * Returns null if no next bundle is set.\n *\n * @returns {Promise<BundleInfo | null>} A Promise that resolves with the next bundle information or null\n * @throws {Error}\n * @since 6.8.0\n */\n getNextBundle(): Promise<BundleInfo | null>;\n\n /**\n * Enable or disable the shake menu for debugging/testing purposes\n *\n * @param options Contains enabled boolean to enable or disable shake menu\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 7.5.0\n */\n setShakeMenu(options: SetShakeMenuOptions): Promise<void>;\n\n /**\n * Get the current state of the shake menu\n *\n * @returns {Promise<ShakeMenuEnabled>} The current state of shake menu\n * @throws {Error}\n * @since 7.5.0\n */\n isShakeMenuEnabled(): Promise<ShakeMenuEnabled>;\n}\n\n/**\n * pending: The bundle is pending to be **SET** as the next bundle.\n * downloading: The bundle is being downloaded.\n * success: The bundle has been downloaded and is ready to be **SET** as the next bundle.\n * error: The bundle has failed to download.\n */\nexport type BundleStatus = 'success' | 'error' | 'pending' | 'downloading';\n\nexport type DelayUntilNext = 'background' | 'kill' | 'nativeVersion' | 'date';\n\nexport interface NoNeedEvent {\n /**\n * Current status of download, between 0 and 100.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface UpdateAvailableEvent {\n /**\n * Current status of download, between 0 and 100.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface ChannelRes {\n /**\n * Current status of set channel\n *\n * @since 4.7.0\n */\n status: string;\n error?: string;\n message?: string;\n}\n\nexport interface GetChannelRes {\n /**\n * Current status of get channel\n *\n * @since 4.8.0\n */\n channel?: string;\n error?: string;\n message?: string;\n status?: string;\n allowSet?: boolean;\n}\n\nexport interface ChannelInfo {\n /**\n * The channel ID\n *\n * @since 7.5.0\n */\n id: string;\n /**\n * The channel name\n *\n * @since 7.5.0\n */\n name: string;\n /**\n * Whether this is a public channel\n *\n * @since 7.5.0\n */\n public: boolean;\n /**\n * Whether devices can self-assign to this channel\n *\n * @since 7.5.0\n */\n allow_self_set: boolean;\n}\n\nexport interface ListChannelsResult {\n /**\n * List of available channels\n *\n * @since 7.5.0\n */\n channels: ChannelInfo[];\n}\n\nexport interface DownloadEvent {\n /**\n * Current status of download, between 0 and 100.\n *\n * @since 4.0.0\n */\n percent: number;\n bundle: BundleInfo;\n}\n\nexport interface MajorAvailableEvent {\n /**\n * Emit when a new major bundle is available.\n *\n * @since 4.0.0\n */\n version: string;\n}\n\nexport interface DownloadFailedEvent {\n /**\n * Emit when a download fail.\n *\n * @since 4.0.0\n */\n version: string;\n}\n\nexport interface DownloadCompleteEvent {\n /**\n * Emit when a new update is available.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface UpdateFailedEvent {\n /**\n * Emit when a update failed to install.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface AppReadyEvent {\n /**\n * Emitted when the app is ready to use.\n *\n * @since 5.2.0\n */\n bundle: BundleInfo;\n status: string;\n}\n\nexport interface ManifestEntry {\n file_name: string | null;\n file_hash: string | null;\n download_url: string | null;\n}\n\nexport interface LatestVersion {\n /**\n * Result of getLatest method\n *\n * @since 4.0.0\n */\n version: string;\n /**\n * @since 6\n */\n checksum?: string;\n major?: boolean;\n message?: string;\n sessionKey?: string;\n error?: string;\n old?: string;\n url?: string;\n /**\n * @since 6.1\n */\n manifest?: ManifestEntry[];\n}\n\nexport interface BundleInfo {\n id: string;\n version: string;\n downloaded: string;\n checksum: string;\n status: BundleStatus;\n}\n\nexport interface SetChannelOptions {\n channel: string;\n triggerAutoUpdate?: boolean;\n}\n\nexport interface UnsetChannelOptions {\n triggerAutoUpdate?: boolean;\n}\n\nexport interface SetCustomIdOptions {\n customId: string;\n}\n\nexport interface DelayCondition {\n /**\n * Set up delay conditions in setMultiDelay\n * @param value is useless for @param kind \"kill\", optional for \"background\" (default value: \"0\") and required for \"nativeVersion\" and \"date\"\n */\n kind: DelayUntilNext;\n value?: string;\n}\n\nexport interface GetLatestOptions {\n /**\n * The channel to get the latest version for\n * The channel must allow 'self_assign' for this to work\n * @since 6.8.0\n * @default undefined\n */\n channel?: string;\n}\n\nexport interface AppReadyResult {\n bundle: BundleInfo;\n}\n\nexport interface UpdateUrl {\n url: string;\n}\n\nexport interface StatsUrl {\n url: string;\n}\n\nexport interface ChannelUrl {\n url: string;\n}\n\n/**\n * 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.\n * 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.\n */\nexport interface DownloadOptions {\n /**\n * 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.)\n */\n url: string;\n /**\n * The version code/name of this bundle/version\n */\n version: string;\n /**\n * The session key for the update, when the bundle is encrypted with a session key\n * @since 4.0.0\n * @default undefined\n */\n sessionKey?: string;\n /**\n * The checksum for the update, it should be in sha256 and encrypted with private key if the bundle is encrypted\n * @since 4.0.0\n * @default undefined\n */\n checksum?: string;\n /**\n * The manifest for multi-file downloads\n * @since 6.1.0\n * @default undefined\n */\n manifest?: ManifestEntry[];\n}\n\nexport interface BundleId {\n id: string;\n}\n\nexport interface BundleListResult {\n bundles: BundleInfo[];\n}\n\nexport interface ResetOptions {\n toLastSuccessful: boolean;\n}\n\nexport interface ListOptions {\n /**\n * Whether to return the raw bundle list or the manifest. If true, the list will attempt to read the internal database instead of files on disk.\n * @since 6.14.0\n * @default false\n */\n raw?: boolean;\n}\n\nexport interface CurrentBundleResult {\n bundle: BundleInfo;\n native: string;\n}\n\nexport interface MultiDelayConditions {\n delayConditions: DelayCondition[];\n}\n\nexport interface BuiltinVersion {\n version: string;\n}\n\nexport interface DeviceId {\n deviceId: string;\n}\n\nexport interface PluginVersion {\n version: string;\n}\n\nexport interface AutoUpdateEnabled {\n enabled: boolean;\n}\n\nexport interface AutoUpdateAvailable {\n available: boolean;\n}\n\nexport interface SetShakeMenuOptions {\n enabled: boolean;\n}\n\nexport interface ShakeMenuEnabled {\n enabled: boolean;\n}\n"]}
@@ -50,7 +50,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
50
50
  CAPPluginMethod(name: "isShakeMenuEnabled", returnType: CAPPluginReturnPromise)
51
51
  ]
52
52
  public var implementation = CapgoUpdater()
53
- private let PLUGIN_VERSION: String = "7.8.1"
53
+ private let PLUGIN_VERSION: String = "7.8.2"
54
54
  static let updateUrlDefault = "https://plugin.capgo.app/updates"
55
55
  static let statsUrlDefault = "https://plugin.capgo.app/stats"
56
56
  static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
@@ -773,7 +773,8 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
773
773
  self.notifyListeners("appReady", data: ["bundle": current.toJSON(), "status": msg])
774
774
 
775
775
  // Auto hide splashscreen if enabled
776
- if self.autoSplashscreen && self.shouldUseDirectUpdate() {
776
+ // We show it on background when conditions are met, so we should hide it on foreground regardless of update outcome
777
+ if self.autoSplashscreen {
777
778
  self.hideSplashscreen()
778
779
  }
779
780
  }
@@ -782,7 +783,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
782
783
  private func hideSplashscreen() {
783
784
  DispatchQueue.main.async {
784
785
  guard let bridge = self.bridge else {
785
- self.logger.warn("Bridge not available for hiding splashscreen")
786
+ self.logger.warn("Bridge not available for hiding splashscreen with autoSplashscreen")
786
787
  return
787
788
  }
788
789
 
@@ -801,10 +802,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
801
802
  _ = splashScreenPlugin.perform(selector, with: call)
802
803
  self.logger.info("Called SplashScreen hide method")
803
804
  } else {
804
- self.logger.warn("SplashScreen plugin does not respond to hide: method")
805
+ self.logger.warn("autoSplashscreen: SplashScreen plugin does not respond to hide: method. Make sure @capacitor/splash-screen plugin is properly installed.")
805
806
  }
806
807
  } else {
807
- self.logger.warn("SplashScreen plugin not found")
808
+ self.logger.warn("autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin.")
808
809
  }
809
810
  }
810
811
  }
@@ -812,7 +813,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
812
813
  private func showSplashscreen() {
813
814
  DispatchQueue.main.async {
814
815
  guard let bridge = self.bridge else {
815
- self.logger.warn("Bridge not available for showing splashscreen")
816
+ self.logger.warn("Bridge not available for showing splashscreen with autoSplashscreen")
816
817
  return
817
818
  }
818
819
 
@@ -831,10 +832,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
831
832
  _ = splashScreenPlugin.perform(selector, with: call)
832
833
  self.logger.info("Called SplashScreen show method")
833
834
  } else {
834
- self.logger.warn("SplashScreen plugin does not respond to show: method")
835
+ self.logger.warn("autoSplashscreen: SplashScreen plugin does not respond to show: method. Make sure @capacitor/splash-screen plugin is properly installed.")
835
836
  }
836
837
  } else {
837
- self.logger.warn("SplashScreen plugin not found")
838
+ self.logger.warn("autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin.")
838
839
  }
839
840
  }
840
841
  }
@@ -900,6 +901,13 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
900
901
  let res = self.implementation.getLatest(url: url, channel: nil)
901
902
  let current = self.implementation.getCurrentBundle()
902
903
 
904
+ // Handle network errors and other failures first
905
+ if res.error != nil {
906
+ self.logger.error("getLatest failed with error: \(res.error ?? "")")
907
+ self.endBackGroundTaskWithNotif(msg: "Network error: \(res.error ?? "")", latestVersionName: res.version, current: current, error: true)
908
+ return
909
+ }
910
+
903
911
  if (res.message) != nil {
904
912
  self.logger.info("API message: \(res.message ?? "")")
905
913
  if res.major == true {
@@ -1098,9 +1106,23 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
1098
1106
  self.implementation.sendStats(action: "app_moved_to_background", versionName: current.getVersionName())
1099
1107
  logger.info("Check for pending update")
1100
1108
 
1101
- // Show splashscreen if autoSplashscreen is enabled
1109
+ // Show splashscreen only if autoSplashscreen is enabled AND autoUpdate is enabled AND directUpdate would be used
1102
1110
  if self.autoSplashscreen {
1103
- self.showSplashscreen()
1111
+ var canShowSplashscreen = true
1112
+
1113
+ if !self._isAutoUpdateEnabled() {
1114
+ logger.warn("autoSplashscreen is enabled but autoUpdate is disabled. Splashscreen will not be shown. Enable autoUpdate or disable autoSplashscreen.")
1115
+ canShowSplashscreen = false
1116
+ }
1117
+
1118
+ if !self.shouldUseDirectUpdate() {
1119
+ logger.warn("autoSplashscreen is enabled but directUpdate is not configured for immediate updates. Set directUpdate to 'always' or 'atInstall', or disable autoSplashscreen.")
1120
+ canShowSplashscreen = false
1121
+ }
1122
+
1123
+ if canShowSplashscreen {
1124
+ self.showSplashscreen()
1125
+ }
1104
1126
  }
1105
1127
 
1106
1128
  // Set background timestamp
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-updater",
3
- "version": "7.8.1",
3
+ "version": "7.8.2",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Live update for capacitor apps",
6
6
  "main": "dist/plugin.cjs.js",