diff options
| author | Matej Drobnič | 2025-05-22 23:54:13 +0200 |
|---|---|---|
| committer | GitHub | 2025-05-22 23:54:13 +0200 |
| commit | 97355881a855c5d2503f7e518c1e30fc03f88d4e (patch) | |
| tree | f9abbdb522c1d4dfc478b6716430ef5afdceee0d /srcs | |
| parent | a7312054b56ad3bdbc62d03c774b5942db9852e9 (diff) | |
| download | unexpected-keyboard-97355881a855c5d2503f7e518c1e30fc03f88d4e.tar.gz unexpected-keyboard-97355881a855c5d2503f7e518c1e30fc03f88d4e.zip | |
Better support for foldable devices (#982)
* Add AndroidX WindowManager
unfortunately, this seems to be the only way to get fold state, native Android APIs are internal. To add this, we need to update some dependencies, raise java version and raise compile SDK.
* adds separate layouts and separate layout settings for folded and unfolded state of the device.
The affected settings are:
+ the margin bottom settings
+ the horizontal margin settings
+ the keyboard height settings
* Update shell.nix
Diffstat (limited to 'srcs')
| -rw-r--r-- | srcs/juloo.keyboard2/Config.java | 57 | ||||
| -rw-r--r-- | srcs/juloo.keyboard2/FoldStateTracker.java | 62 | ||||
| -rw-r--r-- | srcs/juloo.keyboard2/Keyboard2.java | 15 | ||||
| -rw-r--r-- | srcs/juloo.keyboard2/SettingsActivity.java | 8 |
4 files changed, 126 insertions, 16 deletions
diff --git a/srcs/juloo.keyboard2/Config.java b/srcs/juloo.keyboard2/Config.java index 206c12a..9f27e7e 100644 --- a/srcs/juloo.keyboard2/Config.java +++ b/srcs/juloo.keyboard2/Config.java @@ -5,6 +5,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.util.DisplayMetrics; import android.util.TypedValue; +import androidx.window.layout.WindowInfoTracker; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -72,13 +73,16 @@ public final class Config public final IKeyEventHandler handler; public boolean orientation_landscape = false; + public boolean foldable_unfolded = false; /** Index in 'layouts' of the currently used layout. See [get_current_layout()] and [set_current_layout()]. */ int current_layout_portrait; int current_layout_landscape; + int current_layout_unfolded_portrait; + int current_layout_unfolded_landscape; public int bottomInsetMin; - private Config(SharedPreferences prefs, Resources res, IKeyEventHandler h) + private Config(SharedPreferences prefs, Resources res, IKeyEventHandler h, Boolean foldableUnfolded) { _prefs = prefs; // static values @@ -87,7 +91,7 @@ public final class Config labelTextSize = 0.33f; sublabelTextSize = 0.22f; // from prefs - refresh(res); + refresh(res, foldableUnfolded); // initialized later shouldOfferVoiceTyping = false; actionLabel = null; @@ -100,10 +104,12 @@ public final class Config /* ** Reload prefs */ - public void refresh(Resources res) + public void refresh(Resources res, Boolean foldableUnfolded) { DisplayMetrics dm = res.getDisplayMetrics(); orientation_landscape = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + foldable_unfolded = foldableUnfolded; + // The height of the keyboard is relative to the height of the screen. // This is the height of the keyboard if it have 4 rows. int keyboardHeightPercent; @@ -114,12 +120,12 @@ public final class Config { if ("landscape".equals(show_numpad_s)) show_numpad = true; - keyboardHeightPercent = _prefs.getInt("keyboard_height_landscape", 50); + keyboardHeightPercent = _prefs.getInt(foldable_unfolded ? "keyboard_height_landscape_unfolded" : "keyboard_height_landscape", 50); characterSizeScale = 1.25f; } else { - keyboardHeightPercent = _prefs.getInt("keyboard_height", 35); + keyboardHeightPercent = _prefs.getInt(foldable_unfolded ? "keyboard_height_unfolded" : "keyboard_height", 35); } layouts = LayoutsPreference.load_from_preferences(res, _prefs); inverse_numpad = _prefs.getString("numpad_layout", "default").equals("low_first"); @@ -170,6 +176,8 @@ public final class Config pin_entry_enabled = _prefs.getBoolean("pin_entry_enabled", true); current_layout_portrait = _prefs.getInt("current_layout_portrait", 0); current_layout_landscape = _prefs.getInt("current_layout_landscape", 0); + current_layout_unfolded_portrait = _prefs.getInt("current_layout_unfolded_portrait", 0); + current_layout_unfolded_landscape = _prefs.getInt("current_layout_unfolded_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) ? @@ -178,19 +186,34 @@ public final class Config public int get_current_layout() { - return (orientation_landscape) - ? current_layout_landscape : current_layout_portrait; + if (foldable_unfolded) { + return (orientation_landscape) + ? current_layout_unfolded_landscape : current_layout_unfolded_portrait; + } else { + return (orientation_landscape) + ? current_layout_landscape : current_layout_portrait; + } } public void set_current_layout(int l) { - if (orientation_landscape) - current_layout_landscape = l; - else - current_layout_portrait = l; + if (foldable_unfolded) { + if (orientation_landscape) + current_layout_unfolded_landscape = l; + else + current_layout_unfolded_portrait = l; + } else { + if (orientation_landscape) + current_layout_landscape = l; + else + current_layout_portrait = l; + } + SharedPreferences.Editor e = _prefs.edit(); e.putInt("current_layout_portrait", current_layout_portrait); e.putInt("current_layout_landscape", current_layout_landscape); + e.putInt("current_layout_unfolded_portrait", current_layout_unfolded_portrait); + e.putInt("current_layout_unfolded_landscape", current_layout_unfolded_landscape); e.apply(); } @@ -213,7 +236,13 @@ public final class Config /** [get_dip_pref] depending on orientation. */ float get_dip_pref_oriented(DisplayMetrics dm, String pref_base_name, float def_port, float def_land) { - String suffix = orientation_landscape ? "_landscape" : "_portrait"; + final String suffix; + if (foldable_unfolded) { + suffix = orientation_landscape ? "_landscape_unfolded" : "_portrait_unfolded"; + } else { + suffix = orientation_landscape ? "_landscape" : "_portrait"; + } + float def = orientation_landscape ? def_land : def_port; return get_dip_pref(dm, pref_base_name + suffix, def); } @@ -249,10 +278,10 @@ public final class Config private static Config _globalConfig = null; public static void initGlobalConfig(SharedPreferences prefs, Resources res, - IKeyEventHandler handler) + IKeyEventHandler handler, Boolean foldableUnfolded) { migrate(prefs); - _globalConfig = new Config(prefs, res, handler); + _globalConfig = new Config(prefs, res, handler, foldableUnfolded); LayoutModifier.init(_globalConfig, res); } diff --git a/srcs/juloo.keyboard2/FoldStateTracker.java b/srcs/juloo.keyboard2/FoldStateTracker.java new file mode 100644 index 0000000..60933c9 --- /dev/null +++ b/srcs/juloo.keyboard2/FoldStateTracker.java @@ -0,0 +1,62 @@ +package juloo.keyboard2; + +import android.content.Context; +import android.content.pm.PackageManager; +import androidx.window.java.layout.WindowInfoTrackerCallbackAdapter; +import androidx.window.layout.DisplayFeature; +import androidx.window.layout.FoldingFeature; +import androidx.window.layout.WindowInfoTracker; +import androidx.window.layout.WindowLayoutInfo; + +import androidx.core.util.Consumer; + + +public class FoldStateTracker { + private final Consumer<WindowLayoutInfo> _innerListener; + private final WindowInfoTrackerCallbackAdapter _windowInfoTracker; + private FoldingFeature _foldingFeature = null; + private Runnable _changedCallback = null; + public FoldStateTracker(Context context) { + _windowInfoTracker = + new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(context)); + _innerListener = new LayoutStateChangeCallback(); + _windowInfoTracker.addWindowLayoutInfoListener(context, Runnable::run, _innerListener); + } + + public static boolean isFoldableDevice(Context context) { + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_SENSOR_HINGE_ANGLE); + } + + public boolean isUnfolded() { + // FoldableFeature is only present when the device is unfolded. Otherwise, it's removed. + // A weird decision from Google, but that's how it works: + // https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarAdapter.kt;l=187?q=SidecarAdapter + + return _foldingFeature != null; + } + + public void close() { + _windowInfoTracker.removeWindowLayoutInfoListener(_innerListener); + } + + public void setChangedCallback(Runnable _changedCallback) { + this._changedCallback = _changedCallback; + } + + class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> { + @Override + public void accept(WindowLayoutInfo newLayoutInfo) { + FoldingFeature old = _foldingFeature; + _foldingFeature = null; + for (DisplayFeature feature: newLayoutInfo.getDisplayFeatures()) { + if (feature instanceof FoldingFeature) { + _foldingFeature = (FoldingFeature) feature; + } + } + + if (old != _foldingFeature && _changedCallback != null) { + _changedCallback.run(); + } + } + } +} diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java index 9e54aaf..9ce78f1 100644 --- a/srcs/juloo.keyboard2/Keyboard2.java +++ b/srcs/juloo.keyboard2/Keyboard2.java @@ -43,6 +43,8 @@ public class Keyboard2 extends InputMethodService private Config _config; + private FoldStateTracker _foldStateTracker; + /** Layout currently visible before it has been modified. */ KeyboardData current_layout_unmodified() { @@ -111,13 +113,22 @@ public class Keyboard2 extends InputMethodService SharedPreferences prefs = DirectBootAwarePreferences.get_shared_preferences(this); _handler = new Handler(getMainLooper()); _keyeventhandler = new KeyEventHandler(this.new Receiver()); - Config.initGlobalConfig(prefs, getResources(), _keyeventhandler); + _foldStateTracker = new FoldStateTracker(this); + Config.initGlobalConfig(prefs, getResources(), _keyeventhandler, _foldStateTracker.isUnfolded()); prefs.registerOnSharedPreferenceChangeListener(this); _config = Config.globalConfig(); _keyboardView = (Keyboard2View)inflate_view(R.layout.keyboard); _keyboardView.reset(); Logs.set_debug_logs(getResources().getBoolean(R.bool.debug_logs)); ClipboardHistoryService.on_startup(this, _keyeventhandler); + _foldStateTracker.setChangedCallback(() -> { refresh_config(); }); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + _foldStateTracker.close(); } private List<InputMethodSubtype> getEnabledSubtypes(InputMethodManager imm) @@ -234,7 +245,7 @@ public class Keyboard2 extends InputMethodService private void refresh_config() { int prev_theme = _config.theme; - _config.refresh(getResources()); + _config.refresh(getResources(), _foldStateTracker.isUnfolded()); refreshSubtypeImm(); // Refreshing the theme config requires re-creating the views if (prev_theme != _config.theme) diff --git a/srcs/juloo.keyboard2/SettingsActivity.java b/srcs/juloo.keyboard2/SettingsActivity.java index 2b7ae91..dffc986 100644 --- a/srcs/juloo.keyboard2/SettingsActivity.java +++ b/srcs/juloo.keyboard2/SettingsActivity.java @@ -23,6 +23,14 @@ public class SettingsActivity extends PreferenceActivity } catch (Exception _e) { fallbackEncrypted(); return; } addPreferencesFromResource(R.xml.settings); + + boolean foldableDevice = FoldStateTracker.isFoldableDevice(this); + findPreference("margin_bottom_portrait_unfolded").setEnabled(foldableDevice); + findPreference("margin_bottom_landscape_unfolded").setEnabled(foldableDevice); + findPreference("horizontal_margin_portrait_unfolded").setEnabled(foldableDevice); + findPreference("horizontal_margin_landscape_unfolded").setEnabled(foldableDevice); + findPreference("keyboard_height_unfolded").setEnabled(foldableDevice); + findPreference("keyboard_height_landscape_unfolded").setEnabled(foldableDevice); } void fallbackEncrypted() |
