@capgo/capacitor-inappbrowser 8.19.0 → 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 |
@@ -1726,6 +1829,16 @@ Any regex property that is omitted is treated as a wildcard.
1726
1829
  | **`url`** | <code>string</code> | Emit when the url changes | 0.0.1 |
1727
1830
 
1728
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
+
1729
1842
  #### ButtonNearDoneEvent
1730
1843
 
1731
1844
  | Prop | Type | Description | Since |
@@ -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();
@@ -1367,9 +1374,14 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1367
1374
  options.setY(y);
1368
1375
  }
1369
1376
 
1377
+ options.setFullscreen(Boolean.TRUE.equals(call.getBoolean("fullscreen", false)));
1370
1378
  options.setHidden(Boolean.TRUE.equals(call.getBoolean("hidden", false)));
1371
1379
  options.setToBack(Boolean.TRUE.equals(call.getBoolean("toBack", false)));
1372
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
+ }
1373
1385
  boolean allowWebViewJsVisibilityControl = getConfig().getBoolean("allowWebViewJsVisibilityControl", false);
1374
1386
  options.setAllowWebViewJsVisibilityControl(allowWebViewJsVisibilityControl);
1375
1387
  options.setInvisibilityMode(Options.InvisibilityMode.fromString(call.getString("invisibilityMode", "AWARE")));
@@ -1460,6 +1472,50 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1460
1472
  );
1461
1473
  }
1462
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
+
1463
1519
  @PluginMethod
1464
1520
  public void hide(PluginCall call) {
1465
1521
  String targetId = resolveTargetId(call);
@@ -1478,6 +1534,7 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1478
1534
  call.reject("WebView is not initialized");
1479
1535
  return;
1480
1536
  }
1537
+ dialog.clearFullscreen();
1481
1538
  dialog.setHidden(true);
1482
1539
  call.resolve();
1483
1540
  }
@@ -1503,6 +1560,9 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1503
1560
  call.reject("WebView is not initialized");
1504
1561
  return;
1505
1562
  }
1563
+ if (dialog.getInstanceId() != null) {
1564
+ setActiveWebView(dialog.getInstanceId(), dialog);
1565
+ }
1506
1566
  dialog.setHidden(false);
1507
1567
  Options options = dialog.getOptions();
1508
1568
  if (options != null && options.isToBack()) {
@@ -1513,9 +1573,6 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1513
1573
  } else {
1514
1574
  dialog.bringToFrontLayer();
1515
1575
  }
1516
- if (dialog.getInstanceId() != null) {
1517
- setActiveWebView(dialog.getInstanceId(), dialog);
1518
- }
1519
1576
  call.resolve();
1520
1577
  }
1521
1578
  }
@@ -1898,6 +1955,7 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1898
1955
  }
1899
1956
 
1900
1957
  protected void handleOnResume() {
1958
+ fullscreenPaused = false;
1901
1959
  customTabsLifecycleSupport.onResume(customTabsBinder);
1902
1960
 
1903
1961
  // If we have a saved call and user returned without callback, reject
@@ -1908,6 +1966,8 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1908
1966
  }
1909
1967
 
1910
1968
  protected void handleOnPause() {
1969
+ fullscreenPaused = true;
1970
+ for (WebViewDialog dialog : webViewDialogs.values()) dialog.clearFullscreen();
1911
1971
  customTabsLifecycleSupport.onPause(customTabsBinder);
1912
1972
  }
1913
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,6 +198,16 @@ 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;