From 370f921bc367613164d93dc1ddbd28c57042e0a7 Mon Sep 17 00:00:00 2001
From: Jules Aguillon
Date: Thu, 26 Dec 2024 18:29:19 +0100
Subject: Proper support for Android 15 edge-to-edge (#848)
The keyboard background now extends under the system bars and display
cutout on Android 15 but the keys do not.
The back and IME switching buttons that appear in the navigation bar require
special care to not overlap with the keyboard.
The launcher and settings activity are also fixed.
---
AndroidManifest.xml | 4 +-
res/layout/launcher_activity.xml | 2 +-
res/values/styles.xml | 5 +++
res/values/values.xml | 6 ++-
srcs/juloo.keyboard2/Config.java | 3 ++
srcs/juloo.keyboard2/Keyboard2.java | 9 +++++
srcs/juloo.keyboard2/Keyboard2View.java | 66 ++++++++++++++++++++++++---------
srcs/juloo.keyboard2/Utils.java | 15 ++++++++
8 files changed, 89 insertions(+), 21 deletions(-)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a76585c..bee30c3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -10,11 +10,13 @@
-
+
+
+
diff --git a/res/layout/launcher_activity.xml b/res/layout/launcher_activity.xml
index 617c9ee..2273641 100644
--- a/res/layout/launcher_activity.xml
+++ b/res/layout/launcher_activity.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/res/values/styles.xml b/res/values/styles.xml
index bf4773d..f592cd7 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -80,4 +80,9 @@
- horizontal
+
diff --git a/res/values/values.xml b/res/values/values.xml
index b13296b..d506011 100644
--- a/res/values/values.xml
+++ b/res/values/values.xml
@@ -6,5 +6,9 @@
28dp
300dp
28dp
- false
+
+ 48dp
+
+ false
diff --git a/srcs/juloo.keyboard2/Config.java b/srcs/juloo.keyboard2/Config.java
index b28d2c0..9c31a6f 100644
--- a/srcs/juloo.keyboard2/Config.java
+++ b/srcs/juloo.keyboard2/Config.java
@@ -84,6 +84,7 @@ public final class Config
[get_current_layout()] and [set_current_layout()]. */
int current_layout_portrait;
int current_layout_landscape;
+ public int bottomInsetMin;
private Config(SharedPreferences prefs, Resources res, IKeyEventHandler h)
{
@@ -187,6 +188,8 @@ public final class Config
current_layout_landscape = _prefs.getInt("current_layout_landscape", 0);
circle_sensitivity = Integer.valueOf(_prefs.getString("circle_sensitivity", "2"));
clipboard_history_enabled = _prefs.getBoolean("clipboard_history_enabled", false);
+ bottomInsetMin = Utils.is_navigation_bar_gestural(res) ?
+ (int)res.getDimension(R.dimen.bottom_inset_min) : 0;
}
public int get_current_layout()
diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java
index a68b954..201449d 100644
--- a/srcs/juloo.keyboard2/Keyboard2.java
+++ b/srcs/juloo.keyboard2/Keyboard2.java
@@ -292,6 +292,15 @@ public class Keyboard2 extends InputMethodService
private void updateSoftInputWindowLayoutParams() {
final Window window = getWindow().getWindow();
+ // On API >= 30, Keyboard2View behaves as edge-to-edge
+ if (VERSION.SDK_INT >= 30)
+ {
+ WindowManager.LayoutParams wattrs = window.getAttributes();
+ wattrs.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ // Allow to draw behind system bars
+ wattrs.setFitInsetsTypes(0);
+ }
updateLayoutHeightOf(window, ViewGroup.LayoutParams.MATCH_PARENT);
final View inputArea = window.findViewById(android.R.id.inputArea);
diff --git a/srcs/juloo.keyboard2/Keyboard2View.java b/srcs/juloo.keyboard2/Keyboard2View.java
index 06ed677..a10dce6 100644
--- a/srcs/juloo.keyboard2/Keyboard2View.java
+++ b/srcs/juloo.keyboard2/Keyboard2View.java
@@ -42,7 +42,9 @@ public class Keyboard2View extends View
private Config _config;
private float _keyWidth;
- private float _bottomMargin;
+ private float _marginRight;
+ private float _marginLeft;
+ private float _marginBottom;
private Theme _theme;
@@ -232,7 +234,7 @@ public class Keyboard2View extends View
private KeyboardData.Key getKeyAtPosition(float tx, float ty)
{
KeyboardData.Row row = getRowAtPosition(ty);
- float x = _config.horizontal_margin;
+ float x = _marginLeft;
if (row == null || tx < x)
return null;
for (KeyboardData.Key key : row.keys)
@@ -256,28 +258,56 @@ public class Keyboard2View extends View
@Override
public void onMeasure(int wSpec, int hSpec)
{
- DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
- int width = dm.widthPixels;
- _bottomMargin = _config.margin_bottom;
- // Compatibility with display cutouts and navigation on the right
+ int width;
+ int insets_left = 0;
+ int insets_right = 0;
+ int insets_bottom = 0;
+ // LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS is set in [Keyboard2#updateSoftInputWindowLayoutParams].
+ // and keyboard is allowed do draw behind status/navigation bars
if (VERSION.SDK_INT >= 30)
{
WindowMetrics metrics =
((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE))
.getCurrentWindowMetrics();
- Insets insets = metrics.getWindowInsets().getInsetsIgnoringVisibility(
- WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars()
- | WindowInsets.Type.displayCutout());
- width = metrics.getBounds().width() - insets.right - insets.left;
- // Starting in API 35, keyboard window has LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ width = metrics.getBounds().width();
+ WindowInsets wi = metrics.getWindowInsets();
+ int insets_types =
+ WindowInsets.Type.statusBars()
+ | WindowInsets.Type.displayCutout()
+ | WindowInsets.Type.mandatorySystemGestures()
+ | WindowInsets.Type.navigationBars();
+ Insets insets = wi.getInsets(insets_types);
+ insets_left = insets.left;
+ insets_right = insets.right;
+ // On API 35, the keyboard is allowed to draw under the
+ // button-navigation bar but on lower APIs, it must be discounted from
+ // the width.
+ if (VERSION.SDK_INT < 35)
+ {
+ Insets nav_insets = wi.getInsets(WindowInsets.Type.navigationBars());
+ width -= nav_insets.left + nav_insets.right;
+ insets_left -= nav_insets.left;
+ insets_right -= nav_insets.right;
+ }
+ // [insets.bottom] doesn't take into account the buttons that appear in
+ // the gesture navigation bar when the IME is showing so ensure a minimum
+ // of margin is added.
if (VERSION.SDK_INT >= 35)
- _bottomMargin += insets.bottom;
+ insets_bottom = Math.max(insets.bottom, _config.bottomInsetMin);
+ }
+ else
+ {
+ DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
+ width = dm.widthPixels;
}
int height =
(int)(_config.keyHeight * _keyboard.keysHeight
- + _config.marginTop + _bottomMargin);
+ + _config.marginTop + _marginBottom);
setMeasuredDimension(width, height);
- _keyWidth = (width - (_config.horizontal_margin * 2)) / _keyboard.keysWidth;
+ _marginLeft = Math.max(_config.horizontal_margin, insets_left);
+ _marginRight = Math.max(_config.horizontal_margin, insets_right);
+ _marginBottom = _config.margin_bottom + insets_bottom;
+ _keyWidth = (width - _marginLeft - _marginRight) / _keyboard.keysWidth;
}
@Override
@@ -289,10 +319,10 @@ public class Keyboard2View extends View
{
// Disable the back-gesture on the keyboard area
Rect keyboard_area = new Rect(
- left + (int)_config.horizontal_margin,
+ left + (int)_marginLeft,
top + (int)_config.marginTop,
- right - (int)_config.horizontal_margin,
- bottom - (int)_bottomMargin);
+ right - (int)_marginRight,
+ bottom - (int)_marginBottom);
setSystemGestureExclusionRects(Arrays.asList(keyboard_area));
}
}
@@ -327,7 +357,7 @@ public class Keyboard2View extends View
for (KeyboardData.Row row : _keyboard.rows)
{
y += row.shift * _config.keyHeight;
- float x = _config.horizontal_margin + key_horizontal_margin / 2;
+ float x = _marginLeft + key_horizontal_margin / 2;
float keyH = row.height * _config.keyHeight - key_vertical_margin;
for (KeyboardData.Key k : row.keys)
{
diff --git a/srcs/juloo.keyboard2/Utils.java b/srcs/juloo.keyboard2/Utils.java
index f0f3036..88c865c 100644
--- a/srcs/juloo.keyboard2/Utils.java
+++ b/srcs/juloo.keyboard2/Utils.java
@@ -1,8 +1,13 @@
package juloo.keyboard2;
import android.app.AlertDialog;
+import android.content.res.Resources;
+import android.graphics.Insets;
+import android.os.Build.VERSION;
import android.os.IBinder;
+import android.view.View;
import android.view.Window;
+import android.view.WindowInsets;
import android.view.WindowManager;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -44,4 +49,14 @@ public final class Utils
out.append(buff, 0, l);
return out.toString();
}
+
+ /** Whether the thin gesture-navigation bar is used.
+ https://stackoverflow.com/questions/36514167/how-to-really-get-the-navigation-bar-height-in-android
+ */
+ public static boolean is_navigation_bar_gestural(Resources res)
+ {
+ // core/java/android/view/WindowManagerPolicyConstants.java
+ int res_id = res.getIdentifier("config_navBarInteractionMode", "integer", "android");
+ return (res_id > 0 && res.getInteger(res_id) == 2);
+ }
}
--
cgit v1.2.3