11package com .getcapacitor .plugin ;
22
3- import android .content .pm .PackageInfo ;
43import android .content .res .Configuration ;
4+ import android .os .Build ;
55import android .view .View ;
6+ import android .view .ViewGroup ;
67import android .view .Window ;
8+ import android .webkit .JavascriptInterface ;
9+ import android .webkit .WebView ;
710import androidx .core .graphics .Insets ;
811import androidx .core .view .ViewCompat ;
912import androidx .core .view .WindowCompat ;
1013import androidx .core .view .WindowInsetsCompat ;
1114import androidx .core .view .WindowInsetsControllerCompat ;
12- import androidx .webkit .WebViewCompat ;
1315import com .getcapacitor .Plugin ;
1416import com .getcapacitor .PluginCall ;
1517import com .getcapacitor .PluginMethod ;
18+ import com .getcapacitor .WebViewListener ;
1619import com .getcapacitor .annotation .CapacitorPlugin ;
1720import java .util .Locale ;
18- import java .util .regex .Matcher ;
19- import java .util .regex .Pattern ;
2021
2122@ CapacitorPlugin
2223public class SystemBars extends Plugin {
@@ -27,6 +28,9 @@ public class SystemBars extends Plugin {
2728 static final String BAR_STATUS_BAR = "StatusBar" ;
2829 static final String BAR_GESTURE_BAR = "NavigationBar" ;
2930
31+ static final String INSETS_HANDLING_CSS = "css" ;
32+ static final String INSETS_HANDLING_DISABLE = "disable" ;
33+
3034 static final String viewportMetaJSFunction = """
3135 function capacitorSystemBarsCheckMetaViewport() {
3236 const meta = document.querySelectorAll("meta[name=viewport]");
@@ -37,42 +41,52 @@ function capacitorSystemBarsCheckMetaViewport() {
3741 const metaContent = meta[meta.length - 1].content;
3842 return metaContent.includes("viewport-fit=cover");
3943 }
40-
4144 capacitorSystemBarsCheckMetaViewport();
4245 """ ;
4346
47+ private boolean insetHandlingEnabled = true ;
48+ private boolean hasViewportCover = false ;
49+
4450 @ Override
4551 public void load () {
52+ getBridge ().getWebView ().addJavascriptInterface (this , "CapacitorSystemBarsAndroidInterface" );
4653 super .load ();
54+
4755 initSystemBars ();
4856 }
4957
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 );
58+ @ Override
59+ protected void handleOnStart () {
60+ super .handleOnStart ();
61+
62+ this .getBridge ().addWebViewListener (
63+ new WebViewListener () {
64+ @ Override
65+ public void onPageCommitVisible (WebView view , String url ) {
66+ super .onPageCommitVisible (view , url );
67+ getBridge ().getWebView ().requestApplyInsets ();
68+ }
69+ }
70+ );
71+ }
6172
62- return majorVersion >= 140 ;
73+ @ Override
74+ protected void handleOnConfigurationChanged (Configuration newConfig ) {
75+ super .handleOnConfigurationChanged (newConfig );
76+ setStyle (STYLE_DEFAULT , "" );
6377 }
6478
6579 private void initSystemBars () {
6680 String style = getConfig ().getString ("style" , STYLE_DEFAULT ).toUpperCase (Locale .US );
6781 boolean hidden = getConfig ().getBoolean ("hidden" , false );
68- boolean disableCSSInsets = getConfig ().getBoolean ("disableInsets" , false );
6982
70- this .bridge .getWebView ().evaluateJavascript (viewportMetaJSFunction , (res ) -> {
71- boolean hasMetaViewportCover = res .equals ("true" );
72- if (!disableCSSInsets ) {
73- setupSafeAreaInsets (this .hasFixedWebView (), hasMetaViewportCover );
74- }
75- });
83+ String insetsHandling = getConfig ().getString ("insetsHandling" , "css" );
84+ if (insetsHandling .equals (INSETS_HANDLING_DISABLE )) {
85+ insetHandlingEnabled = false ;
86+ }
87+
88+ initWindowInsetsListener ();
89+ initSafeAreaInsets ();
7690
7791 getBridge ().executeOnMainThread (() -> {
7892 setStyle (style , "" );
@@ -116,28 +130,63 @@ public void setAnimation(final PluginCall call) {
116130 call .resolve ();
117131 }
118132
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- }
133+ @ JavascriptInterface
134+ public void onDOMReady () {
135+ getActivity (). runOnUiThread (() -> {
136+ this . bridge . getWebView (). evaluateJavascript ( viewportMetaJSFunction , ( res ) -> {
137+ hasViewportCover = res . equals ( "true" );
124138
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 ());
139+ getBridge ().getWebView ().requestApplyInsets ();
140+ });
141+ });
142+ }
128143
129- int bottomInsets = safeArea .bottom ;
144+ private Insets calcSafeAreaInsets (WindowInsetsCompat insets ) {
145+ Insets safeArea = insets .getInsets (WindowInsetsCompat .Type .systemBars () | WindowInsetsCompat .Type .displayCutout ());
146+ return Insets .of (safeArea .left , safeArea .top , safeArea .right , safeArea .bottom );
147+ }
130148
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 ;
149+ private void initSafeAreaInsets () {
150+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .VANILLA_ICE_CREAM && insetHandlingEnabled ) {
151+ View v = (View ) this .getBridge ().getWebView ().getParent ();
152+ WindowInsetsCompat insets = ViewCompat .getRootWindowInsets (v );
153+ if (insets != null ) {
154+ Insets safeAreaInsets = calcSafeAreaInsets (insets );
155+ injectSafeAreaCSS (safeAreaInsets .top , safeAreaInsets .right , safeAreaInsets .bottom , safeAreaInsets .left );
135156 }
157+ }
158+ }
136159
137- injectSafeAreaCSS (safeArea .top , safeArea .right , bottomInsets , safeArea .left );
160+ private void initWindowInsetsListener () {
161+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .VANILLA_ICE_CREAM && insetHandlingEnabled ) {
162+ ViewCompat .setOnApplyWindowInsetsListener ((View ) getBridge ().getWebView ().getParent (), (v , insets ) -> {
163+ if (hasViewportCover ) {
164+ Insets safeAreaInsets = calcSafeAreaInsets (insets );
165+ boolean keyboardVisible = insets .isVisible (WindowInsetsCompat .Type .ime ());
138166
139- return WindowInsetsCompat .CONSUMED ;
140- });
167+ if (keyboardVisible ) {
168+ Insets imeInsets = insets .getInsets (WindowInsetsCompat .Type .ime ());
169+ setViewMargins (v , Insets .of (0 , 0 , 0 , imeInsets .bottom ));
170+ } else {
171+ setViewMargins (v , Insets .NONE );
172+ }
173+
174+ injectSafeAreaCSS (safeAreaInsets .top , safeAreaInsets .right , safeAreaInsets .bottom , safeAreaInsets .left );
175+ return WindowInsetsCompat .CONSUMED ;
176+ }
177+
178+ return insets ;
179+ });
180+ }
181+ }
182+
183+ private void setViewMargins (View v , Insets insets ) {
184+ ViewGroup .MarginLayoutParams mlp = (ViewGroup .MarginLayoutParams ) v .getLayoutParams ();
185+ mlp .leftMargin = insets .left ;
186+ mlp .bottomMargin = insets .bottom ;
187+ mlp .rightMargin = insets .right ;
188+ mlp .topMargin = insets .top ;
189+ v .setLayoutParams (mlp );
141190 }
142191
143192 private void injectSafeAreaCSS (int top , int right , int bottom , int left ) {
0 commit comments