22
33import android .content .Context ;
44import android .graphics .Rect ;
5+ import android .os .Build ;
56import android .util .DisplayMetrics ;
7+ import android .util .TypedValue ;
68import android .view .View ;
79import android .view .Window ;
810import android .view .inputmethod .InputMethodManager ;
1315import androidx .core .view .ViewCompat ;
1416import androidx .core .view .WindowInsetsAnimationCompat ;
1517import androidx .core .view .WindowInsetsCompat ;
18+ import com .getcapacitor .Bridge ;
1619import java .util .List ;
1720
1821public class Keyboard {
@@ -21,6 +24,7 @@ interface KeyboardEventListener {
2124 void onKeyboardEvent (String event , int size );
2225 }
2326
27+ private Bridge bridge ;
2428 private AppCompatActivity activity ;
2529 private View rootView ;
2630 private int usableHeightPrevious ;
@@ -39,6 +43,13 @@ public void setKeyboardEventListener(@Nullable KeyboardEventListener keyboardEve
3943 static final String EVENT_KB_WILL_HIDE = "keyboardWillHide" ;
4044 static final String EVENT_KB_DID_HIDE = "keyboardDidHide" ;
4145
46+ // From android 15 on, we need access to the bridge to get the config to resize the keyboard properly.
47+ public Keyboard (Bridge bridge , boolean resizeOnFullScreen ) {
48+ this (bridge .getActivity (), resizeOnFullScreen );
49+ this .bridge = bridge ;
50+ }
51+
52+ // We may want to deprecate this constructor in the future, but we are keeping it now to keep backward compatibility with cap 7
4253 public Keyboard (AppCompatActivity activity , boolean resizeOnFullScreen ) {
4354 this .activity = activity ;
4455
@@ -131,9 +142,38 @@ private void possiblyResizeChildOfContent(boolean keyboardShown) {
131142 private int computeUsableHeight () {
132143 Rect r = new Rect ();
133144 mChildOfContent .getWindowVisibleDisplayFrame (r );
145+ if (shouldApplyEdgeToEdgeAdjustments ()) {
146+ WindowInsetsCompat insets = ViewCompat .getRootWindowInsets (rootView );
147+ if (insets != null ) {
148+ int systemBars = insets .getInsets (WindowInsetsCompat .Type .systemBars ()).bottom ;
149+ if (systemBars > 0 ) {
150+ return r .bottom + systemBars ;
151+ }
152+ }
153+ }
154+
134155 return isOverlays () ? r .bottom : r .height ();
135156 }
136157
158+ private boolean shouldApplyEdgeToEdgeAdjustments () {
159+ var adjustMarginsForEdgeToEdge = this .bridge == null ? "auto" : this .bridge .getConfig ().adjustMarginsForEdgeToEdge ();
160+ if (adjustMarginsForEdgeToEdge .equals ("force" )) { // Force edge-to-edge adjustments regardless of app settings
161+ return true ;
162+ } else if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .VANILLA_ICE_CREAM && adjustMarginsForEdgeToEdge .equals ("auto" )) { // Auto means that we need to check the app's edge-to-edge preference
163+ TypedValue value = new TypedValue ();
164+ boolean optOutAttributeExists = activity
165+ .getTheme ()
166+ .resolveAttribute (android .R .attr .windowOptOutEdgeToEdgeEnforcement , value , true );
167+
168+ if (!optOutAttributeExists ) { // Default is to apply edge to edge
169+ return true ;
170+ } else {
171+ return value .data == 0 ;
172+ }
173+ }
174+ return false ;
175+ }
176+
137177 @ SuppressWarnings ("deprecation" )
138178 private boolean isOverlays () {
139179 final Window window = activity .getWindow ();
0 commit comments