77import android .os .Build ;
88import android .util .TypedValue ;
99import android .view .View ;
10- import android .view .ViewGroup ;
1110import android .view .Window ;
1211import android .webkit .JavascriptInterface ;
1312import android .webkit .WebView ;
@@ -36,6 +35,11 @@ public class SystemBars extends Plugin {
3635 static final String INSETS_HANDLING_CSS = "css" ;
3736 static final String INSETS_HANDLING_DISABLE = "disable" ;
3837
38+ // https://issues.chromium.org/issues/40699457
39+ private static final int WEBVIEW_VERSION_WITH_SAFE_AREA_FIX = 140 ;
40+ // https://issues.chromium.org/issues/457682720
41+ private static final int WEBVIEW_VERSION_WITH_SAFE_AREA_KEYBOARD_FIX = 144 ;
42+
3943 static final String viewportMetaJSFunction = """
4044 function capacitorSystemBarsCheckMetaViewport() {
4145 const meta = document.querySelectorAll("meta[name=viewport]");
@@ -94,7 +98,7 @@ private void initSystemBars() {
9498 }
9599
96100 initWindowInsetsListener ();
97- initSafeAreaInsets ();
101+ initSafeAreaCSSVariables ();
98102
99103 getBridge ().executeOnMainThread (() -> {
100104 setStyle (style , "" );
@@ -157,7 +161,7 @@ private Insets calcSafeAreaInsets(WindowInsetsCompat insets) {
157161 return Insets .of (safeArea .left , safeArea .top , safeArea .right , safeArea .bottom );
158162 }
159163
160- private void initSafeAreaInsets () {
164+ private void initSafeAreaCSSVariables () {
161165 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .VANILLA_ICE_CREAM && insetHandlingEnabled ) {
162166 View v = (View ) this .getBridge ().getWebView ().getParent ();
163167 WindowInsetsCompat insets = ViewCompat .getRootWindowInsets (v );
@@ -169,41 +173,57 @@ private void initSafeAreaInsets() {
169173 }
170174
171175 private void initWindowInsetsListener () {
172- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .VANILLA_ICE_CREAM && insetHandlingEnabled ) {
173- ViewCompat .setOnApplyWindowInsetsListener ((View ) getBridge ().getWebView ().getParent (), (v , insets ) -> {
174- boolean hasBrokenWebViewVersion = getWebViewMajorVersion () <= 139 ;
176+ ViewCompat .setOnApplyWindowInsetsListener ((View ) getBridge ().getWebView ().getParent (), (v , insets ) -> {
177+ boolean shouldPassthroughInsets = getWebViewMajorVersion () >= WEBVIEW_VERSION_WITH_SAFE_AREA_FIX && hasViewportCover ;
178+
179+ Insets systemBarsInsets = insets .getInsets (WindowInsetsCompat .Type .systemBars () | WindowInsetsCompat .Type .displayCutout ());
180+ Insets imeInsets = insets .getInsets (WindowInsetsCompat .Type .ime ());
181+ boolean keyboardVisible = insets .isVisible (WindowInsetsCompat .Type .ime ());
175182
176- if (hasViewportCover ) {
183+ if (shouldPassthroughInsets ) {
184+ // We need to correct for a possible shown IME
185+ v .setPadding (0 , 0 , 0 , keyboardVisible ? imeInsets .bottom : 0 );
186+
187+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled ) {
177188 Insets safeAreaInsets = calcSafeAreaInsets (insets );
178189 injectSafeAreaCSS (safeAreaInsets .top , safeAreaInsets .right , safeAreaInsets .bottom , safeAreaInsets .left );
179190 }
180191
181- if (hasBrokenWebViewVersion ) {
182- if (hasViewportCover && v .hasWindowFocus () && v .isShown ()) {
183- boolean keyboardVisible = insets .isVisible (WindowInsetsCompat .Type .ime ());
184- if (keyboardVisible ) {
185- Insets imeInsets = insets .getInsets (WindowInsetsCompat .Type .ime ());
186- setViewMargins (v , Insets .of (0 , 0 , 0 , imeInsets .bottom ));
187- } else {
188- setViewMargins (v , Insets .NONE );
189- }
190-
191- return WindowInsetsCompat .CONSUMED ;
192- }
193- }
192+ return new WindowInsetsCompat .Builder (insets )
193+ .setInsets (
194+ WindowInsetsCompat .Type .systemBars () | WindowInsetsCompat .Type .displayCutout (),
195+ Insets .of (
196+ systemBarsInsets .left ,
197+ systemBarsInsets .top ,
198+ systemBarsInsets .right ,
199+ getBottomInset (systemBarsInsets , keyboardVisible )
200+ )
201+ )
202+ .build ();
203+ }
194204
195- return insets ;
196- });
197- }
198- }
205+ // We need to correct for a possible shown IME
206+ v .setPadding (
207+ systemBarsInsets .left ,
208+ systemBarsInsets .top ,
209+ systemBarsInsets .right ,
210+ keyboardVisible ? imeInsets .bottom : systemBarsInsets .bottom
211+ );
212+
213+ // Returning `WindowInsetsCompat.CONSUMED` breaks recalculation of safe area insets
214+ // So we have to explicitly set insets to `0`
215+ // See: https://issues.chromium.org/issues/461332423
216+ WindowInsetsCompat newInsets = new WindowInsetsCompat .Builder (insets )
217+ .setInsets (WindowInsetsCompat .Type .systemBars () | WindowInsetsCompat .Type .displayCutout (), Insets .of (0 , 0 , 0 , 0 ))
218+ .build ();
219+
220+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled ) {
221+ Insets safeAreaInsets = calcSafeAreaInsets (newInsets );
222+ injectSafeAreaCSS (safeAreaInsets .top , safeAreaInsets .right , safeAreaInsets .bottom , safeAreaInsets .left );
223+ }
199224
200- private void setViewMargins (View v , Insets insets ) {
201- ViewGroup .MarginLayoutParams mlp = (ViewGroup .MarginLayoutParams ) v .getLayoutParams ();
202- mlp .leftMargin = insets .left ;
203- mlp .bottomMargin = insets .bottom ;
204- mlp .rightMargin = insets .right ;
205- mlp .topMargin = insets .top ;
206- v .setLayoutParams (mlp );
225+ return newInsets ;
226+ });
207227 }
208228
209229 private void injectSafeAreaCSS (int top , int right , int bottom , int left ) {
@@ -305,4 +325,18 @@ private Integer getWebViewMajorVersion() {
305325
306326 return 0 ;
307327 }
328+
329+ private int getBottomInset (Insets systemBarsInsets , boolean keyboardVisible ) {
330+ if (getWebViewMajorVersion () < WEBVIEW_VERSION_WITH_SAFE_AREA_KEYBOARD_FIX ) {
331+ // This is a workaround for webview versions that have a bug
332+ // that causes the bottom inset to be incorrect if the IME is visible
333+ // See: https://issues.chromium.org/issues/457682720
334+
335+ if (keyboardVisible ) {
336+ return 0 ;
337+ }
338+ }
339+
340+ return systemBarsInsets .bottom ;
341+ }
308342}
0 commit comments