@capgo/capacitor-inappbrowser 8.18.1 → 8.20.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
@@ -217,7 +217,7 @@ All CCT options are Android-only and safely ignored on iOS. See [`OpenOptions`](
217
217
 
218
218
  ### Open WebView with Custom Dimensions
219
219
 
220
- By default, the webview opens in fullscreen. You can set custom dimensions to control the size and position:
220
+ By default, the WebView fills the available screen and retains its configured toolbar. You can set custom dimensions to control the size and position:
221
221
 
222
222
  ```js
223
223
  import { InAppBrowser } from '@capgo/capacitor-inappbrowser';
@@ -259,9 +259,44 @@ InAppBrowser.openWebView({
259
259
 
260
260
  Web platform is not supported. Use `window.open` instead.
261
261
 
262
- ### Open WebView in Full Screen Mode
262
+ ### Fullscreen at launch and during a session
263
263
 
264
- To open the webview in true full screen mode (content extends behind the status bar), set `enabledSafeTopMargin` to `false`:
264
+ Set `fullscreen: true` to present a native WebView in fullscreen from its first visible frame:
265
+
266
+ ```ts
267
+ import { InAppBrowser, ToolBarType } from '@capgo/capacitor-inappbrowser';
268
+
269
+ const listener = await InAppBrowser.addListener('fullscreenChange', ({ id, enabled }) => {
270
+ console.log('Fullscreen changed', id, enabled);
271
+ });
272
+
273
+ const { id } = await InAppBrowser.openWebView({
274
+ url: 'https://example.com',
275
+ toolbarType: ToolBarType.NAVIGATION,
276
+ fullscreen: true,
277
+ });
278
+
279
+ await InAppBrowser.setFullscreen({ id, enabled: false });
280
+ await InAppBrowser.setFullscreen({ id, enabled: true });
281
+ const { enabled } = await InAppBrowser.getFullscreen({ id });
282
+
283
+ // When the host no longer needs updates:
284
+ await listener.remove();
285
+ ```
286
+
287
+ Fullscreen hides native navigation and system bars. A native exit button stays above the page at the top-right safe-area inset. Android Back exits fullscreen before navigating page history. Exiting restores the configured toolbar and safe-area settings. Entry and exit keep the same WebView, including its JavaScript state, cookies, and history.
288
+
289
+ The opening option defaults to `false`. It also works with `isPresentAfterPageLoad: true` and `hidden: true`. An initially hidden WebView applies fullscreen when first shown; until then, `getFullscreen()` reports `false` and the host's system bars stay unchanged. Calling `setFullscreen({ id, enabled: false })` cancels that pending startup request.
290
+
291
+ Backgrounding, hiding, closing, cross-origin navigation, and renderer termination exit fullscreen through native cleanup. Showing or resuming the WebView afterward does not enter fullscreen again. The host receives a `fullscreenChange` event for each applied change, including native exit controls. Register the listener before opening to observe initial entry.
292
+
293
+ These APIs control the browser's presentation independently of HTML/video fullscreen. They support full-size, frontmost `openWebView` presentations on iOS and Android. Runtime entry into a hidden WebView, custom dimensions, behind-host presentations, and missing target IDs reject. Web usage is unsupported. Permission prompts are the host application's responsibility.
294
+
295
+ The example app's Fullscreen section includes launch options and an interactive page for runtime entry, exit, hide/show, history, form, cookie, and counter checks.
296
+
297
+ ### Extend content behind the status bar
298
+
299
+ To extend content behind the status bar, set `enabledSafeTopMargin` to `false`:
265
300
 
266
301
  ```js
267
302
  import { InAppBrowser } from '@capgo/capacitor-inappbrowser';
@@ -277,7 +312,7 @@ This option works independently of the toolbar type:
277
312
  - **iOS**: The webview extends behind the status bar, providing true edge-to-edge content
278
313
  - **Android**: The top margin is disabled, allowing content to fill the entire screen
279
314
 
280
- Perfect for immersive experiences like video players, games, or full-screen web applications. Can be combined with any `toolbarType` setting.
315
+ This setting controls the content inset. Use `fullscreen` to hide native navigation and system bars as well.
281
316
 
282
317
  ### Proxy examples
283
318
 
@@ -632,6 +667,7 @@ await InAppBrowser.openWebView({
632
667
  * [`takeScreenshot(...)`](#takescreenshot)
633
668
  * [`setUrl(...)`](#seturl)
634
669
  * [`addListener('urlChangeEvent', ...)`](#addlistenerurlchangeevent-)
670
+ * [`addListener('fullscreenChange', ...)`](#addlistenerfullscreenchange-)
635
671
  * [`addListener('buttonNearDoneClick', ...)`](#addlistenerbuttonneardoneclick-)
636
672
  * [`addListener('closeEvent', ...)`](#addlistenercloseevent-)
637
673
  * [`addListener('hideEvent', ...)`](#addlistenerhideevent-)
@@ -652,6 +688,8 @@ await InAppBrowser.openWebView({
652
688
  * [`removeAllListeners()`](#removealllisteners)
653
689
  * [`reload(...)`](#reload)
654
690
  * [`updateDimensions(...)`](#updatedimensions)
691
+ * [`setFullscreen(...)`](#setfullscreen)
692
+ * [`getFullscreen(...)`](#getfullscreen)
655
693
  * [`setEnabledSafeTopMargin(...)`](#setenabledsafetopmargin)
656
694
  * [`setEnabledSafeBottomMargin(...)`](#setenabledsafebottommargin)
657
695
  * [`openSecureWindow(...)`](#opensecurewindow)
@@ -1029,6 +1067,27 @@ Listen for url change, only for openWebView
1029
1067
  --------------------
1030
1068
 
1031
1069
 
1070
+ ### addListener('fullscreenChange', ...)
1071
+
1072
+ ```typescript
1073
+ addListener(eventName: 'fullscreenChange', listenerFunc: (event: FullscreenChangeEvent) => void) => Promise<PluginListenerHandle>
1074
+ ```
1075
+
1076
+ Listen for applied fullscreen changes, including startup entry, the native exit button,
1077
+ Android Back, and native lifecycle cleanup. Register before opening to observe startup entry.
1078
+ Repeated requests for the current state do not emit duplicate events.
1079
+ This event describes host-controlled fullscreen, not HTML/video fullscreen.
1080
+
1081
+ | Param | Type |
1082
+ | ------------------ | ------------------------------------------------------------------------------------------- |
1083
+ | **`eventName`** | <code>'fullscreenChange'</code> |
1084
+ | **`listenerFunc`** | <code>(event: <a href="#fullscreenchangeevent">FullscreenChangeEvent</a>) =&gt; void</code> |
1085
+
1086
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
1087
+
1088
+ --------------------
1089
+
1090
+
1032
1091
  ### addListener('buttonNearDoneClick', ...)
1033
1092
 
1034
1093
  ```typescript
@@ -1425,6 +1484,49 @@ When `id` is omitted, targets the active webview.
1425
1484
  --------------------
1426
1485
 
1427
1486
 
1487
+ ### setFullscreen(...)
1488
+
1489
+ ```typescript
1490
+ setFullscreen(options: { enabled: boolean; id?: string; }) => Promise<void>
1491
+ ```
1492
+
1493
+ Enter or exit immersive fullscreen without recreating or reloading the webview.
1494
+ When `id` is omitted, targets the active webview. Repeated calls are idempotent.
1495
+ Entry requires a visible, frontmost, full-size `openWebView` on iOS or Android.
1496
+ Missing webviews, invalid arguments, custom dimensions, behind-host presentations,
1497
+ and the Web platform reject. Use the opening `fullscreen` option for an initially hidden webview.
1498
+ Disabling also cancels any pending fullscreen-at-launch request.
1499
+
1500
+ Native exit controls, backgrounding, hiding, closing, cross-origin navigation, and renderer
1501
+ termination restore the original chrome even when host JavaScript cannot respond.
1502
+
1503
+ | Param | Type |
1504
+ | ------------- | ----------------------------------------------- |
1505
+ | **`options`** | <code>{ enabled: boolean; id?: string; }</code> |
1506
+
1507
+ --------------------
1508
+
1509
+
1510
+ ### getFullscreen(...)
1511
+
1512
+ ```typescript
1513
+ getFullscreen(options?: { id?: string | undefined; } | undefined) => Promise<{ enabled: boolean; }>
1514
+ ```
1515
+
1516
+ Read the applied host-controlled fullscreen state of an `openWebView`.
1517
+ When `id` is omitted, targets the active webview. Initially hidden fullscreen webviews
1518
+ report `false` until their first presentation. Missing webviews and Web usage reject.
1519
+ HTML/video fullscreen does not change this value.
1520
+
1521
+ | Param | Type |
1522
+ | ------------- | ----------------------------- |
1523
+ | **`options`** | <code>{ id?: string; }</code> |
1524
+
1525
+ **Returns:** <code>Promise&lt;{ enabled: boolean; }&gt;</code>
1526
+
1527
+ --------------------
1528
+
1529
+
1428
1530
  ### setEnabledSafeTopMargin(...)
1429
1531
 
1430
1532
  ```typescript
@@ -1574,6 +1676,7 @@ See the README section "openSecureWindow (OAuth)" for full setup examples.
1574
1676
 
1575
1677
  | Prop | Type | Description | Default | Since |
1576
1678
  | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ------ |
1679
+ | **`fullscreen`** | <code>boolean</code> | Present this webview in immersive fullscreen from its first visible frame. Hides native navigation and system bars and provides a native exit-fullscreen button. The same webview, page state, cookies, and history are retained on entry and exit. Exiting restores the configured toolbar and safe-area settings. Applies before presentation, including when `isPresentAfterPageLoad` is true. With `hidden: true`, waits for the first `show()` without changing the host's system bars. Subsequent hide/show or background/resume cycles do not re-enter fullscreen automatically. Only supported on iOS and Android for full-size, frontmost `openWebView` presentations. Custom dimensions and `toBack: true` are unsupported. The Web implementation rejects `true`. Permission prompts, if needed, are the host application's responsibility. | <code>false</code> | |
1577
1680
  | **`url`** | <code>string</code> | Target URL to load. Remote `http://` and `https://` URLs are loaded as-is. To open bundled web assets from the app bundle without running a local HTTP server, pass a relative path such as `/index.html` or `assets/page.html`. The plugin resolves it to the Capacitor local URL for the current platform (defaults: `capacitor://localhost/...` on iOS, `https://localhost/...` on Android; actual scheme and host follow the app's configured Capacitor local URL) and serves files from the packaged `public/` directory, or `www/` on iOS when `public/` is absent. | | 0.1.0 |
1578
1681
  | **`headers`** | <code><a href="#headers">Headers</a></code> | <a href="#headers">Headers</a> to send with the request. | | 0.1.0 |
1579
1682
  | **`customUserAgent`** | <code>string</code> | Custom User-Agent string for the webview. When set, replaces the system default webview User-Agent on iOS and Android. Takes precedence over a `User-Agent` entry in `headers`. | | 8.13.0 |
@@ -1590,7 +1693,8 @@ See the README section "openSecureWindow (OAuth)" for full setup examples.
1590
1693
  | **`useSharedDataStore`** | <code>boolean</code> | Share the host Capacitor WebView's website data store (cookies, local storage, etc.). On iOS 17+, InAppBrowser uses an isolated plugin-owned `WKWebsiteDataStore` by default so host and browser data stay separate. Set this to `true` to use `WKWebsiteDataStore.default()` instead, which shares session cookies with the Capacitor host WebView. Useful for SSO / OIDC silent login when the IdP session was established in the main app WebView. Requires `persistWebViewData: true` (the default). When `persistWebViewData` is false, a non-persistent store is used and this option has no effect. On Android this is a no-op: cookies are already process-global via `CookieManager`. On Web this is a no-op. Warning: clearing cookies/cache for a webview opened with this flag can affect the host WebView on iOS, because both share the same store. `clearAllBrowsingData()` still skips the host store. | <code>false</code> | 8.13.6 |
1591
1694
  | **`clientCertificate`** | <code>'none' \| 'prompt'</code> | Controls Android TLS client certificate prompts during HTTPS handshakes. Use `prompt` to show the system certificate picker; omit or use `none` to cancel silently (default). | <code>"none"</code> | 8.7.5 |
1592
1695
  | **`clientCertificatePrompt`** | <code>boolean</code> | | | |
1593
- | **`handleDownloads`** | <code>boolean</code> | Automatically handles downloads triggered inside the webview without requiring a custom JavaScript bridge. When enabled: - Standard attachment responses are written to a temporary file. - `blob:` downloads are also captured when the platform supports them. - Previewable files reopen inside the in-app browser when possible. - Other files are handed off to the native preview or viewer flow. - `downloadCompleted` and `downloadFailed` events notify the host app about the saved file. | <code>false</code> | 8.6.0 |
1696
+ | **`handleDownloads`** | <code>boolean</code> | Automatically handles downloads triggered inside the webview without requiring a custom JavaScript bridge. When enabled: - Standard attachment responses are written to a temporary file. - `blob:` downloads are also captured when the platform supports them. - Previewable files reopen inside the in-app browser when possible (see `downloadPreview`). - Other files are handed off to the native preview or viewer flow. - `downloadCompleted` and `downloadFailed` events notify the host app about the saved file. | <code>false</code> | 8.6.0 |
1697
+ | **`downloadPreview`** | <code><a href="#downloadpreview">DownloadPreview</a></code> | Controls where managed downloads are previewed after saving. - `'inAppBrowser'` (default): Reopens previewable files (PDF, images, text, JSON) inside the same webview. - `'systemPreview'`: Opens the system Quick Look preview with share and Save to Files actions. iOS only; ignored on Android. Requires `handleDownloads: true`. | <code>"inAppBrowser"</code> | 8.19.0 |
1594
1698
  | **`shareDisclaimer`** | <code><a href="#disclaimeroptions">DisclaimerOptions</a></code> | Share options for the webview. When provided, shows a disclaimer dialog before sharing content. This is useful for: - Warning users about sharing sensitive information - Getting user consent before sharing - Explaining what will be shared - Complying with privacy regulations Note: shareSubject is required when using shareDisclaimer | | 0.1.0 |
1595
1699
  | **`toolbarType`** | <code><a href="#toolbartype">ToolBarType</a></code> | Toolbar type determines the appearance and behavior of the browser's toolbar - "activity": Shows a simple toolbar with just a close button and share button - "navigation": Shows a full navigation toolbar with back/forward buttons - "blank": Shows no toolbar - "": Default toolbar with close button | <code>ToolBarType.DEFAULT</code> | 0.1.0 |
1596
1700
  | **`shareSubject`** | <code>string</code> | Subject text for sharing. Required when using shareDisclaimer. This text will be used as the subject line when sharing content. | | 0.1.0 |
@@ -1725,6 +1829,16 @@ Any regex property that is omitted is treated as a wildcard.
1725
1829
  | **`url`** | <code>string</code> | Emit when the url changes | 0.0.1 |
1726
1830
 
1727
1831
 
1832
+ #### FullscreenChangeEvent
1833
+
1834
+ A change to host-controlled fullscreen; independent of HTML/video fullscreen.
1835
+
1836
+ | Prop | Type | Description |
1837
+ | ------------- | -------------------- | --------------------------------------------------------------- |
1838
+ | **`id`** | <code>string</code> | Webview instance id. |
1839
+ | **`enabled`** | <code>boolean</code> | Whether fullscreen is currently applied to the visible webview. |
1840
+
1841
+
1728
1842
  #### ButtonNearDoneEvent
1729
1843
 
1730
1844
  | Prop | Type | Description | Since |
@@ -1956,6 +2070,13 @@ Construct a type with a set of properties K of type T
1956
2070
  <code>'click' | 'touchstart' | 'touchmove' | 'touchend' | 'touchcancel'</code>
1957
2071
 
1958
2072
 
2073
+ #### DownloadPreview
2074
+
2075
+ Where managed downloads are previewed after saving.
2076
+
2077
+ <code>'inAppBrowser' | 'systemPreview'</code>
2078
+
2079
+
1959
2080
  #### PreferredContentMode
1960
2081
 
1961
2082
  Web content rendering mode for managed WKWebViews on iOS.
@@ -339,6 +339,7 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
339
339
  }
340
340
 
341
341
  private WebViewDialog createManagedDialog(String webViewId, Options options, boolean makeActive) {
342
+ if (fullscreenPaused) options.setFullscreen(false);
342
343
  WebViewDialog dialog = new WebViewDialog(
343
344
  getContext(),
344
345
  android.R.style.Theme_NoTitleBar,
@@ -346,6 +347,12 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
346
347
  CapgoInAppBrowserPlugin.this,
347
348
  getBridge().getWebView()
348
349
  );
350
+ dialog.setFullscreenChangeListener((enabled) -> {
351
+ JSObject event = new JSObject();
352
+ event.put("id", webViewId);
353
+ event.put("enabled", enabled);
354
+ notifyListeners("fullscreenChange", event);
355
+ });
349
356
  dialog.setInstanceId(webViewId);
350
357
  dialog.activity = CapgoInAppBrowserPlugin.this.getActivity();
351
358
  dialog.bindToHostActivity();
@@ -1117,6 +1124,18 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1117
1124
  options.setAllowScreenshotsFromWebPage(Boolean.TRUE.equals(call.getBoolean("allowScreenshotsFromWebPage", false)));
1118
1125
  options.setCaptureConsoleLogs(Boolean.TRUE.equals(call.getBoolean("captureConsoleLogs", false)));
1119
1126
  options.setHandleDownloads(Boolean.TRUE.equals(call.getBoolean("handleDownloads", false)));
1127
+ String downloadPreview = call.getString("downloadPreview", "inAppBrowser");
1128
+ if (downloadPreview != null) {
1129
+ String normalizedDownloadPreview = downloadPreview.toLowerCase(Locale.ROOT);
1130
+ if ("inappbrowser".equals(normalizedDownloadPreview)) {
1131
+ options.setDownloadPreview("inAppBrowser");
1132
+ } else if ("systempreview".equals(normalizedDownloadPreview)) {
1133
+ options.setDownloadPreview("systemPreview");
1134
+ } else {
1135
+ call.reject("downloadPreview must be 'inAppBrowser' or 'systemPreview'");
1136
+ return;
1137
+ }
1138
+ }
1120
1139
 
1121
1140
  options.setPersistWebViewData(call.getBoolean("persistWebViewData", true));
1122
1141
  options.setClearCookiesOnOpen(Boolean.TRUE.equals(call.getBoolean("clearCookiesOnOpen", false)));
@@ -1355,9 +1374,14 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1355
1374
  options.setY(y);
1356
1375
  }
1357
1376
 
1377
+ options.setFullscreen(Boolean.TRUE.equals(call.getBoolean("fullscreen", false)));
1358
1378
  options.setHidden(Boolean.TRUE.equals(call.getBoolean("hidden", false)));
1359
1379
  options.setToBack(Boolean.TRUE.equals(call.getBoolean("toBack", false)));
1360
1380
  options.setTransparentBackground(call.getBoolean("transparentBackground", true));
1381
+ if (options.isFullscreen() && (options.isToBack() || width != null || height != null || x != null || y != null)) {
1382
+ call.reject("Fullscreen requires a full-size front WebView");
1383
+ return;
1384
+ }
1361
1385
  boolean allowWebViewJsVisibilityControl = getConfig().getBoolean("allowWebViewJsVisibilityControl", false);
1362
1386
  options.setAllowWebViewJsVisibilityControl(allowWebViewJsVisibilityControl);
1363
1387
  options.setInvisibilityMode(Options.InvisibilityMode.fromString(call.getString("invisibilityMode", "AWARE")));
@@ -1448,6 +1472,50 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1448
1472
  );
1449
1473
  }
1450
1474
 
1475
+ private boolean fullscreenPaused;
1476
+
1477
+ @PluginMethod
1478
+ public void setFullscreen(PluginCall call) {
1479
+ Object enabled = call.getData().opt("enabled");
1480
+ if (!(enabled instanceof Boolean)) {
1481
+ call.reject("enabled must be a boolean");
1482
+ return;
1483
+ }
1484
+ String id = resolveTargetId(call);
1485
+ getActivity().runOnUiThread(() -> {
1486
+ WebViewDialog dialog = resolveDialog(id);
1487
+ if (dialog == null) {
1488
+ call.reject("WebView is not initialized");
1489
+ return;
1490
+ }
1491
+ if ((Boolean) enabled && (!dialog.isShowing() || fullscreenPaused)) {
1492
+ call.reject("Application is in background");
1493
+ return;
1494
+ }
1495
+ try {
1496
+ dialog.setFullscreen((Boolean) enabled);
1497
+ call.resolve();
1498
+ } catch (IllegalStateException error) {
1499
+ call.reject(error.getMessage());
1500
+ }
1501
+ });
1502
+ }
1503
+
1504
+ @PluginMethod
1505
+ public void getFullscreen(PluginCall call) {
1506
+ String id = resolveTargetId(call);
1507
+ getActivity().runOnUiThread(() -> {
1508
+ WebViewDialog dialog = resolveDialog(id);
1509
+ if (dialog == null) {
1510
+ call.reject("WebView is not initialized");
1511
+ return;
1512
+ }
1513
+ JSObject result = new JSObject();
1514
+ result.put("enabled", dialog.isFullscreen());
1515
+ call.resolve(result);
1516
+ });
1517
+ }
1518
+
1451
1519
  @PluginMethod
1452
1520
  public void hide(PluginCall call) {
1453
1521
  String targetId = resolveTargetId(call);
@@ -1466,6 +1534,7 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1466
1534
  call.reject("WebView is not initialized");
1467
1535
  return;
1468
1536
  }
1537
+ dialog.clearFullscreen();
1469
1538
  dialog.setHidden(true);
1470
1539
  call.resolve();
1471
1540
  }
@@ -1491,6 +1560,9 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1491
1560
  call.reject("WebView is not initialized");
1492
1561
  return;
1493
1562
  }
1563
+ if (dialog.getInstanceId() != null) {
1564
+ setActiveWebView(dialog.getInstanceId(), dialog);
1565
+ }
1494
1566
  dialog.setHidden(false);
1495
1567
  Options options = dialog.getOptions();
1496
1568
  if (options != null && options.isToBack()) {
@@ -1501,9 +1573,6 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1501
1573
  } else {
1502
1574
  dialog.bringToFrontLayer();
1503
1575
  }
1504
- if (dialog.getInstanceId() != null) {
1505
- setActiveWebView(dialog.getInstanceId(), dialog);
1506
- }
1507
1576
  call.resolve();
1508
1577
  }
1509
1578
  }
@@ -1886,6 +1955,7 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1886
1955
  }
1887
1956
 
1888
1957
  protected void handleOnResume() {
1958
+ fullscreenPaused = false;
1889
1959
  customTabsLifecycleSupport.onResume(customTabsBinder);
1890
1960
 
1891
1961
  // If we have a saved call and user returned without callback, reject
@@ -1896,6 +1966,8 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1896
1966
  }
1897
1967
 
1898
1968
  protected void handleOnPause() {
1969
+ fullscreenPaused = true;
1970
+ for (WebViewDialog dialog : webViewDialogs.values()) dialog.clearFullscreen();
1899
1971
  customTabsLifecycleSupport.onPause(customTabsBinder);
1900
1972
  }
1901
1973
 
@@ -0,0 +1,72 @@
1
+ package ee.forgr.capacitor_inappbrowser;
2
+
3
+ import android.os.Build;
4
+ import android.view.View;
5
+ import android.view.Window;
6
+ import android.view.WindowManager;
7
+ import androidx.core.view.ViewCompat;
8
+ import androidx.core.view.WindowInsetsCompat;
9
+ import androidx.core.view.WindowInsetsControllerCompat;
10
+
11
+ /** Saves each nesting level so media fullscreen can return to browser fullscreen. */
12
+ final class FullscreenWindowState {
13
+
14
+ private final Window window;
15
+ private final WindowManager.LayoutParams attributes = new WindowManager.LayoutParams();
16
+ private final int visibility;
17
+ private final int behavior;
18
+ private final boolean statusVisible;
19
+ private final boolean navigationVisible;
20
+
21
+ FullscreenWindowState(Window window) {
22
+ this.window = window;
23
+ attributes.copyFrom(window.getAttributes());
24
+ View decor = window.getDecorView();
25
+ visibility = decor.getSystemUiVisibility();
26
+ WindowInsetsControllerCompat controller = new WindowInsetsControllerCompat(window, decor);
27
+ behavior = controller.getSystemBarsBehavior();
28
+ WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(decor);
29
+ statusVisible =
30
+ insets != null
31
+ ? insets.isVisible(WindowInsetsCompat.Type.statusBars())
32
+ : (attributes.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) == 0;
33
+ navigationVisible =
34
+ insets != null
35
+ ? insets.isVisible(WindowInsetsCompat.Type.navigationBars())
36
+ : (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
37
+ }
38
+
39
+ void enter() {
40
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
41
+ // DEFAULT excludes the cutout once bars hide, leaving a black band in immersive mode.
42
+ WindowManager.LayoutParams fullscreenAttributes = window.getAttributes();
43
+ fullscreenAttributes.layoutInDisplayCutoutMode =
44
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
45
+ ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
46
+ : WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
47
+ window.setAttributes(fullscreenAttributes);
48
+ }
49
+ window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
50
+ View decor = window.getDecorView();
51
+ decor.setSystemUiVisibility(
52
+ decor.getSystemUiVisibility() |
53
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
54
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
55
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
56
+ );
57
+ WindowInsetsControllerCompat controller = new WindowInsetsControllerCompat(window, decor);
58
+ controller.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
59
+ controller.hide(WindowInsetsCompat.Type.systemBars());
60
+ }
61
+
62
+ void restore() {
63
+ WindowInsetsControllerCompat controller = new WindowInsetsControllerCompat(window, window.getDecorView());
64
+ if (statusVisible) controller.show(WindowInsetsCompat.Type.statusBars());
65
+ else controller.hide(WindowInsetsCompat.Type.statusBars());
66
+ if (navigationVisible) controller.show(WindowInsetsCompat.Type.navigationBars());
67
+ else controller.hide(WindowInsetsCompat.Type.navigationBars());
68
+ controller.setSystemBarsBehavior(behavior);
69
+ window.setAttributes(attributes);
70
+ window.getDecorView().setSystemUiVisibility(visibility);
71
+ }
72
+ }
@@ -198,12 +198,24 @@ public class Options {
198
198
  private boolean hidden = false;
199
199
  private boolean toBack = false;
200
200
  private boolean transparentBackground = true;
201
+ private boolean fullscreen = false;
202
+
203
+ public boolean isFullscreen() {
204
+ return fullscreen;
205
+ }
206
+
207
+ public void setFullscreen(boolean fullscreen) {
208
+ this.fullscreen = fullscreen;
209
+ }
210
+
201
211
  private boolean showScreenshotButton = false;
202
212
  private boolean screenshotOnHide = false;
203
213
  private boolean allowWebViewJsVisibilityControl = false;
204
214
  private boolean allowScreenshotsFromWebPage = false;
205
215
  private boolean captureConsoleLogs = false;
206
216
  private boolean handleDownloads = false;
217
+ /** Accepted for API parity with iOS; Android already uses an external viewer for managed downloads. */
218
+ private String downloadPreview = "inAppBrowser";
207
219
  private boolean persistWebViewData = true;
208
220
  private boolean clearCookiesOnOpen = false;
209
221
  private boolean clearCacheOnOpen = false;
@@ -315,6 +327,14 @@ public class Options {
315
327
  this.handleDownloads = handleDownloads;
316
328
  }
317
329
 
330
+ public String getDownloadPreview() {
331
+ return downloadPreview;
332
+ }
333
+
334
+ public void setDownloadPreview(String downloadPreview) {
335
+ this.downloadPreview = downloadPreview;
336
+ }
337
+
318
338
  public boolean getPersistWebViewData() {
319
339
  return persistWebViewData;
320
340
  }
@@ -900,6 +920,7 @@ public class Options {
900
920
  copy.setAllowScreenshotsFromWebPage(allowScreenshotsFromWebPage);
901
921
  copy.setCaptureConsoleLogs(captureConsoleLogs);
902
922
  copy.setHandleDownloads(handleDownloads);
923
+ copy.setDownloadPreview(downloadPreview);
903
924
  copy.setInvisibilityMode(invisibilityMode);
904
925
  copy.setPopupWindowMode(true);
905
926
  copy.setHiddenPopupWindow(hiddenPopupWindow);