Merge pull request #108287 from syntaxerror247/fix-virtual-keyboard-height

Fix immersive mode and virtual keyboard height issue on Android
This commit is contained in:
Thaddeus Crews 2025-07-18 11:05:19 -05:00
commit 71a9948157
No known key found for this signature in database
GPG key ID: 8C6E5FEB5FC03CCC
3 changed files with 39 additions and 9 deletions

View file

@ -2059,6 +2059,7 @@
<return type="int" /> <return type="int" />
<description> <description>
Returns the on-screen keyboard's height in pixels. Returns 0 if there is no keyboard or if it is currently hidden. Returns the on-screen keyboard's height in pixels. Returns 0 if there is no keyboard or if it is currently hidden.
[b]Note:[/b] On Android 7 and 8, the keyboard height may return 0 the first time the keyboard is opened in non-immersive mode. This behavior does not occur in immersive mode.
</description> </description>
</method> </method>
<method name="virtual_keyboard_hide"> <method name="virtual_keyboard_hide">

View file

@ -37,6 +37,7 @@
android:excludeFromRecents="false" android:excludeFromRecents="false"
android:exported="true" android:exported="true"
android:screenOrientation="landscape" android:screenOrientation="landscape"
android:windowSoftInputMode="adjustResize"
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode" android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
android:resizeableActivity="false" android:resizeableActivity="false"
tools:ignore="UnusedAttribute" > tools:ignore="UnusedAttribute" >

View file

@ -38,6 +38,7 @@ import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Color import android.graphics.Color
import android.graphics.Rect
import android.hardware.Sensor import android.hardware.Sensor
import android.hardware.SensorManager import android.hardware.SensorManager
import android.os.* import android.os.*
@ -357,16 +358,29 @@ class Godot private constructor(val context: Context) {
if (enabled) { if (enabled) {
ViewCompat.setOnApplyWindowInsetsListener(rootView, null) ViewCompat.setOnApplyWindowInsetsListener(rootView, null)
rootView.setPadding(0, 0, 0, 0) rootView.setPadding(0, 0, 0, 0)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
}
} else { } else {
if (rootView.rootWindowInsets != null) { if (rootView.rootWindowInsets != null) {
val windowInsets = WindowInsetsCompat.toWindowInsetsCompat(rootView.rootWindowInsets) if (!useImmersive.get() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)) {
val insets = windowInsets.getInsets(getInsetType()) val windowInsets = WindowInsetsCompat.toWindowInsetsCompat(rootView.rootWindowInsets)
rootView.setPadding(insets.left, insets.top, insets.right, insets.bottom) val insets = windowInsets.getInsets(getInsetType())
rootView.setPadding(insets.left, insets.top, insets.right, insets.bottom)
}
} }
ViewCompat.setOnApplyWindowInsetsListener(rootView) { v: View, insets: WindowInsetsCompat -> ViewCompat.setOnApplyWindowInsetsListener(rootView) { v: View, insets: WindowInsetsCompat ->
val windowInsets = insets.getInsets(getInsetType()) v.post {
v.setPadding(windowInsets.left, windowInsets.top, windowInsets.right, windowInsets.bottom) if (useImmersive.get() && Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
// Fixes issue where padding remained visible in immersive mode on some devices.
v.setPadding(0, 0, 0, 0)
} else {
val windowInsets = insets.getInsets(getInsetType())
v.setPadding(windowInsets.left, windowInsets.top, windowInsets.right, windowInsets.bottom)
}
}
WindowInsetsCompat.CONSUMED WindowInsetsCompat.CONSUMED
} }
} }
@ -531,12 +545,18 @@ class Godot private constructor(val context: Context) {
startBottom = ViewCompat.getRootWindowInsets(topView)?.getInsets(WindowInsetsCompat.Type.ime())?.bottom ?: 0 startBottom = ViewCompat.getRootWindowInsets(topView)?.getInsets(WindowInsetsCompat.Type.ime())?.bottom ?: 0
} }
override fun onStart(animation: WindowInsetsAnimationCompat, bounds: WindowInsetsAnimationCompat.BoundsCompat): WindowInsetsAnimationCompat.BoundsCompat { override fun onStart(
animation: WindowInsetsAnimationCompat,
bounds: WindowInsetsAnimationCompat.BoundsCompat
): WindowInsetsAnimationCompat.BoundsCompat {
endBottom = ViewCompat.getRootWindowInsets(topView)?.getInsets(WindowInsetsCompat.Type.ime())?.bottom ?: 0 endBottom = ViewCompat.getRootWindowInsets(topView)?.getInsets(WindowInsetsCompat.Type.ime())?.bottom ?: 0
return bounds return bounds
} }
override fun onProgress(windowInsets: WindowInsetsCompat, animationsList: List<WindowInsetsAnimationCompat>): WindowInsetsCompat { override fun onProgress(
windowInsets: WindowInsetsCompat,
animationsList: List<WindowInsetsAnimationCompat>
): WindowInsetsCompat {
// Find the IME animation. // Find the IME animation.
var imeAnimation: WindowInsetsAnimationCompat? = null var imeAnimation: WindowInsetsAnimationCompat? = null
for (animation in animationsList) { for (animation in animationsList) {
@ -551,12 +571,20 @@ class Godot private constructor(val context: Context) {
val interpolatedFraction = imeAnimation.interpolatedFraction val interpolatedFraction = imeAnimation.interpolatedFraction
// Linear interpolation between start and end values. // Linear interpolation between start and end values.
val keyboardHeight = startBottom * (1.0f - interpolatedFraction) + endBottom * interpolatedFraction val keyboardHeight = startBottom * (1.0f - interpolatedFraction) + endBottom * interpolatedFraction
GodotLib.setVirtualKeyboardHeight(keyboardHeight.toInt()) val finalHeight = maxOf(keyboardHeight.toInt() - topView.rootView.paddingBottom, 0)
GodotLib.setVirtualKeyboardHeight(finalHeight)
} }
return windowInsets return windowInsets
} }
override fun onEnd(animation: WindowInsetsAnimationCompat) {} override fun onEnd(animation: WindowInsetsAnimationCompat) {
// Fixes issue on Android 7 and 8 where immersive mode gets auto disabled after the keyboard is hidden.
if (useImmersive.get() && Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
runOnHostThread {
enableImmersiveMode(true, true)
}
}
}
}) })
renderView?.queueOnRenderThread { renderView?.queueOnRenderThread {