Compare commits

...

1 Commits

Author SHA1 Message Date
Derek Ross 7d474a0817 Fix Android WebView stuck at half-screen height
The @capacitor/keyboard plugin's possiblyResizeChildOfContent() sets the
CoordinatorLayout's height to a fixed pixel value when the keyboard
appears. On dismiss, an animation callback resets it to MATCH_PARENT.
However, on Android 15+ (API 35+) with edge-to-edge enforced, insets
can change without a keyboard animation (permission dialogs, config
changes, edge-to-edge recalculations), causing the height to be set
but never reset — leaving the WebView stuck at roughly half height.

Add an OnApplyWindowInsetsListener on the ContentFrameLayout that
resets the CoordinatorLayout's height to MATCH_PARENT whenever the
IME is not visible. This fires in the same insets dispatch pass as
the Keyboard plugin's rootView listener, preventing stale heights
from persisting. The listener is placed on the ContentFrameLayout
(not the CoordinatorLayout) to avoid overwriting the SystemBars
plugin's listener.

Closes #220
2026-04-12 13:23:18 -04:00
@@ -8,6 +8,13 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.getcapacitor.BridgeActivity;
@@ -23,6 +30,45 @@ public class MainActivity extends BridgeActivity {
super.onCreate(savedInstanceState);
// Workaround for @capacitor/keyboard plugin intermittently leaving
// the CoordinatorLayout at a fixed pixel height on Android 15+
// (API 35+) with edge-to-edge enforced.
//
// The Keyboard plugin's possiblyResizeChildOfContent() sets the
// CoordinatorLayout's LayoutParams.height to a computed pixel value
// when the keyboard appears. On keyboard dismiss, the animation
// callback resets it to MATCH_PARENT. However, when insets change
// without a keyboard animation (permission dialogs, config changes,
// edge-to-edge recalculations), the plugin's rootView insets
// listener fires with showingKeyboard=true and sets the height,
// but no animation runs to reset it — leaving the WebView stuck
// at roughly half height.
//
// Fix: set an OnApplyWindowInsetsListener on the CoordinatorLayout
// itself. This fires AFTER the Keyboard plugin's listener on the
// rootView (parent dispatches to children). When the IME is not
// visible, we force the height back to MATCH_PARENT, overriding
// any stale value the plugin may have set in the same dispatch.
FrameLayout content = getWindow().getDecorView().findViewById(android.R.id.content);
if (content != null && content.getChildCount() > 0) {
View child = content.getChildAt(0);
// Set the listener on the ContentFrameLayout (parent of the
// CoordinatorLayout) so it fires after the Keyboard plugin's
// rootView listener but before the SystemBars plugin's listener
// on the CoordinatorLayout — avoiding overwriting either one.
ViewCompat.setOnApplyWindowInsetsListener(content, (@NonNull View v, @NonNull WindowInsetsCompat insets) -> {
boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
if (!imeVisible) {
ViewGroup.LayoutParams lp = child.getLayoutParams();
if (lp.height >= 0) {
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
child.requestLayout();
}
}
return insets;
});
}
// Only start the foreground service if the user has opted into
// "persistent" notification style. Default is "push" (no service).
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);