@capgo/capacitor-inappbrowser 8.15.3 → 8.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/CapgoInAppBrowserPlugin.java +15 -65
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java +1 -2
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/OpenTimeBrowsingDataClearSupport.java +40 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/Options.java +20 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/SafeAreaInsetsSupport.java +25 -5
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/SystemUiChromeSupport.java +109 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewCustomFullscreenSupport.java +49 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java +344 -200
- package/dist/docs.json +34 -2
- package/dist/esm/definitions.d.ts +30 -5
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/InAppBrowserPlugin/InAppBrowserPlugin.swift +36 -1
- package/ios/Sources/InAppBrowserPlugin/WKWebViewController.swift +13 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1584,7 +1584,9 @@ See the README section "openSecureWindow (OAuth)" for full setup examples.
|
|
|
1584
1584
|
| **`jsInterface`** | | JavaScript Interface: The webview automatically injects a JavaScript interface providing: - `window.mobileApp.close()`: Closes the webview from JavaScript - `window.mobileApp.postMessage(obj)`: Sends a message to the app (listen via "messageFromWebview" event) - `window.mobileApp.hide()` / `window.mobileApp.show()` when allowWebViewJsVisibilityControl is true in CapacitorConfig - `window.mobileApp.takeScreenshot()` when `allowScreenshotsFromWebPage` is true | | 6.10.0 |
|
|
1585
1585
|
| **`allowScreenshotsFromWebPage`** | <code>boolean</code> | Allows page JavaScript to call `window.mobileApp.takeScreenshot()`. Disabled by default so only the host app can trigger native screenshots through the plugin API. | <code>false</code> | 8.4.0 |
|
|
1586
1586
|
| **`captureConsoleLogs`** | <code>boolean</code> | Emits `consoleMessage` events for JavaScript `console.*` output coming from the managed page. Useful when the webview stays hidden and you still need page-level diagnostics. | <code>false</code> | 8.6.0 |
|
|
1587
|
-
| **`persistWebViewData`** | <code>boolean</code> | Controls whether the webview should persist website data such as cache, cookies, local storage, IndexedDB, and session data. When false, iOS uses a non-persistent `WKWebsiteDataStore`. Android disables per-view cache and
|
|
1587
|
+
| **`persistWebViewData`** | <code>boolean</code> | Controls whether the webview should persist website data such as cache, cookies, local storage, IndexedDB, and session data. When false, iOS uses a non-persistent `WKWebsiteDataStore`. Android disables per-view cache and database storage where the system WebView supports it while keeping DOM storage available so SPAs can use `localStorage`. Android cookies use the shared WebView cookie store; clearing them also affects the host WebView, so prefer per-URL cookie helpers instead of relying on process-global wipes. | <code>true</code> | 8.6.36 |
|
|
1588
|
+
| **`clearCookiesOnOpen`** | <code>boolean</code> | Clear all cookies from the InAppBrowser data store before the first navigation of this webview. Cordova `clearcache: 'yes'` parity. On Android, uses process-global `CookieManager.removeAllCookies()` and blocks until the wipe completes. On iOS, clears cookies from the webview's `WKWebsiteDataStore` (isolated plugin store by default). On Web this is a no-op. | <code>false</code> | 8.16.0 |
|
|
1589
|
+
| **`clearCacheOnOpen`** | <code>boolean</code> | Clear cached website data before the first navigation of this webview. Cordova `cleardata: 'yes'` parity. On Android, calls `WebView.clearCache(true)` after the webview is created. On iOS, removes disk and memory cache from the webview's `WKWebsiteDataStore`. On Web this is a no-op. | <code>false</code> | 8.16.0 |
|
|
1588
1590
|
| **`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 |
|
|
1589
1591
|
| **`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 |
|
|
1590
1592
|
| **`clientCertificatePrompt`** | <code>boolean</code> | | | |
|
|
@@ -1628,7 +1630,7 @@ See the README section "openSecureWindow (OAuth)" for full setup examples.
|
|
|
1628
1630
|
| **`preventDeeplink`** | <code>boolean</code> | If true, deeplinks and external app hand-off are blocked and stay in the webview. If false (default), custom schemes such as `tel:`, `mailto:`, and `sms:` open natively. On iOS, listing a custom scheme under `LSApplicationQueriesSchemes` is only required when you rely on `canOpenURL` for that scheme (for example `instagram://`). It is not required for HTTPS `authorizedAppLinks`, and `mailto`/`tel`/`sms` open without that Info.plist entry. | <code>false</code> | 0.1.0 |
|
|
1629
1631
|
| **`openBlankTargetInWebView`** | <code>boolean</code> | When true, HTTP and HTTPS links opened from `target="_blank"` anchors stay in the current webview instead of spawning a popup or opening in the system browser. By default, blank-target HTTP(S) links stay inside the plugin as managed popups that share cookies with the opener; enabling this option keeps everything in the same tab. Custom schemes such as `tel:` and `mailto:` and authorized app links still prefer their native handlers unless `preventDeeplink` is enabled. | <code>false</code> | 8.5.6 |
|
|
1630
1632
|
| **`authorizedAppLinks`** | <code>string[]</code> | List of base URLs whose hosts are treated as authorized App Links (Android) and Universal Links (iOS). - On both platforms, only HTTP(S) links whose host matches any entry in this list will attempt to leave the in-app browser for the native / system handler. - iOS tries a Universal Link first (`universalLinksOnly`), then falls back to a normal system open (App Store, Safari, etc.). Only if both fail does the URL stay in-webview. - Android uses an `ACTION_VIEW` intent for matching hosts. - Matching is host-based (case-insensitive), ignoring the "www." prefix. - HTTPS hosts do not need `LSApplicationQueriesSchemes`; that Info.plist key is for custom schemes like `instagram://`, not `https://instagram.com`. - When `preventDeeplink` is enabled, all external handling is blocked regardless of this list. | <code>[]</code> | 7.12.0 |
|
|
1631
|
-
| **`enabledSafeBottomMargin`** | <code>boolean</code> | If true, the webView is inset by the bottom system bar (navigation bar) so bottom-anchored content stays reachable.
|
|
1633
|
+
| **`enabledSafeBottomMargin`** | <code>boolean</code> | If true, the webView is inset by the bottom system bar (navigation bar) so bottom-anchored content stays reachable. When false (default), the webView can extend behind the navigation bar and apps can use `env(safe-area-inset-*)` for layout, matching the main Capacitor WebView. On pre-API 30 devices with a transparent navigation bar, bottom padding may still be applied when the window lays out behind the navigation bar regardless of this option. | <code>false</code> | 7.13.0 |
|
|
1632
1634
|
| **`enabledSafeTopMargin`** | <code>boolean</code> | If false, the webView will extend behind the status bar for true full-screen immersive content. When true (default), respects the safe area at the top of the screen. Works independently of toolbarType - use for full-screen video players, games, or immersive web apps. On Android, when a toolbar is visible the toolbar itself provides that safe area; with `toolbarType: 'blank'` on Android 15+ the status bar inset is applied to the webView instead. | <code>true</code> | 8.2.0 |
|
|
1633
1635
|
| **`useTopInset`** | <code>boolean</code> | When true, applies the system status bar inset to the top of the WebView on Android even when the window is not edge-to-edge (before Android 15). Keeps the legacy 0px inset by default for apps that handle padding themselves. On Android 15+ this is not needed: without a visible toolbar the status bar inset already follows `enabledSafeTopMargin`. | <code>false</code> | |
|
|
1634
1636
|
| **`enableGooglePaySupport`** | <code>boolean</code> | enableGooglePaySupport: if true, enables support for Google Pay popups and Payment Request API. This fixes OR_BIBED_15 errors by allowing popup windows and configuring Cross-Origin-Opener-Policy. Only enable this if you need Google Pay functionality as it allows popup windows. When enabled: - Allows popup windows for Google Pay authentication - Sets proper CORS headers for Payment Request API - Enables multiple window support in WebView - Configures secure context for payment processing | <code>false</code> | 7.13.0 |
|
|
@@ -20,7 +20,6 @@ import android.util.Log;
|
|
|
20
20
|
import android.webkit.CookieManager;
|
|
21
21
|
import android.webkit.PermissionRequest;
|
|
22
22
|
import android.webkit.WebView;
|
|
23
|
-
import androidx.activity.result.ActivityResult;
|
|
24
23
|
import androidx.activity.result.ActivityResultLauncher;
|
|
25
24
|
import androidx.activity.result.contract.ActivityResultContracts;
|
|
26
25
|
import androidx.annotation.NonNull;
|
|
@@ -63,8 +62,7 @@ import org.json.JSONObject;
|
|
|
63
62
|
@Permission(alias = "camera", strings = { Manifest.permission.CAMERA }),
|
|
64
63
|
@Permission(alias = "microphone", strings = { Manifest.permission.RECORD_AUDIO }),
|
|
65
64
|
@Permission(alias = "storage", strings = { Manifest.permission.READ_EXTERNAL_STORAGE })
|
|
66
|
-
}
|
|
67
|
-
requestCodes = { WebViewDialog.FILE_CHOOSER_REQUEST_CODE }
|
|
65
|
+
}
|
|
68
66
|
)
|
|
69
67
|
public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.PermissionHandler {
|
|
70
68
|
|
|
@@ -108,7 +106,6 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
|
|
|
108
106
|
private boolean currentPermissionNeedsCamera = false;
|
|
109
107
|
private boolean currentPermissionNeedsMicrophone = false;
|
|
110
108
|
|
|
111
|
-
private ActivityResultLauncher<Intent> fileChooserLauncher;
|
|
112
109
|
private ActivityResultLauncher<String[]> webPermissionLauncher;
|
|
113
110
|
private PluginCall pendingCameraPermissionCall;
|
|
114
111
|
|
|
@@ -118,10 +115,6 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
|
|
|
118
115
|
@Override
|
|
119
116
|
public void load() {
|
|
120
117
|
super.load();
|
|
121
|
-
fileChooserLauncher = getActivity().registerForActivityResult(
|
|
122
|
-
new ActivityResultContracts.StartActivityForResult(),
|
|
123
|
-
this::handleFileChooserResult
|
|
124
|
-
);
|
|
125
118
|
webPermissionLauncher = getActivity().registerForActivityResult(
|
|
126
119
|
new ActivityResultContracts.RequestMultiplePermissions(),
|
|
127
120
|
this::handleWebPermissionResult
|
|
@@ -355,6 +348,7 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
|
|
|
355
348
|
);
|
|
356
349
|
dialog.setInstanceId(webViewId);
|
|
357
350
|
dialog.activity = CapgoInAppBrowserPlugin.this.getActivity();
|
|
351
|
+
dialog.bindToHostActivity();
|
|
358
352
|
registerWebView(webViewId, dialog, makeActive);
|
|
359
353
|
return dialog;
|
|
360
354
|
}
|
|
@@ -525,59 +519,6 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
|
|
|
525
519
|
return targetWebViews;
|
|
526
520
|
}
|
|
527
521
|
|
|
528
|
-
private WebViewDialog findFileChooserDialog() {
|
|
529
|
-
if (activeWebViewId != null) {
|
|
530
|
-
WebViewDialog dialog = webViewDialogs.get(activeWebViewId);
|
|
531
|
-
if (dialog != null && dialog.mFilePathCallback != null) {
|
|
532
|
-
return dialog;
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
if (webViewDialog != null && webViewDialog.mFilePathCallback != null) {
|
|
537
|
-
return webViewDialog;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
for (WebViewDialog dialog : webViewDialogs.values()) {
|
|
541
|
-
if (dialog != null && dialog.mFilePathCallback != null) {
|
|
542
|
-
return dialog;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
return null;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
private void handleFileChooserResult(ActivityResult result) {
|
|
550
|
-
WebViewDialog webViewDialog = findFileChooserDialog();
|
|
551
|
-
if (webViewDialog != null && webViewDialog.mFilePathCallback != null) {
|
|
552
|
-
Uri[] results = null;
|
|
553
|
-
Intent data = result.getData();
|
|
554
|
-
|
|
555
|
-
if (result.getResultCode() == Activity.RESULT_OK) {
|
|
556
|
-
// Handle camera capture result
|
|
557
|
-
if (webViewDialog.tempCameraUri != null && (data == null || data.getData() == null)) {
|
|
558
|
-
results = new Uri[] { webViewDialog.tempCameraUri };
|
|
559
|
-
}
|
|
560
|
-
// Handle regular file picker result
|
|
561
|
-
else if (data != null) {
|
|
562
|
-
if (data.getClipData() != null) {
|
|
563
|
-
// Handle multiple files
|
|
564
|
-
int count = data.getClipData().getItemCount();
|
|
565
|
-
results = new Uri[count];
|
|
566
|
-
for (int i = 0; i < count; i++) {
|
|
567
|
-
results[i] = data.getClipData().getItemAt(i).getUri();
|
|
568
|
-
}
|
|
569
|
-
} else if (data.getData() != null) {
|
|
570
|
-
// Handle single file
|
|
571
|
-
results = new Uri[] { data.getData() };
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// Send the result to WebView and clean up
|
|
577
|
-
webViewDialog.completeLegacyFileChooserResult(results);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
|
|
581
522
|
public void handleMicrophonePermissionRequest(PermissionRequest request) {
|
|
582
523
|
if (request == null) {
|
|
583
524
|
return;
|
|
@@ -796,6 +737,16 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
|
|
|
796
737
|
);
|
|
797
738
|
}
|
|
798
739
|
|
|
740
|
+
private CustomTabColorSchemeParams buildCustomTabColorScheme(int colorInt) {
|
|
741
|
+
return applyCustomTabNavigationBarColor(new CustomTabColorSchemeParams.Builder().setToolbarColor(colorInt), colorInt).build();
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
@SuppressWarnings("deprecation")
|
|
745
|
+
private CustomTabColorSchemeParams.Builder applyCustomTabNavigationBarColor(CustomTabColorSchemeParams.Builder builder, int colorInt) {
|
|
746
|
+
// androidx.browser 1.9.0 still exposes only setNavigationBarColor; no non-deprecated replacement.
|
|
747
|
+
return builder.setNavigationBarColor(colorInt);
|
|
748
|
+
}
|
|
749
|
+
|
|
799
750
|
@PluginMethod
|
|
800
751
|
public void open(PluginCall call) {
|
|
801
752
|
String url = call.getString("url");
|
|
@@ -828,10 +779,7 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
|
|
|
828
779
|
if (toolbarColor != null) {
|
|
829
780
|
try {
|
|
830
781
|
int colorInt = Color.parseColor(toolbarColor);
|
|
831
|
-
CustomTabColorSchemeParams colorParams =
|
|
832
|
-
.setToolbarColor(colorInt)
|
|
833
|
-
.setNavigationBarColor(colorInt)
|
|
834
|
-
.build();
|
|
782
|
+
CustomTabColorSchemeParams colorParams = buildCustomTabColorScheme(colorInt);
|
|
835
783
|
builder.setDefaultColorSchemeParams(colorParams);
|
|
836
784
|
builder.setColorSchemeParams(CustomTabsIntent.COLOR_SCHEME_DARK, colorParams);
|
|
837
785
|
} catch (IllegalArgumentException e) {
|
|
@@ -1152,6 +1100,8 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
|
|
|
1152
1100
|
options.setHandleDownloads(Boolean.TRUE.equals(call.getBoolean("handleDownloads", false)));
|
|
1153
1101
|
|
|
1154
1102
|
options.setPersistWebViewData(call.getBoolean("persistWebViewData", true));
|
|
1103
|
+
options.setClearCookiesOnOpen(Boolean.TRUE.equals(call.getBoolean("clearCookiesOnOpen", false)));
|
|
1104
|
+
options.setClearCacheOnOpen(Boolean.TRUE.equals(call.getBoolean("clearCacheOnOpen", false)));
|
|
1155
1105
|
// No-op on Android: CookieManager is already process-global (API parity with iOS).
|
|
1156
1106
|
options.setUseSharedDataStore(Boolean.TRUE.equals(call.getBoolean("useSharedDataStore", false)));
|
|
1157
1107
|
// Set text zoom if specified in options (default is 100)
|
|
@@ -11,7 +11,6 @@ import com.getcapacitor.annotation.Permission;
|
|
|
11
11
|
@Permission(alias = "camera", strings = { Manifest.permission.CAMERA }),
|
|
12
12
|
@Permission(alias = "microphone", strings = { Manifest.permission.RECORD_AUDIO }),
|
|
13
13
|
@Permission(alias = "storage", strings = { Manifest.permission.READ_EXTERNAL_STORAGE })
|
|
14
|
-
}
|
|
15
|
-
requestCodes = { WebViewDialog.FILE_CHOOSER_REQUEST_CODE }
|
|
14
|
+
}
|
|
16
15
|
)
|
|
17
16
|
public class InAppBrowserPlugin extends CapgoInAppBrowserPlugin {}
|
package/android/src/main/java/ee/forgr/capacitor_inappbrowser/OpenTimeBrowsingDataClearSupport.java
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
package ee.forgr.capacitor_inappbrowser;
|
|
2
|
+
|
|
3
|
+
import android.webkit.CookieManager;
|
|
4
|
+
import android.webkit.WebView;
|
|
5
|
+
import java.util.concurrent.CountDownLatch;
|
|
6
|
+
import java.util.concurrent.TimeUnit;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Applies cordova-style open-time cookie/cache clearing before the first navigation.
|
|
10
|
+
*/
|
|
11
|
+
final class OpenTimeBrowsingDataClearSupport {
|
|
12
|
+
|
|
13
|
+
private static final long COOKIE_CLEAR_TIMEOUT_SECONDS = 10;
|
|
14
|
+
|
|
15
|
+
private OpenTimeBrowsingDataClearSupport() {}
|
|
16
|
+
|
|
17
|
+
static void applyBeforeFirstNavigation(
|
|
18
|
+
CookieManager cookieManager,
|
|
19
|
+
WebView webView,
|
|
20
|
+
boolean clearCookiesOnOpen,
|
|
21
|
+
boolean clearCacheOnOpen
|
|
22
|
+
) {
|
|
23
|
+
if (clearCookiesOnOpen) {
|
|
24
|
+
final CountDownLatch latch = new CountDownLatch(1);
|
|
25
|
+
cookieManager.removeAllCookies((value) -> latch.countDown());
|
|
26
|
+
try {
|
|
27
|
+
if (!latch.await(COOKIE_CLEAR_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
|
|
28
|
+
throw new IllegalStateException("Timed out waiting for cookie clear");
|
|
29
|
+
}
|
|
30
|
+
} catch (InterruptedException e) {
|
|
31
|
+
Thread.currentThread().interrupt();
|
|
32
|
+
}
|
|
33
|
+
cookieManager.flush();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (clearCacheOnOpen && webView != null) {
|
|
37
|
+
webView.clearCache(true);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -204,6 +204,8 @@ public class Options {
|
|
|
204
204
|
private boolean captureConsoleLogs = false;
|
|
205
205
|
private boolean handleDownloads = false;
|
|
206
206
|
private boolean persistWebViewData = true;
|
|
207
|
+
private boolean clearCookiesOnOpen = false;
|
|
208
|
+
private boolean clearCacheOnOpen = false;
|
|
207
209
|
/** Accepted for API parity with iOS; Android cookies are already process-global. */
|
|
208
210
|
private boolean useSharedDataStore = false;
|
|
209
211
|
private InvisibilityMode invisibilityMode = InvisibilityMode.AWARE;
|
|
@@ -320,6 +322,22 @@ public class Options {
|
|
|
320
322
|
this.persistWebViewData = persistWebViewData;
|
|
321
323
|
}
|
|
322
324
|
|
|
325
|
+
public boolean getClearCookiesOnOpen() {
|
|
326
|
+
return clearCookiesOnOpen;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
public void setClearCookiesOnOpen(boolean clearCookiesOnOpen) {
|
|
330
|
+
this.clearCookiesOnOpen = clearCookiesOnOpen;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
public boolean getClearCacheOnOpen() {
|
|
334
|
+
return clearCacheOnOpen;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
public void setClearCacheOnOpen(boolean clearCacheOnOpen) {
|
|
338
|
+
this.clearCacheOnOpen = clearCacheOnOpen;
|
|
339
|
+
}
|
|
340
|
+
|
|
323
341
|
public boolean getUseSharedDataStore() {
|
|
324
342
|
return useSharedDataStore;
|
|
325
343
|
}
|
|
@@ -857,6 +875,8 @@ public class Options {
|
|
|
857
875
|
copy.setBlockedHosts(new ArrayList<>(getBlockedHosts()));
|
|
858
876
|
copy.setWidth(width);
|
|
859
877
|
copy.setPersistWebViewData(persistWebViewData);
|
|
878
|
+
copy.setClearCookiesOnOpen(clearCookiesOnOpen);
|
|
879
|
+
copy.setClearCacheOnOpen(clearCacheOnOpen);
|
|
860
880
|
copy.setUseSharedDataStore(useSharedDataStore);
|
|
861
881
|
copy.setHeight(height);
|
|
862
882
|
copy.setToBack(toBack);
|
|
@@ -119,12 +119,32 @@ final class SafeAreaInsetsSupport {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
* Whether the WebView container must be inset for the bottom system bar.
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
122
|
+
* Whether the WebView container must be inset for the bottom system bar. Follows the
|
|
123
|
+
* enabledSafeBottomMargin opt-in on all API levels, including Android 15+ edge-to-edge windows,
|
|
124
|
+
* so disabling the flag matches Capacitor full-screen behaviour (#672). Pre-API 30 windows that
|
|
125
|
+
* lay out behind a transparent navigation bar still need the inset regardless of the opt-in.
|
|
126
126
|
*/
|
|
127
|
+
static boolean shouldInsetBottomForContainer(boolean enabledSafeBottomMargin, boolean isEdgeToEdge, boolean layoutBehindNavigationBar) {
|
|
128
|
+
return enabledSafeBottomMargin || layoutBehindNavigationBar;
|
|
129
|
+
}
|
|
130
|
+
|
|
127
131
|
static boolean shouldInsetBottomForContainer(boolean enabledSafeBottomMargin, boolean isEdgeToEdge) {
|
|
128
|
-
return enabledSafeBottomMargin
|
|
132
|
+
return shouldInsetBottomForContainer(enabledSafeBottomMargin, isEdgeToEdge, false);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* CSS custom-property inset for the top safe area. Only published when enabledSafeTopMargin is
|
|
137
|
+
* on; otherwise apps rely on env(safe-area-inset-*) like the main Capacitor WebView (#672).
|
|
138
|
+
*/
|
|
139
|
+
static int cssTopInset(boolean enabledSafeTopMargin, int containerTopPadding) {
|
|
140
|
+
return enabledSafeTopMargin ? Math.max(0, containerTopPadding) : 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* CSS custom-property inset for the bottom safe area. Only published when enabledSafeBottomMargin
|
|
145
|
+
* is on; container padding and env() insets are handled separately (#672).
|
|
146
|
+
*/
|
|
147
|
+
static int cssBottomInset(boolean enabledSafeBottomMargin, int safeBottomInset) {
|
|
148
|
+
return enabledSafeBottomMargin ? Math.max(0, safeBottomInset) : 0;
|
|
129
149
|
}
|
|
130
150
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
package ee.forgr.capacitor_inappbrowser;
|
|
2
|
+
|
|
3
|
+
import android.graphics.Color;
|
|
4
|
+
import android.os.Build;
|
|
5
|
+
import android.view.View;
|
|
6
|
+
import android.view.Window;
|
|
7
|
+
import android.view.WindowManager;
|
|
8
|
+
import androidx.core.view.WindowCompat;
|
|
9
|
+
import androidx.core.view.WindowInsetsControllerCompat;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Centralizes window chrome decisions so WebViewDialog can avoid deprecated system UI APIs on
|
|
13
|
+
* Android 15+ while keeping API 24–34 behavior unchanged.
|
|
14
|
+
*/
|
|
15
|
+
final class SystemUiChromeSupport {
|
|
16
|
+
|
|
17
|
+
static final int EDGE_TO_EDGE_SDK = Build.VERSION_CODES.VANILLA_ICE_CREAM;
|
|
18
|
+
|
|
19
|
+
private SystemUiChromeSupport() {}
|
|
20
|
+
|
|
21
|
+
static boolean requiresEdgeToEdgeChrome(int sdkInt) {
|
|
22
|
+
return sdkInt >= EDGE_TO_EDGE_SDK;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static boolean shouldApplyLegacySystemBarColors(int sdkInt) {
|
|
26
|
+
return sdkInt < EDGE_TO_EDGE_SDK;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static boolean shouldUsePreApi30LayoutFlags(int sdkInt) {
|
|
30
|
+
return sdkInt < Build.VERSION_CODES.R;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static boolean usesLayoutBehindNavigationBar(int sdkInt, boolean edgeToEdge) {
|
|
34
|
+
return !edgeToEdge && shouldUsePreApi30LayoutFlags(sdkInt);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static void setDecorFitsSystemWindows(Window window, boolean decorFitsSystemWindows) {
|
|
38
|
+
if (window == null) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
WindowCompat.setDecorFitsSystemWindows(window, decorFitsSystemWindows);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static void prepareInitialDialogWindow(Window window) {
|
|
45
|
+
if (window == null) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
|
50
|
+
if (shouldApplyLegacySystemBarColors(Build.VERSION.SDK_INT)) {
|
|
51
|
+
applyLegacySystemBarColors(window, Color.TRANSPARENT, null);
|
|
52
|
+
clearLegacyTranslucentStatusFlag(window);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static void applyDialogWindowChrome(
|
|
57
|
+
Window window,
|
|
58
|
+
View decorView,
|
|
59
|
+
boolean edgeToEdge,
|
|
60
|
+
Integer statusBarColor,
|
|
61
|
+
Integer navigationBarColor,
|
|
62
|
+
Boolean lightStatusBars
|
|
63
|
+
) {
|
|
64
|
+
if (window == null || decorView == null) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (edgeToEdge) {
|
|
69
|
+
setDecorFitsSystemWindows(window, false);
|
|
70
|
+
} else if (!shouldUsePreApi30LayoutFlags(Build.VERSION.SDK_INT)) {
|
|
71
|
+
setDecorFitsSystemWindows(window, true);
|
|
72
|
+
applyLegacySystemBarColors(window, statusBarColor, navigationBarColor);
|
|
73
|
+
} else {
|
|
74
|
+
applyPreApi30LayoutBehindNavigationBar(decorView);
|
|
75
|
+
applyLegacySystemBarColors(window, statusBarColor, navigationBarColor);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
WindowInsetsControllerCompat controller = new WindowInsetsControllerCompat(window, decorView);
|
|
79
|
+
if (lightStatusBars != null) {
|
|
80
|
+
controller.setAppearanceLightStatusBars(lightStatusBars);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@SuppressWarnings("deprecation")
|
|
85
|
+
private static void applyPreApi30LayoutBehindNavigationBar(View decorView) {
|
|
86
|
+
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@SuppressWarnings("deprecation")
|
|
90
|
+
static void applyLegacySystemBarColors(Window window, Integer statusBarColor, Integer navigationBarColor) {
|
|
91
|
+
if (window == null || !shouldApplyLegacySystemBarColors(Build.VERSION.SDK_INT)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (statusBarColor != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
96
|
+
window.setStatusBarColor(statusBarColor);
|
|
97
|
+
}
|
|
98
|
+
if (navigationBarColor != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
99
|
+
window.setNavigationBarColor(navigationBarColor);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@SuppressWarnings("deprecation")
|
|
104
|
+
private static void clearLegacyTranslucentStatusFlag(Window window) {
|
|
105
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
106
|
+
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewCustomFullscreenSupport.java
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
package ee.forgr.capacitor_inappbrowser;
|
|
2
|
+
|
|
3
|
+
import android.view.View;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Testable helpers for HTML5/iframe fullscreen routed through WebChromeClient custom views
|
|
7
|
+
* (e.g. embedded YouTube fullscreen).
|
|
8
|
+
*/
|
|
9
|
+
final class WebViewCustomFullscreenSupport {
|
|
10
|
+
|
|
11
|
+
private WebViewCustomFullscreenSupport() {}
|
|
12
|
+
|
|
13
|
+
static boolean isCustomFullscreenActive(Object customFullscreenView) {
|
|
14
|
+
return customFullscreenView != null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static boolean shouldRejectDuplicateShow(boolean isActive) {
|
|
18
|
+
return isActive;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static boolean shouldConsumeBackPress(boolean isActive) {
|
|
22
|
+
return isActive;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@SuppressWarnings("deprecation")
|
|
26
|
+
static int immersiveFullscreenSystemUiVisibility() {
|
|
27
|
+
return (
|
|
28
|
+
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
|
29
|
+
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
|
30
|
+
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
|
31
|
+
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
|
32
|
+
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
|
33
|
+
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static boolean shouldUseHostActivityWindow(boolean backLayerActive) {
|
|
38
|
+
return backLayerActive;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static boolean shouldRegisterHostBackHandler(boolean backLayerActive) {
|
|
42
|
+
return backLayerActive;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@SuppressWarnings("deprecation")
|
|
46
|
+
static int restoredSystemUiVisibility() {
|
|
47
|
+
return View.SYSTEM_UI_FLAG_VISIBLE;
|
|
48
|
+
}
|
|
49
|
+
}
|