@capgo/capacitor-updater 8.50.1 → 8.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -300,46 +300,46 @@ Downgrading to a previous version of the updater plugin is not supported.
300
300
 
301
301
  CapacitorUpdater can be configured with these options:
302
302
 
303
- | Prop | Type | Description | Default | Since |
304
- | ------------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------- |
305
- | **`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> | |
306
- | **`responseTimeout`** | <code>number</code> | Configure the number of seconds the native plugin should wait before considering API timeout. Only available for Android and iOS. | <code>20 // (20 second)</code> | |
307
- | **`autoDeleteFailed`** | <code>boolean</code> | Configure whether the plugin should use automatically delete failed bundles. Only available for Android and iOS. | <code>true</code> | |
308
- | **`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> | |
309
- | **`autoUpdate`** | <code>boolean \| 'always' \| 'off' \| 'atBackground' \| 'atInstall' \| 'onLaunch' \| 'onlyDownload'</code> | Configure how the plugin checks for, downloads, and applies live updates. The plugin checks for updates when the app moves to the foreground and, if {@link periodCheckDelay} is set, on a repeating timer while the app stays open. Boolean values keep their existing behavior: - `true`: Same as `"atBackground"`. - `false`: Same as `"off"`. String values merge the previous Auto Update and Direct Update configuration: - `"off"`: Disable automatic update checks. - `"atBackground"`: Check and download automatically on each foreground check, then apply the update the next time the app moves to background. - `"atInstall"`: Apply immediately only after a fresh install or native app store update; otherwise use `"atBackground"` behavior. - `"onLaunch"`: Apply immediately only when the app is brought to the foreground from a killed state (cold start). After that first check, fall back to `"atBackground"` behavior. - `"always"`: Check on every foreground transition and apply immediately whenever an update is available. - `"onlyDownload"`: Check and download automatically, emit `updateAvailable`, and never set the next bundle or apply an update automatically. Only available for Android and iOS. | <code>true</code> | |
310
- | **`resetWhenUpdate`** | <code>boolean</code> | Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device. Setting this to false can broke the auto update flow if the user download from the store a native app bundle that is older than the current downloaded bundle. Upload will be prevented by channel setting downgrade_under_native. Only available for Android and iOS. | <code>true</code> | |
311
- | **`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> | |
312
- | **`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> | |
313
- | **`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. Native stats include update lifecycle events, app health signals such as crashes, Android ANRs, low-memory exits, iOS memory warnings, and WebView health signals such as JavaScript errors, unhandled promise rejections, resource load failures, WebView renderer exits, and unclean WebView restarts when available. | <code>https://plugin.capgo.app/stats</code> | |
314
- | **`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 |
315
- | **`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 |
316
- | **`directUpdate`** | <code>boolean \| 'always' \| 'atInstall' \| 'onLaunch'</code> | Configure when the plugin should direct install updates. Only for autoUpdate mode. | <code>false</code> | 5.1.0 |
317
- | **`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 autoUpdate is set to "atInstall", "always", or "onLaunch", or when the deprecated directUpdate option is set to "atInstall", "always", "onLaunch", or true. Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false. Requires Auto Update and Direct Update behavior to be enabled. Only available for Android and iOS. | <code>false</code> | 7.6.0 |
318
- | **`autoSplashscreenLoader`** | <code>boolean</code> | Display a native loading indicator on top of the splashscreen while automatic direct updates are running. Only takes effect when {@link autoSplashscreen} is enabled. Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false. Only available for Android and iOS. | <code>false</code> | 7.19.0 |
319
- | **`autoSplashscreenTimeout`** | <code>number</code> | Automatically hide the splashscreen after the specified number of milliseconds when using automatic direct updates. If the timeout elapses, the update continues to download in the background while the splashscreen is dismissed. Set to `0` (zero) to disable the timeout. When the timeout fires, the direct update flow is skipped and the downloaded bundle is installed on the next background/launch. Requires {@link autoSplashscreen} to be enabled. Only available for Android and iOS. | <code>10000 // (10 seconds)</code> | 7.19.0 |
320
- | **`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>0 (disabled)</code> | |
321
- | **`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 |
322
- | **`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 |
323
- | **`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 |
324
- | **`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 |
325
- | **`localSupaAnon`** | <code>string</code> | Configure the CLI to use a local server for testing. | <code>undefined</code> | 4.17.48 |
326
- | **`localApi`** | <code>string</code> | Configure the CLI to use a local api for testing. | <code>undefined</code> | 6.3.3 |
327
- | **`localApiFiles`** | <code>string</code> | Configure the CLI to use a local file api for testing. | <code>undefined</code> | 6.3.3 |
328
- | **`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 |
329
- | **`allowModifyAppId`** | <code>boolean</code> | Allow the plugin to modify the appId dynamically from the JavaScript side. | <code>false</code> | 7.14.0 |
330
- | **`allowManualBundleError`** | <code>boolean</code> | Allow marking bundles as errored from JavaScript while using manual update flows. When enabled, {@link CapacitorUpdaterPlugin.setBundleError} can change a bundle status to `error`. | <code>false</code> | 7.20.0 |
331
- | **`allowPreview`** | <code>boolean</code> | Allow JavaScript to start a native preview session and temporarily request updates for another app id. This is intended for trusted container apps that implement Expo Go-style preview flows. Only available for Android and iOS. | <code>false</code> | 8.47.0 |
332
- | **`persistCustomId`** | <code>boolean</code> | Persist the customId set through {@link CapacitorUpdaterPlugin.setCustomId} across app restarts. Only available for Android and iOS. | <code>false (will be true by default in a future major release v8.x.x)</code> | 7.17.3 |
333
- | **`persistModifyUrl`** | <code>boolean</code> | Persist the updateUrl, statsUrl and channelUrl set through {@link CapacitorUpdaterPlugin.setUpdateUrl}, {@link CapacitorUpdaterPlugin.setStatsUrl} and {@link CapacitorUpdaterPlugin.setChannelUrl} across app restarts. Only available for Android and iOS. | <code>false</code> | 7.20.0 |
334
- | **`allowSetDefaultChannel`** | <code>boolean</code> | Allow or disallow the {@link CapacitorUpdaterPlugin.setChannel} method to modify the defaultChannel. When set to `false`, calling `setChannel()` will return an error with code `disabled_by_config`. | <code>true</code> | 7.34.0 |
335
- | **`defaultChannel`** | <code>string</code> | Set the default channel for the app in the config. Case sensitive. This will setting will override the default channel set in the cloud, but will still respect overrides made in the cloud. This requires the channel to allow devices to self dissociate/associate in the channel settings. https://capgo.app/docs/public-api/channels/#channel-configuration-options | <code>undefined</code> | 5.5.0 |
336
- | **`appId`** | <code>string</code> | Configure the app id for the app in the config. | <code>undefined</code> | 6.0.0 |
337
- | **`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 |
338
- | **`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 |
339
- | **`osLogging`** | <code>boolean</code> | Enable OS-level logging. When enabled, logs are written to the system log which can be inspected in production builds. - **iOS**: Uses os_log instead of Swift.print, logs accessible via Console.app or Instruments - **Android**: Logs to Logcat (android.util.Log) When set to false, system logging is disabled on both platforms (only JavaScript console logging will occur if enabled). This is useful for debugging production apps (App Store/TestFlight builds on iOS, or production APKs on Android). | <code>true</code> | 8.42.0 |
340
- | **`shakeMenu`** | <code>boolean</code> | Enable the native preview menu gesture while a preview session is active. Outside preview sessions this preview menu is ignored, unless {@link PluginsConfig.CapacitorUpdater.allowShakeChannelSelector} is enabled. | <code>false</code> | 7.5.0 |
341
- | **`shakeMenuGesture`** | <code><a href="#shakemenugesture">ShakeMenuGesture</a></code> | Choose which native gesture opens the preview/channel menu. This applies to both {@link PluginsConfig.CapacitorUpdater.shakeMenu} and {@link PluginsConfig.CapacitorUpdater.allowShakeChannelSelector}. Only available for Android and iOS. | <code>'shake'</code> | 8.48.0 |
342
- | **`allowShakeChannelSelector`** | <code>boolean</code> | Enable the native menu gesture to show a channel selector menu for switching between update channels. If {@link PluginsConfig.CapacitorUpdater.shakeMenu} is also enabled while a preview session is active, the shake menu includes both preview actions and channel switching. The native gesture can be changed with {@link PluginsConfig.CapacitorUpdater.shakeMenuGesture}. Only available for Android and iOS. | <code>false</code> | 8.43.0 |
303
+ | Prop | Type | Description | Default | Since |
304
+ | ------------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------- |
305
+ | **`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> | |
306
+ | **`responseTimeout`** | <code>number</code> | Configure the number of seconds the native plugin should wait before considering API timeout. Only available for Android and iOS. | <code>20 // (20 second)</code> | |
307
+ | **`autoDeleteFailed`** | <code>boolean</code> | Configure whether the plugin should use automatically delete failed bundles. Only available for Android and iOS. | <code>true</code> | |
308
+ | **`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> | |
309
+ | **`autoUpdate`** | <code>boolean \| 'always' \| 'off' \| 'atBackground' \| 'atInstall' \| 'onLaunch' \| 'onlyDownload'</code> | Configure how the plugin checks for, downloads, and applies live updates. The plugin checks for updates when the app moves to the foreground. When {@link periodCheckDelay} is greater than 0, it also checks on a repeating timer while the app stays open. Boolean values keep their existing behavior: - `true`: Same as `"atBackground"`. - `false`: Same as `"off"`. String values merge the previous Auto Update and Direct Update configuration: - `"off"`: Disable automatic update checks. - `"atBackground"`: Check and download automatically on each foreground check, then apply the update the next time the app moves to background. - `"atInstall"`: Apply immediately only after a fresh install or native app store update; otherwise use `"atBackground"` behavior. - `"onLaunch"`: Apply immediately only when the app is brought to the foreground from a killed state (cold start). After that first check, fall back to `"atBackground"` behavior. - `"always"`: Check on every foreground transition and apply immediately whenever an update is available. - `"onlyDownload"`: Check and download automatically, emit `updateAvailable`, and never set the next bundle or apply an update automatically. Only available for Android and iOS. | <code>true</code> | |
310
+ | **`resetWhenUpdate`** | <code>boolean</code> | Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device. Setting this to false can broke the auto update flow if the user download from the store a native app bundle that is older than the current downloaded bundle. Upload will be prevented by channel setting downgrade_under_native. Only available for Android and iOS. | <code>true</code> | |
311
+ | **`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> | |
312
+ | **`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> | |
313
+ | **`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. Native stats include update lifecycle events, app health signals such as crashes, Android ANRs, low-memory exits, iOS memory warnings, and WebView health signals such as JavaScript errors, unhandled promise rejections, resource load failures, WebView renderer exits, unclean WebView restarts, app launch readiness timing, and WebView load milestones when available. | <code>https://plugin.capgo.app/stats</code> | |
314
+ | **`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 |
315
+ | **`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 |
316
+ | **`directUpdate`** | <code>boolean \| 'always' \| 'atInstall' \| 'onLaunch'</code> | Configure when the plugin should direct install updates. Only for autoUpdate mode. | <code>false</code> | 5.1.0 |
317
+ | **`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 autoUpdate is set to "atInstall", "always", or "onLaunch", or when the deprecated directUpdate option is set to "atInstall", "always", "onLaunch", or true. Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false. Requires Auto Update and Direct Update behavior to be enabled. Only available for Android and iOS. | <code>false</code> | 7.6.0 |
318
+ | **`autoSplashscreenLoader`** | <code>boolean</code> | Display a native loading indicator on top of the splashscreen while automatic direct updates are running. Only takes effect when {@link autoSplashscreen} is enabled. Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false. Only available for Android and iOS. | <code>false</code> | 7.19.0 |
319
+ | **`autoSplashscreenTimeout`** | <code>number</code> | Automatically hide the splashscreen after the specified number of milliseconds when using automatic direct updates. If the timeout elapses, the update continues to download in the background while the splashscreen is dismissed. Set to `0` (zero) to disable the timeout. When the timeout fires, the direct update flow is skipped and the downloaded bundle is installed on the next background/launch. Requires {@link autoSplashscreen} to be enabled. Only available for Android and iOS. | <code>10000 // (10 seconds)</code> | 7.19.0 |
320
+ | **`periodCheckDelay`** | <code>number</code> | Configure the interval in seconds for repeating update checks while the app stays open. Foreground checks still run when this is 0. Values below 600 are normalized to 600. Only available for Android and iOS. Cannot be less than 600 seconds (10 minutes). | <code>0 (disabled)</code> | |
321
+ | **`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 |
322
+ | **`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 |
323
+ | **`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 |
324
+ | **`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 |
325
+ | **`localSupaAnon`** | <code>string</code> | Configure the CLI to use a local server for testing. | <code>undefined</code> | 4.17.48 |
326
+ | **`localApi`** | <code>string</code> | Configure the CLI to use a local api for testing. | <code>undefined</code> | 6.3.3 |
327
+ | **`localApiFiles`** | <code>string</code> | Configure the CLI to use a local file api for testing. | <code>undefined</code> | 6.3.3 |
328
+ | **`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 |
329
+ | **`allowModifyAppId`** | <code>boolean</code> | Allow the plugin to modify the appId dynamically from the JavaScript side. | <code>false</code> | 7.14.0 |
330
+ | **`allowManualBundleError`** | <code>boolean</code> | Allow marking bundles as errored from JavaScript while using manual update flows. When enabled, {@link CapacitorUpdaterPlugin.setBundleError} can change a bundle status to `error`. | <code>false</code> | 7.20.0 |
331
+ | **`allowPreview`** | <code>boolean</code> | Allow JavaScript to start a native preview session and temporarily request updates for another app id. This is intended for trusted container apps that implement Expo Go-style preview flows. Only available for Android and iOS. | <code>false</code> | 8.47.0 |
332
+ | **`persistCustomId`** | <code>boolean</code> | Persist the customId set through {@link CapacitorUpdaterPlugin.setCustomId} across app restarts. Only available for Android and iOS. | <code>false (will be true by default in a future major release v8.x.x)</code> | 7.17.3 |
333
+ | **`persistModifyUrl`** | <code>boolean</code> | Persist the updateUrl, statsUrl and channelUrl set through {@link CapacitorUpdaterPlugin.setUpdateUrl}, {@link CapacitorUpdaterPlugin.setStatsUrl} and {@link CapacitorUpdaterPlugin.setChannelUrl} across app restarts. Only available for Android and iOS. | <code>false</code> | 7.20.0 |
334
+ | **`allowSetDefaultChannel`** | <code>boolean</code> | Allow or disallow the {@link CapacitorUpdaterPlugin.setChannel} method to modify the defaultChannel. When set to `false`, calling `setChannel()` will return an error with code `disabled_by_config`. | <code>true</code> | 7.34.0 |
335
+ | **`defaultChannel`** | <code>string</code> | Set the default channel for the app in the config. Case sensitive. This will setting will override the default channel set in the cloud, but will still respect overrides made in the cloud. This requires the channel to allow devices to self dissociate/associate in the channel settings. https://capgo.app/docs/public-api/channels/#channel-configuration-options | <code>undefined</code> | 5.5.0 |
336
+ | **`appId`** | <code>string</code> | Configure the app id for the app in the config. | <code>undefined</code> | 6.0.0 |
337
+ | **`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 |
338
+ | **`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 |
339
+ | **`osLogging`** | <code>boolean</code> | Enable OS-level logging. When enabled, logs are written to the system log which can be inspected in production builds. - **iOS**: Uses os_log instead of Swift.print, logs accessible via Console.app or Instruments - **Android**: Logs to Logcat (android.util.Log) When set to false, system logging is disabled on both platforms (only JavaScript console logging will occur if enabled). This is useful for debugging production apps (App Store/TestFlight builds on iOS, or production APKs on Android). | <code>true</code> | 8.42.0 |
340
+ | **`shakeMenu`** | <code>boolean</code> | Enable the native preview menu gesture while a preview session is active. Outside preview sessions this preview menu is ignored, unless {@link PluginsConfig.CapacitorUpdater.allowShakeChannelSelector} is enabled. | <code>false</code> | 7.5.0 |
341
+ | **`shakeMenuGesture`** | <code><a href="#shakemenugesture">ShakeMenuGesture</a></code> | Choose which native gesture opens the preview/channel menu. This applies to both {@link PluginsConfig.CapacitorUpdater.shakeMenu} and {@link PluginsConfig.CapacitorUpdater.allowShakeChannelSelector}. Only available for Android and iOS. | <code>'shake'</code> | 8.48.0 |
342
+ | **`allowShakeChannelSelector`** | <code>boolean</code> | Enable the native menu gesture to show a channel selector menu for switching between update channels. If {@link PluginsConfig.CapacitorUpdater.shakeMenu} is also enabled while a preview session is active, the shake menu includes both preview actions and channel switching. The native gesture can be changed with {@link PluginsConfig.CapacitorUpdater.shakeMenuGesture}. Only available for Android and iOS. | <code>false</code> | 8.43.0 |
343
343
 
344
344
  ### Examples
345
345
 
@@ -659,7 +659,9 @@ For multi-file delta updates, provide the `manifest` array.
659
659
  **Android Background Runner note:** `@capacitor/background-runner` loads its
660
660
  configured runner script from native APK assets. Live updates cannot replace
661
661
  that runner script. Keep it stable across OTA updates and ship a native app
662
- update when the runner code changes.
662
+ update when the runner code changes. When a bundle switch happens, Capacitor
663
+ Updater cancels and reschedules configured Background Runner WorkManager jobs
664
+ and syncs the bundled runner script into native `public/` storage when present.
663
665
 
664
666
  | Param | Type | Description |
665
667
  | ------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
@@ -69,7 +69,7 @@ dependencies {
69
69
  androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
70
70
  androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
71
71
  implementation 'org.brotli:dec:0.1.2'
72
- implementation 'com.squareup.okhttp3:okhttp:4.12.0'
72
+ implementation 'com.squareup.okhttp3:okhttp:5.4.0'
73
73
  }
74
74
 
75
75
  tasks.withType(Test) {
@@ -142,7 +142,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
142
142
  static final int APPLICATION_EXIT_REASON_USER_REQUESTED = 10;
143
143
  static final int APPLICATION_EXIT_REASON_DEPENDENCY_DIED = 12;
144
144
 
145
- private final String pluginVersion = "8.50.1";
145
+ private final String pluginVersion = "8.51.0";
146
146
  private static final String DELAY_CONDITION_PREFERENCES = "";
147
147
 
148
148
  private SharedPreferences.Editor editor;
@@ -236,6 +236,10 @@ public class CapacitorUpdaterPlugin extends Plugin {
236
236
 
237
237
  private ShakeMenu shakeMenu;
238
238
  private final Handler mainHandler = new Handler(Looper.getMainLooper());
239
+ private final long launchStartedAtMs = System.currentTimeMillis();
240
+ private volatile long webViewPageStartedAtMs = 0;
241
+ private volatile boolean launchStartReported = false;
242
+ private volatile boolean launchReadyReported = false;
239
243
  private FrameLayout splashscreenLoaderOverlay;
240
244
  private Runnable splashscreenTimeoutRunnable;
241
245
  private FrameLayout previewTransitionLoaderOverlay;
@@ -807,6 +811,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
807
811
  }
808
812
  logger.info("init for device " + this.implementation.deviceID);
809
813
  logger.info("version native " + this.currentVersionNative.getOriginalString());
814
+ this.reportAppLaunchStart();
810
815
  this.autoDeleteFailed = this.getConfig().getBoolean("autoDeleteFailed", true);
811
816
  this.autoDeletePrevious = this.getConfig().getBoolean("autoDeletePrevious", true);
812
817
  this.updateUrl = this.getConfig().getString("updateUrl", updateUrlDefault);
@@ -1486,26 +1491,39 @@ public class CapacitorUpdaterPlugin extends Plugin {
1486
1491
  this.webViewStatsListener = new WebViewListener() {
1487
1492
  @Override
1488
1493
  public void onPageStarted(final android.webkit.WebView view) {
1494
+ CapacitorUpdaterPlugin.this.webViewPageStartedAtMs = System.currentTimeMillis();
1489
1495
  CapacitorUpdaterPlugin.this.evaluateWebViewStatsReporterScript(view, script);
1490
1496
  }
1491
1497
 
1492
1498
  @Override
1493
1499
  public void onPageLoaded(final android.webkit.WebView view) {
1500
+ CapacitorUpdaterPlugin.this.reportWebViewPageLoaded(view);
1494
1501
  CapacitorUpdaterPlugin.this.evaluateWebViewStatsReporterScript(view, script);
1495
1502
  }
1496
-
1497
- @Override
1498
- public boolean onRenderProcessGone(final android.webkit.WebView view, final RenderProcessGoneDetail detail) {
1499
- final Map<String, String> metadata = CapacitorUpdaterPlugin.this.buildWebViewRenderProcessGoneMetadata(detail);
1500
- CapacitorUpdaterPlugin.this.persistPendingWebViewRenderProcessGone(metadata);
1501
- return false;
1502
- }
1503
1503
  };
1504
1504
 
1505
1505
  this.bridge.addWebViewListener(this.webViewStatsListener);
1506
+ // Keep RenderProcessGoneDetail off the Plugin class method table and off this
1507
+ // listener so Android < 8 (API 26) does not crash during plugin reflection.
1508
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1509
+ this.installWebViewRenderProcessGoneReporter();
1510
+ }
1506
1511
  this.evaluateWebViewStatsReporterScript(webView, script);
1507
1512
  }
1508
1513
 
1514
+ private void installWebViewRenderProcessGoneReporter() {
1515
+ this.bridge.addWebViewListener(
1516
+ new WebViewListener() {
1517
+ @Override
1518
+ public boolean onRenderProcessGone(final android.webkit.WebView view, final RenderProcessGoneDetail detail) {
1519
+ final Map<String, String> metadata = CapacitorUpdaterPlugin.this.buildWebViewRenderProcessGoneMetadata(detail);
1520
+ CapacitorUpdaterPlugin.this.persistPendingWebViewRenderProcessGone(metadata);
1521
+ return false;
1522
+ }
1523
+ }
1524
+ );
1525
+ }
1526
+
1509
1527
  private void installDocumentStartWebViewStatsReporter(final android.webkit.WebView webView, final String script) {
1510
1528
  try {
1511
1529
  final Class<?> webViewFeature = Class.forName("androidx.webkit.WebViewFeature");
@@ -1545,16 +1563,84 @@ public class CapacitorUpdaterPlugin extends Plugin {
1545
1563
  });
1546
1564
  }
1547
1565
 
1548
- private Map<String, String> buildWebViewRenderProcessGoneMetadata(final RenderProcessGoneDetail detail) {
1566
+ private void reportAppLaunchStart() {
1567
+ if (
1568
+ this.implementation == null ||
1569
+ this.implementation.statsUrl == null ||
1570
+ this.implementation.statsUrl.isEmpty() ||
1571
+ this.launchStartReported
1572
+ ) {
1573
+ return;
1574
+ }
1575
+
1576
+ this.launchStartReported = true;
1577
+ final BundleInfo current = this.implementation.getCurrentBundle();
1578
+ final Map<String, String> metadata = new HashMap<>();
1579
+ metadata.put("launch_started_at", Long.toString(this.launchStartedAtMs));
1580
+ metadata.put("source", "plugin_load");
1581
+ this.implementation.sendStats("app_launch_start", current == null ? "" : current.getVersionName(), "", metadata);
1582
+ }
1583
+
1584
+ private void reportAppLaunchReady(final BundleInfo bundle) {
1585
+ if (
1586
+ this.implementation == null ||
1587
+ this.implementation.statsUrl == null ||
1588
+ this.implementation.statsUrl.isEmpty() ||
1589
+ this.launchReadyReported
1590
+ ) {
1591
+ return;
1592
+ }
1593
+
1594
+ this.launchReadyReported = true;
1595
+ final Map<String, String> metadata = new HashMap<>();
1596
+ metadata.put("duration_ms", Long.toString(Math.max(0, System.currentTimeMillis() - this.launchStartedAtMs)));
1597
+ metadata.put("launch_started_at", Long.toString(this.launchStartedAtMs));
1598
+ metadata.put("source", "notify_app_ready");
1599
+ this.implementation.sendStats("app_launch_ready", bundle == null ? "" : bundle.getVersionName(), "", metadata);
1600
+ }
1601
+
1602
+ private void reportAppLaunchTimeout(final BundleInfo bundle) {
1603
+ if (this.implementation == null || this.implementation.statsUrl == null || this.implementation.statsUrl.isEmpty()) {
1604
+ return;
1605
+ }
1606
+
1607
+ final Map<String, String> metadata = new HashMap<>();
1608
+ metadata.put("duration_ms", Long.toString(Math.max(0, System.currentTimeMillis() - this.launchStartedAtMs)));
1609
+ metadata.put("launch_started_at", Long.toString(this.launchStartedAtMs));
1610
+ metadata.put("timeout_ms", Long.toString(this.appReadyTimeout));
1611
+ metadata.put("source", "app_ready_timeout");
1612
+ this.implementation.sendStats("app_launch_timeout", bundle == null ? "" : bundle.getVersionName(), "", metadata);
1613
+ }
1614
+
1615
+ private void reportWebViewPageLoaded(final android.webkit.WebView view) {
1616
+ if (this.implementation == null || this.implementation.statsUrl == null || this.implementation.statsUrl.isEmpty()) {
1617
+ return;
1618
+ }
1619
+
1620
+ final Map<String, String> metadata = new HashMap<>();
1621
+ metadata.put("source", "android_webview_listener");
1622
+ final long pageStartedAt = this.webViewPageStartedAtMs;
1623
+ if (pageStartedAt > 0) {
1624
+ metadata.put("duration_ms", Long.toString(Math.max(0, System.currentTimeMillis() - pageStartedAt)));
1625
+ metadata.put("page_started_at", Long.toString(pageStartedAt));
1626
+ }
1627
+ if (view != null && view.getUrl() != null && !view.getUrl().isEmpty()) {
1628
+ metadata.put("href", truncateStatsMetadataValue(sanitizeStatsMetadataUrl(view.getUrl()), 512));
1629
+ }
1630
+ this.reportWebViewStats("webview_page_loaded", metadata);
1631
+ }
1632
+
1633
+ private Map<String, String> buildWebViewRenderProcessGoneMetadata(final Object detailObj) {
1549
1634
  final Map<String, String> metadata = new HashMap<>();
1550
1635
  metadata.put("error_type", "render_process_gone");
1551
1636
  metadata.put("source", "android_on_render_process_gone");
1552
1637
  metadata.put("timestamp", Long.toString(System.currentTimeMillis()));
1553
- if (detail != null) {
1638
+ // Parameter typed as Object so Android < 8 ART does not resolve
1639
+ // RenderProcessGoneDetail while reflecting CapacitorUpdaterPlugin methods.
1640
+ if (detailObj != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1641
+ final RenderProcessGoneDetail detail = (RenderProcessGoneDetail) detailObj;
1554
1642
  metadata.put("did_crash", Boolean.toString(detail.didCrash()));
1555
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1556
- metadata.put("renderer_priority_at_exit", Integer.toString(detail.rendererPriorityAtExit()));
1557
- }
1643
+ metadata.put("renderer_priority_at_exit", Integer.toString(detail.rendererPriorityAtExit()));
1558
1644
  }
1559
1645
  return metadata;
1560
1646
  }
@@ -1652,6 +1738,10 @@ public class CapacitorUpdaterPlugin extends Plugin {
1652
1738
  return "webview_render_process_gone";
1653
1739
  case "web_content_process_terminated":
1654
1740
  return "webview_content_process_terminated";
1741
+ case "webview_dom_content_loaded":
1742
+ return "webview_dom_content_loaded";
1743
+ case "webview_page_loaded":
1744
+ return "webview_page_loaded";
1655
1745
  case "javascript_error":
1656
1746
  default:
1657
1747
  return "webview_javascript_error";
@@ -1670,6 +1760,8 @@ public class CapacitorUpdaterPlugin extends Plugin {
1670
1760
  putStatsMetadataValue(metadata, "href", sanitizeStatsMetadataUrl(data.optString("href", "")), 512);
1671
1761
  putStatsMetadataValue(metadata, "user_agent", data.optString("user_agent", ""), 256);
1672
1762
  putStatsMetadataValue(metadata, "session_id", data.optString("session_id", ""), 128);
1763
+ putStatsMetadataValue(metadata, "duration_ms", data.optString("duration_ms", ""), 32);
1764
+ putStatsMetadataValue(metadata, "page_started_at", data.optString("page_started_at", ""), 64);
1673
1765
  putStatsMetadataValue(metadata, "previous_session_id", data.optString("previous_session_id", ""), 128);
1674
1766
  putStatsMetadataValue(metadata, "previous_href", sanitizeStatsMetadataUrl(data.optString("previous_href", "")), 512);
1675
1767
  putStatsMetadataValue(metadata, "previous_started_at", data.optString("previous_started_at", ""), 64);
@@ -1802,12 +1894,14 @@ public class CapacitorUpdaterPlugin extends Plugin {
1802
1894
  "if(previous&&previous.active){send({type:'webview_unclean_restart',message:'WebView restarted without a clean page unload',previous_session_id:s(previous.id),previous_href:s(previous.href),previous_started_at:s(previous.started_at),previous_updated_at:s(previous.updated_at)});}" +
1803
1895
  "writeSession(true);" +
1804
1896
  "setInterval(function(){writeSession(true);},15000);" +
1897
+ "function pageDuration(){var started=Number(window.__capgoWebViewSessionStartedAt||Date.now());return String(Math.max(0,Date.now()-started));}" +
1805
1898
  "function markClean(){writeSession(false);}" +
1806
1899
  "window.addEventListener('pagehide',markClean,true);" +
1807
1900
  "window.addEventListener('beforeunload',markClean,true);" +
1808
1901
  "window.addEventListener('error',function(event){var target=event&&event.target;if(target&&target!==window&&(target.src||target.href)){send({type:'resource_error',message:'Resource failed to load',source:s(target.src||target.href),tag_name:s(target.tagName)});return;}send({type:'javascript_error',message:s((event&&event.message)||(event&&event.error)),source:s(event&&event.filename),line:s(event&&event.lineno),column:s(event&&event.colno),stack:stack(event&&event.error)});},true);" +
1809
1902
  "window.addEventListener('unhandledrejection',function(event){var reason=event&&event.reason;send({type:'unhandled_rejection',message:s(reason),stack:stack(reason)});},true);" +
1810
1903
  "document.addEventListener('securitypolicyviolation',function(event){send({type:'security_policy_violation',message:s(event&&event.violatedDirective),source:s(event&&event.blockedURI)});},true);" +
1904
+ "document.addEventListener('DOMContentLoaded',function(){send({type:'webview_dom_content_loaded',message:'WebView DOM content loaded',duration_ms:pageDuration(),page_started_at:String(window.__capgoWebViewSessionStartedAt)});},true);" +
1811
1905
  "document.addEventListener('deviceready',scheduleFlush,false);" +
1812
1906
  "setTimeout(scheduleFlush,0);" +
1813
1907
  "})();"
@@ -4187,6 +4281,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
4187
4281
  try {
4188
4282
  final BundleInfo bundle = this.implementation.getCurrentBundle();
4189
4283
  this.implementation.setSuccess(bundle, this.autoDeletePrevious);
4284
+ this.reportAppLaunchReady(bundle);
4190
4285
  logger.info("Current bundle loaded successfully. ['notifyAppReady()' was called] " + bundle);
4191
4286
  logger.info("semaphoreReady countDown");
4192
4287
  this.semaphoreDown();
@@ -4861,6 +4956,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
4861
4956
  ret.put("bundle", InternalUtils.mapToJSObject(current.toJSONMap()));
4862
4957
  this.persistLastFailedBundle(current);
4863
4958
  this.notifyListeners("updateFailed", ret);
4959
+ this.reportAppLaunchTimeout(current);
4864
4960
  this.implementation.sendStats("update_fail", current.getVersionName());
4865
4961
  this.implementation.setError(current);
4866
4962
  this.performReset(true, false, true);