Compare commits

...

9 Commits

Author SHA1 Message Date
Jules Aguillon
87da258c5b Fix margin calculation on Android 14
Some checks failed
Make Apk CI / Build-Apk (push) Has been cancelled
Check translations / check-translations (push) Has been cancelled
Check layouts / check_layout.output (push) Has been cancelled
Check layouts / Generated files (push) Has been cancelled
2024-12-26 18:10:02 +01:00
Jules Aguillon
dd99579a90 Comment 2024-12-26 17:42:57 +01:00
Jules Aguillon
cc3d2591a7 Simpler fix for the launcher activity 2024-12-26 17:36:25 +01:00
Jules Aguillon
ede983aef7 Implement for the settings activity 2024-12-26 17:25:30 +01:00
Jules Aguillon
d396af9240 Implement edge-to-edge for the launcher activity 2024-12-26 17:11:17 +01:00
Jules Aguillon
185ddcec60 Account for buttons in the gesture-navigation bar
Some checks are pending
Check layouts / check_layout.output (push) Waiting to run
Check layouts / Generated files (push) Waiting to run
Check translations / check-translations (push) Waiting to run
Make Apk CI / Build-Apk (push) Waiting to run
The back and IME switching buttons that appear in the navigation bar are
not accounted by WindowInsets and would overlap with the keyboard.
2024-12-26 13:41:06 +01:00
Jules Aguillon
f13af579c1 Allow keyboard to draw behind navigation bars 2024-12-26 13:37:05 +01:00
Jules Aguillon
da957d534b Keyboard doesn't draw behind button-navigation bars in landscape mode 2024-12-26 12:32:35 +01:00
Jules Aguillon
139ff760e0 Proper support for Android 15 edge-to-edge
The keyboard background now extends under the system bars and display
cutout but the keys do not.

This does not yet handle the gesture navigation bar changing height when
the IME dismiss and switch buttons are added.
2024-12-24 17:13:16 +01:00
8 changed files with 89 additions and 21 deletions

View File

@@ -10,11 +10,13 @@
</intent-filter> </intent-filter>
<meta-data android:name="android.view.im" android:resource="@xml/method"/> <meta-data android:name="android.view.im" android:resource="@xml/method"/>
</service> </service>
<activity android:name="juloo.keyboard2.SettingsActivity" android:icon="@mipmap/ic_launcher" android:label="@string/settings_activity_label" android:theme="@style/appTheme" android:exported="true" android:directBootAware="true">
<activity android:name="juloo.keyboard2.SettingsActivity" android:icon="@mipmap/ic_launcher" android:label="@string/settings_activity_label" android:theme="@style/settingsTheme" android:exported="true" android:directBootAware="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="juloo.keyboard2.LauncherActivity" android:icon="@mipmap/ic_launcher" android:theme="@style/appTheme" android:exported="true" android:directBootAware="true"> <activity android:name="juloo.keyboard2.LauncherActivity" android:icon="@mipmap/ic_launcher" android:theme="@style/appTheme" android:exported="true" android:directBootAware="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true"> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:fitsSystemWindows="true">
<LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical">
<TextView style="@style/paragraph" android:text="@string/launcher_description"/> <TextView style="@style/paragraph" android:text="@string/launcher_description"/>
<Button style="@style/paragraph" android:text="@string/launcher_button_imesettings" android:onClick="launch_imesettings" android:layout_width="wrap_content"/> <Button style="@style/paragraph" android:text="@string/launcher_button_imesettings" android:onClick="launch_imesettings" android:layout_width="wrap_content"/>

View File

@@ -80,4 +80,9 @@
<item name="android:orientation">horizontal</item> <item name="android:orientation">horizontal</item>
</style> </style>
<style name="appTheme" parent="@android:style/Theme.DeviceDefault.DayNight"/> <style name="appTheme" parent="@android:style/Theme.DeviceDefault.DayNight"/>
<style name="settingsTheme" parent="appTheme">
<!-- Setting this in the activity theme so it propagate to nested
preference screens. -->
<item name="android:fitsSystemWindows">true</item>
</style>
</resources> </resources>

View File

@@ -6,5 +6,9 @@
<dimen name="emoji_text_size">28dp</dimen> <dimen name="emoji_text_size">28dp</dimen>
<dimen name="clipboard_view_height">300dp</dimen> <dimen name="clipboard_view_height">300dp</dimen>
<dimen name="pref_button_size">28dp</dimen> <dimen name="pref_button_size">28dp</dimen>
<bool name="debug_logs">false</bool> <!-- Will be overwritten automatically by Gradle for the debug build variant --> <!-- Margin needed to accomodate the gesture nav bar on Android 15. Found in
[core/res/res/values/dimens.xml]. -->
<dimen name="bottom_inset_min">48dp</dimen>
<!-- Will be overwritten automatically by Gradle for the debug build variant -->
<bool name="debug_logs">false</bool>
</resources> </resources>

View File

@@ -84,6 +84,7 @@ public final class Config
[get_current_layout()] and [set_current_layout()]. */ [get_current_layout()] and [set_current_layout()]. */
int current_layout_portrait; int current_layout_portrait;
int current_layout_landscape; int current_layout_landscape;
public int bottomInsetMin;
private Config(SharedPreferences prefs, Resources res, IKeyEventHandler h) 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); current_layout_landscape = _prefs.getInt("current_layout_landscape", 0);
circle_sensitivity = Integer.valueOf(_prefs.getString("circle_sensitivity", "2")); circle_sensitivity = Integer.valueOf(_prefs.getString("circle_sensitivity", "2"));
clipboard_history_enabled = _prefs.getBoolean("clipboard_history_enabled", false); 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() public int get_current_layout()

View File

@@ -292,6 +292,15 @@ public class Keyboard2 extends InputMethodService
private void updateSoftInputWindowLayoutParams() { private void updateSoftInputWindowLayoutParams() {
final Window window = getWindow().getWindow(); 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); updateLayoutHeightOf(window, ViewGroup.LayoutParams.MATCH_PARENT);
final View inputArea = window.findViewById(android.R.id.inputArea); final View inputArea = window.findViewById(android.R.id.inputArea);

View File

@@ -42,7 +42,9 @@ public class Keyboard2View extends View
private Config _config; private Config _config;
private float _keyWidth; private float _keyWidth;
private float _bottomMargin; private float _marginRight;
private float _marginLeft;
private float _marginBottom;
private Theme _theme; private Theme _theme;
@@ -232,7 +234,7 @@ public class Keyboard2View extends View
private KeyboardData.Key getKeyAtPosition(float tx, float ty) private KeyboardData.Key getKeyAtPosition(float tx, float ty)
{ {
KeyboardData.Row row = getRowAtPosition(ty); KeyboardData.Row row = getRowAtPosition(ty);
float x = _config.horizontal_margin; float x = _marginLeft;
if (row == null || tx < x) if (row == null || tx < x)
return null; return null;
for (KeyboardData.Key key : row.keys) for (KeyboardData.Key key : row.keys)
@@ -256,28 +258,56 @@ public class Keyboard2View extends View
@Override @Override
public void onMeasure(int wSpec, int hSpec) public void onMeasure(int wSpec, int hSpec)
{ {
DisplayMetrics dm = getContext().getResources().getDisplayMetrics(); int width;
int width = dm.widthPixels; int insets_left = 0;
_bottomMargin = _config.margin_bottom; int insets_right = 0;
// Compatibility with display cutouts and navigation on the right 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) if (VERSION.SDK_INT >= 30)
{ {
WindowMetrics metrics = WindowMetrics metrics =
((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)) ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE))
.getCurrentWindowMetrics(); .getCurrentWindowMetrics();
Insets insets = metrics.getWindowInsets().getInsetsIgnoringVisibility( width = metrics.getBounds().width();
WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars() WindowInsets wi = metrics.getWindowInsets();
| WindowInsets.Type.displayCutout()); int insets_types =
width = metrics.getBounds().width() - insets.right - insets.left; WindowInsets.Type.statusBars()
// Starting in API 35, keyboard window has LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS | 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) 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 height =
(int)(_config.keyHeight * _keyboard.keysHeight (int)(_config.keyHeight * _keyboard.keysHeight
+ _config.marginTop + _bottomMargin); + _config.marginTop + _marginBottom);
setMeasuredDimension(width, height); 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 @Override
@@ -289,10 +319,10 @@ public class Keyboard2View extends View
{ {
// Disable the back-gesture on the keyboard area // Disable the back-gesture on the keyboard area
Rect keyboard_area = new Rect( Rect keyboard_area = new Rect(
left + (int)_config.horizontal_margin, left + (int)_marginLeft,
top + (int)_config.marginTop, top + (int)_config.marginTop,
right - (int)_config.horizontal_margin, right - (int)_marginRight,
bottom - (int)_bottomMargin); bottom - (int)_marginBottom);
setSystemGestureExclusionRects(Arrays.asList(keyboard_area)); setSystemGestureExclusionRects(Arrays.asList(keyboard_area));
} }
} }
@@ -327,7 +357,7 @@ public class Keyboard2View extends View
for (KeyboardData.Row row : _keyboard.rows) for (KeyboardData.Row row : _keyboard.rows)
{ {
y += row.shift * _config.keyHeight; 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; float keyH = row.height * _config.keyHeight - key_vertical_margin;
for (KeyboardData.Key k : row.keys) for (KeyboardData.Key k : row.keys)
{ {

View File

@@ -1,8 +1,13 @@
package juloo.keyboard2; package juloo.keyboard2;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.res.Resources;
import android.graphics.Insets;
import android.os.Build.VERSION;
import android.os.IBinder; import android.os.IBinder;
import android.view.View;
import android.view.Window; import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager; import android.view.WindowManager;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@@ -44,4 +49,14 @@ public final class Utils
out.append(buff, 0, l); out.append(buff, 0, l);
return out.toString(); 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);
}
} }