Skip to content

Commit 17be74c

Browse files
author
Capacitor+ Bot
committed
chore: sync upstream PR ionic-team#8268 from @theproducer
2 parents 3852542 + 4aa5930 commit 17be74c

File tree

3 files changed

+105
-48
lines changed

3 files changed

+105
-48
lines changed

android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java

Lines changed: 88 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
package com.getcapacitor.plugin;
22

3-
import android.content.pm.PackageInfo;
43
import android.content.res.Configuration;
4+
import android.os.Build;
55
import android.view.View;
6+
import android.view.ViewGroup;
67
import android.view.Window;
78
import androidx.core.graphics.Insets;
89
import androidx.core.view.ViewCompat;
910
import androidx.core.view.WindowCompat;
1011
import androidx.core.view.WindowInsetsCompat;
1112
import androidx.core.view.WindowInsetsControllerCompat;
12-
import androidx.webkit.WebViewCompat;
1313
import com.getcapacitor.Plugin;
1414
import com.getcapacitor.PluginCall;
1515
import com.getcapacitor.PluginMethod;
1616
import com.getcapacitor.annotation.CapacitorPlugin;
1717
import java.util.Locale;
18-
import java.util.regex.Matcher;
19-
import java.util.regex.Pattern;
2018

2119
@CapacitorPlugin
2220
public class SystemBars extends Plugin {
@@ -27,6 +25,11 @@ public class SystemBars extends Plugin {
2725
static final String BAR_STATUS_BAR = "StatusBar";
2826
static final String BAR_GESTURE_BAR = "NavigationBar";
2927

28+
static final String INSETS_HANDLING_BOTH = "both";
29+
static final String INSETS_HANDLING_CSS = "css";
30+
static final String INSETS_HANDLING_MARGINS = "margins";
31+
static final String INSETS_HANDLING_DISABLE = "disable";
32+
3033
static final String viewportMetaJSFunction = """
3134
function capacitorSystemBarsCheckMetaViewport() {
3235
const meta = document.querySelectorAll("meta[name=viewport]");
@@ -41,38 +44,51 @@ function capacitorSystemBarsCheckMetaViewport() {
4144
capacitorSystemBarsCheckMetaViewport();
4245
""";
4346

47+
private boolean useCSSVariables = true;
48+
private boolean useViewMargins = true;
49+
4450
@Override
4551
public void load() {
4652
super.load();
4753
initSystemBars();
4854
}
4955

50-
private boolean hasFixedWebView() {
51-
PackageInfo packageInfo = WebViewCompat.getCurrentWebViewPackage(bridge.getContext());
52-
Pattern pattern = Pattern.compile("(\\d+)");
53-
Matcher matcher = pattern.matcher(packageInfo.versionName);
54-
55-
if (!matcher.find()) {
56-
return false;
57-
}
58-
59-
String majorVersionStr = matcher.group(0);
60-
int majorVersion = Integer.parseInt(majorVersionStr);
61-
62-
return majorVersion >= 140;
63-
}
64-
6556
private void initSystemBars() {
6657
String style = getConfig().getString("style", STYLE_DEFAULT).toUpperCase(Locale.US);
6758
boolean hidden = getConfig().getBoolean("hidden", false);
68-
boolean disableCSSInsets = getConfig().getBoolean("disableInsets", false);
6959

70-
this.bridge.getWebView().evaluateJavascript(viewportMetaJSFunction, (res) -> {
71-
boolean hasMetaViewportCover = res.equals("true");
72-
if (!disableCSSInsets) {
73-
setupSafeAreaInsets(this.hasFixedWebView(), hasMetaViewportCover);
60+
String insetsHandling = getConfig().getString("insetsHandling", "both");
61+
switch (insetsHandling) {
62+
case INSETS_HANDLING_BOTH -> {
63+
useViewMargins = true;
64+
useCSSVariables = true;
7465
}
75-
});
66+
case INSETS_HANDLING_CSS -> {
67+
useViewMargins = false;
68+
useCSSVariables = true;
69+
}
70+
case INSETS_HANDLING_MARGINS -> {
71+
useViewMargins = true;
72+
useCSSVariables = false;
73+
}
74+
case INSETS_HANDLING_DISABLE -> {
75+
useViewMargins = false;
76+
useCSSVariables = false;
77+
}
78+
}
79+
80+
getBridge()
81+
.getWebView()
82+
.post(() -> {
83+
this.bridge.getWebView().evaluateJavascript(viewportMetaJSFunction, (res) -> {
84+
boolean hasMetaViewportCover = res.equals("true");
85+
86+
useViewMargins = !hasMetaViewportCover && useViewMargins;
87+
88+
initWindowInsetsListener();
89+
initSafeAreaInsets();
90+
});
91+
});
7692

7793
getBridge().executeOnMainThread(() -> {
7894
setStyle(style, "");
@@ -116,30 +132,62 @@ public void setAnimation(final PluginCall call) {
116132
call.resolve();
117133
}
118134

119-
private void setupSafeAreaInsets(boolean hasFixedWebView, boolean hasMetaViewportCover) {
120-
ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> {
121-
if (hasFixedWebView && hasMetaViewportCover) {
122-
return insets;
123-
}
135+
private Insets calcSafeAreaInsets(WindowInsetsCompat insets) {
136+
Insets safeArea = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
137+
Insets imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime());
138+
boolean keyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
139+
140+
int bottomInsets = safeArea.bottom;
141+
142+
if (keyboardVisible) {
143+
// When https://issues.chromium.org/issues/457682720 is fixed and released,
144+
// add behind a WebView version check
145+
bottomInsets = imeInsets.bottom - bottomInsets;
146+
}
147+
148+
return Insets.of(safeArea.left, safeArea.top, safeArea.right, bottomInsets);
149+
}
150+
151+
private void initSafeAreaInsets() {
152+
View v = (View) this.getBridge().getWebView().getParent();
153+
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(v);
154+
Insets safeAreaInsets = calcSafeAreaInsets(insets);
124155

125-
Insets safeArea = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
126-
Insets imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime());
127-
boolean keyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
156+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && useCSSVariables) {
157+
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
158+
}
159+
160+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && useViewMargins) {
161+
setSafeAreaMargins(v, safeAreaInsets);
162+
}
163+
}
128164

129-
int bottomInsets = safeArea.bottom;
165+
private void initWindowInsetsListener() {
166+
ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> {
167+
Insets safeAreaInsets = calcSafeAreaInsets(insets);
130168

131-
if (keyboardVisible) {
132-
// When https://issues.chromium.org/issues/457682720 is fixed and released,
133-
// add behind a WebView version check
134-
bottomInsets = imeInsets.bottom - bottomInsets;
169+
if (useCSSVariables) {
170+
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
135171
}
136172

137-
injectSafeAreaCSS(safeArea.top, safeArea.right, bottomInsets, safeArea.left);
173+
if (useViewMargins) {
174+
setSafeAreaMargins(v, safeAreaInsets);
175+
return WindowInsetsCompat.CONSUMED;
176+
}
138177

139-
return WindowInsetsCompat.CONSUMED;
178+
return insets;
140179
});
141180
}
142181

182+
private void setSafeAreaMargins(View v, Insets insets) {
183+
ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
184+
mlp.leftMargin = insets.left;
185+
mlp.bottomMargin = insets.bottom;
186+
mlp.rightMargin = insets.right;
187+
mlp.topMargin = insets.top;
188+
v.setLayoutParams(mlp);
189+
}
190+
143191
private void injectSafeAreaCSS(int top, int right, int bottom, int left) {
144192
// Convert pixels to density-independent pixels
145193
float density = getActivity().getResources().getDisplayMetrics().density;

cli/src/declarations.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -707,11 +707,21 @@ export interface PluginsConfig {
707707
*/
708708
SystemBars?: {
709709
/**
710-
* Disables the injection of device css insets into the web view.
710+
* Specifies how to handle problematic insets on Android.
711711
*
712-
* @default false
712+
* This option is only supported on Android.
713+
*
714+
* `both` = Injects CSS variables (`--safe-area-inset-*`) into the document in addition to shrinking the webview into the device safe areas. The margin shrinking is ignored if the document has a meta viewport tag with `viewport-fit=cover`.
715+
*
716+
* `css` = Injects CSS variables (`--safe-area-inset-*`) containing correct safe area inset values into the webview.
717+
*
718+
* `margins` = Shrinks the webview into the device safe area using view margins. This is ignored if the document has a meta viewport tag with `viewport-fit=cover`.
719+
*
720+
* `disable` = Disable all inset handling.
721+
*
722+
* @default "both"
713723
*/
714-
disableInsets?: boolean;
724+
insetsHandling?: 'both' | 'css' | 'margins' | 'disable';
715725
/**
716726
* The style of the text and icons of the system bars.
717727
*

core/system-bars.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ html {
3232
padding-right: var(--safe-area-inset-right, env(safe-area-inset-right, 0px));
3333
}
3434
```
35-
36-
To disable the inset variable injections, set the configuration setting `disableInsets` to `true`.
35+
In Android 15+'s default edge-to-edge environment, if your web application does not opt-in to safe area handling via the meta viewport tag (`viewport-fit=cover`), this plugin will automatically apply native padding to the WebView. This ensures your application fits within the safe areas without being obscured by system bars. To control this behavior, use the `insetsHandling` configuration setting.
3736

3837
## Example
3938

@@ -72,7 +71,7 @@ const setStatusBarAnimation = async () => {
7271
## Configuration
7372
| Prop | Type | Description | Default |
7473
| ------------- | -------------------- | ------------------------------------------------------------------------- | ------------------ |
75-
| **`disableInsets`** | <code>boolean</code> | Disables the injection of device css insets into the webview. This option is only supported on Android. | <code>false</code> |
74+
| **`insetsHandling`** | <code>string</code> | Specifies how to handle problematic insets on Android. This option is only supported on Android.<br>`both` = Injects CSS variables (`--safe-area-inset-*`) into the document in addition to shrinking the webview into the device safe areas. The margin shrinking is ignored if the document has a meta viewport tag with viewport-fit=cover.<br>`css` = Injects CSS variables (`--safe-area-inset-*`) containing correct safe area inset values into the webview.<br>`margins` = Shrinks the webview into the device safe area using view margins. This is ignored if the document has a meta viewport tag with viewport-fit=cover.<br>`disable` = Disable all inset handling. | <code>both</code> |
7675
| **`style`** | <code>string</code> | The style of the text and icons of the system bars. | <code>DEFAULT</code> |
7776
| **`hidden`** | <code>boolean</code> | Hide the system bars on start. | <code>false</code> |
7877
| **`animation`** | <code>string</code> | The type of status bar animation used when showing or hiding. This option is only supported on iOS. | <code>FADE</code> |
@@ -86,7 +85,7 @@ In `capacitor.config.json`:
8685
{
8786
"plugins": {
8887
"SystemBars": {
89-
"disableInsets": true,
88+
"insetsHandling": "margins",
9089
"style": "DARK",
9190
"hidden": false,
9291
"animation": "NONE"
@@ -103,7 +102,7 @@ import { CapacitorConfig } from '@capacitor/cli';
103102
const config: CapacitorConfig = {
104103
plugins: {
105104
SystemBars: {
106-
disableInsets: true,
105+
insetsHandling: "margins",
107106
style: "DARK",
108107
hidden: false,
109108
animation: "NONE"

0 commit comments

Comments
 (0)