Android: handle status bar, navigation bar and keyboard insets natively and consume them, so the keyboard no longer shrinks the view twice and content no longer sits under the status bar

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-26 00:12:21 +02:00
co-authored by Claude Opus 5.5
parent aeb6c590db
commit b431428260
3 changed files with 23 additions and 9 deletions
@@ -15,7 +15,7 @@
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBarLaunch"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:windowSoftInputMode="adjustNothing"
android:exported="true">
<intent-filter>
@@ -1,10 +1,13 @@
package de.mrterm.app;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.webkit.WebView;
import androidx.activity.OnBackPressedCallback;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
@@ -25,14 +28,24 @@ public class MainActivity extends BridgeActivity {
}
});
// Edge-to-Edge: Bei offener Tastatur den WebView um die Tastaturhöhe verkleinern,
// damit Terminal und Zusatztasten sichtbar bleiben
// Edge-to-Edge: Abstände für Statusleiste, Navigationsleiste und Tastatur setzt allein diese Ansicht.
// Die Insets werden danach verbraucht, damit WebView/System nicht ein zweites Mal verkleinern.
View parent = (View) web.getParent();
parent.setBackgroundColor(0xFF14161D);
final boolean[] imeOpen = { false };
// Vor Android 11 kommen Tastatur-Insets nicht zuverlässig: dort verkleinert das System das Fenster
final boolean legacyIme = Build.VERSION.SDK_INT < Build.VERSION_CODES.R;
if (legacyIme) getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
ViewCompat.setOnApplyWindowInsetsListener(parent, (v, insets) -> {
int ime = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
v.setPadding(0, 0, 0, ime);
web.evaluateJavascript("window.mrtermIme && window.mrtermIme(" + (ime > 0) + ")", null);
return insets;
Insets bars = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
int ime = legacyIme ? 0 : insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
v.setPadding(bars.left, bars.top, bars.right, Math.max(ime, bars.bottom));
boolean open = ime > 0;
if (open != imeOpen[0]) {
imeOpen[0] = open;
web.evaluateJavascript("window.mrtermIme && window.mrtermIme(" + open + ")", null);
}
return WindowInsetsCompat.CONSUMED;
});
}
}
+3 -2
View File
@@ -13,8 +13,9 @@
--orange: #ffb347;
--radius: 14px;
/* Capacitor setzt --safe-area-inset-* bei Edge-to-Edge; env() als Rückfall */
--top: max(env(safe-area-inset-top, 0px), var(--safe-area-inset-top, 0px));
--bottom: max(env(safe-area-inset-bottom, 0px), var(--safe-area-inset-bottom, 0px));
/* Status-/Navigationsleiste und Tastatur rechnet MainActivity als Rand um den WebView heraus */
--top: 0px;
--bottom: 0px;
color-scheme: dark;
}
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }