From 2ecf93d9904544ee73159e9f0ee74b49057bca6c Mon Sep 17 00:00:00 2001 From: Jules Aguillon Date: Sun, 1 Feb 2026 23:25:38 +0100 Subject: Candidates view improvements (#1168) * Refactor: Create subpackage 'suggestions' * Candidates view: Status message when no dictionary installed Show a message on the candidates view instead of leaving it empty. A button points to the dictionary installation activity. * Add an option to disable the suggestions * Refactor: Remove Config.should_show_candidates_view This was moved to EditorConfig. * Don't disable text suggestions in some text boxes * Suggestion text size matching settings The candidates view height is based on the height of keyboard rows and the suggestion text size is based on the size of labels on the keys. This is influenced by symbol size and keyboard height options.--- srcs/juloo.keyboard2/CandidatesView.java | 104 ------------- srcs/juloo.keyboard2/Config.java | 13 +- srcs/juloo.keyboard2/CurrentlyTypedWord.java | 5 +- srcs/juloo.keyboard2/EditorConfig.java | 2 +- srcs/juloo.keyboard2/KeyEventHandler.java | 3 +- srcs/juloo.keyboard2/Keyboard2.java | 9 +- srcs/juloo.keyboard2/Logs.java | 2 - srcs/juloo.keyboard2/Suggestions.java | 36 ----- srcs/juloo.keyboard2/Theme.java | 12 +- .../suggestions/CandidatesView.java | 161 +++++++++++++++++++++ srcs/juloo.keyboard2/suggestions/Suggestions.java | 36 +++++ 11 files changed, 222 insertions(+), 161 deletions(-) delete mode 100644 srcs/juloo.keyboard2/CandidatesView.java delete mode 100644 srcs/juloo.keyboard2/Suggestions.java create mode 100644 srcs/juloo.keyboard2/suggestions/CandidatesView.java create mode 100644 srcs/juloo.keyboard2/suggestions/Suggestions.java (limited to 'srcs/juloo.keyboard2') diff --git a/srcs/juloo.keyboard2/CandidatesView.java b/srcs/juloo.keyboard2/CandidatesView.java deleted file mode 100644 index 232ed23..0000000 --- a/srcs/juloo.keyboard2/CandidatesView.java +++ /dev/null @@ -1,104 +0,0 @@ -package juloo.keyboard2; - -import android.content.Context; -import android.text.InputType; -import android.util.AttributeSet; -import android.view.View; -import android.view.inputmethod.EditorInfo; -import android.widget.LinearLayout; -import android.widget.TextView; -import java.util.ArrayList; -import java.util.List; - -public class CandidatesView extends LinearLayout -{ - static final int NUM_CANDIDATES = 3; - - Config _config; - - /** Candidates currently visible. Entries can be [null] when there are less - than [NUM_CANDIDATES] suggestions. */ - String[] _items = new String[NUM_CANDIDATES]; - - /** Text views showing the candidates in [_items]. Text views visibility is - set to [GONE] when there are less than [NUM_CANDIDATES] suggestions. */ - TextView[] _item_views = new TextView[NUM_CANDIDATES]; - - public CandidatesView(Context context, AttributeSet attrs) - { - super(context, attrs); - _config = Config.globalConfig(); - } - - @Override - protected void onFinishInflate() - { - super.onFinishInflate(); - setup_item_view(0, R.id.candidates_middle); - setup_item_view(1, R.id.candidates_right); - setup_item_view(2, R.id.candidates_left); - } - - public void set_candidates(List suggestions) - { - int s_count = suggestions.size(); - for (int i = 0; i < _item_views.length; i++) - { - TextView v = _item_views[i]; - if (i < s_count) - { - String it = suggestions.get(i); - _items[i] = it; - v.setText(it); - v.setVisibility(View.VISIBLE); - } - else - { - _items[i] = null; - v.setVisibility(View.GONE); - } - } - } - - private void setup_item_view(final int item_index, int item_id) - { - TextView v = (TextView)findViewById(item_id); - v.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(View _v) - { - String it = _items[item_index]; - if (it != null) - _config.handler.suggestion_entered(it); - } - }); - v.setVisibility(View.GONE); - _item_views[item_index] = v; - } - - public static boolean should_show(EditorInfo info) - { - int variation = info.inputType & InputType.TYPE_MASK_VARIATION; - int flags = info.inputType & InputType.TYPE_MASK_FLAGS; - switch (info.inputType & InputType.TYPE_MASK_CLASS) - { - case InputType.TYPE_CLASS_TEXT: - switch (variation) - { - case InputType.TYPE_TEXT_VARIATION_PASSWORD: - case InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD: - case InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD: - return false; - default: - if ((flags & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) - return false; // Editor requested that we don't show suggestions - return true; - } - case InputType.TYPE_CLASS_NUMBER: - // Beware of TYPE_NUMBER_VARIATION_PASSWORD - return false; - default: return false; - } - } -} diff --git a/srcs/juloo.keyboard2/Config.java b/srcs/juloo.keyboard2/Config.java index 5719bd9..16a92ef 100644 --- a/srcs/juloo.keyboard2/Config.java +++ b/srcs/juloo.keyboard2/Config.java @@ -42,6 +42,7 @@ public final class Config public boolean number_row_symbols; public float swipe_dist_px; public float slide_step_px; + public boolean suggestions_enabled; // Let the system handle vibration when false. public boolean vibrate_custom; // Control the vibration if [vibrate_custom] is true. @@ -50,7 +51,7 @@ public final class Config public long longPressInterval; public boolean keyrepeat_enabled; public float margin_bottom; - public int keyboardHeightPercent; + public int keyboard_rows_height_pixels; public int screenHeightPixels; public float horizontal_margin; public float key_vertical_margin; @@ -75,7 +76,6 @@ public final class Config // Dynamically set /** Configuration options implied by the connected editor. */ public EditorConfig editor_config; - public boolean should_show_candidates_view; public boolean shouldOfferVoiceTyping; public ExtraKeys extra_keys_subtype; public Map extra_keys_param; @@ -102,7 +102,6 @@ public final class Config // from prefs refresh(res, foldableUnfolded); // initialized later - should_show_candidates_view = false; shouldOfferVoiceTyping = false; extra_keys_subtype = null; handler = h; @@ -120,6 +119,7 @@ public final class Config float characterSizeScale = 1.f; String show_numpad_s = _prefs.getString("show_numpad", "never"); show_numpad = "always".equals(show_numpad_s); + int keyboardHeightPercent; if (orientation_landscape) { if ("landscape".equals(show_numpad_s)) @@ -136,6 +136,7 @@ public final class Config String number_row = _prefs.getString("number_row", "no_number_row"); add_number_row = !number_row.equals("no_number_row"); number_row_symbols = number_row.equals("symbols"); + suggestions_enabled = _prefs.getBoolean("suggestions", true); // The baseline for the swipe distance correspond to approximately the // width of a key in portrait mode, as most layouts have 10 columns. // Multipled by the DPI ratio because most swipes are made in the diagonals. @@ -165,6 +166,12 @@ public final class Config customBorderRadius = _prefs.getInt("custom_border_radius", 0) / 100.f; customBorderLineWidth = get_dip_pref(dm, "custom_border_line_width", 0); screenHeightPixels = dm.heightPixels; + // Rows height is proportional to the screen height, meaning it doesn't + // change for layouts with more or less rows. 3.95 is the usual height of + // a layout in KeyboardData unit. The keyboard will be higher if the layout + // has more rows and smaller if it has less because rows stay the same + // height. + keyboard_rows_height_pixels = screenHeightPixels * keyboardHeightPercent / 395; horizontal_margin = get_dip_pref_oriented(dm, "horizontal_margin", 3, 28); double_tap_lock_shift = _prefs.getBoolean("lock_double_tap", false); diff --git a/srcs/juloo.keyboard2/CurrentlyTypedWord.java b/srcs/juloo.keyboard2/CurrentlyTypedWord.java index 87a1cf1..8685fb1 100644 --- a/srcs/juloo.keyboard2/CurrentlyTypedWord.java +++ b/srcs/juloo.keyboard2/CurrentlyTypedWord.java @@ -113,12 +113,9 @@ public final class CurrentlyTypedWord /** Refresh the current word by immediately querying the editor. */ void set_current_word(CharSequence text_before_cursor) { + _w.setLength(0); if (text_before_cursor == null) - { - _enabled = false; return; - } - _w.setLength(0); int saved_cursor = _cursor; type_chars(text_before_cursor.toString()); _cursor = saved_cursor; diff --git a/srcs/juloo.keyboard2/EditorConfig.java b/srcs/juloo.keyboard2/EditorConfig.java index 2bfbcef..67b7799 100644 --- a/srcs/juloo.keyboard2/EditorConfig.java +++ b/srcs/juloo.keyboard2/EditorConfig.java @@ -5,7 +5,7 @@ import android.os.Build.VERSION; import android.text.InputType; import android.text.TextUtils; import android.view.inputmethod.EditorInfo; -import juloo.keyboard2.CandidatesView; +import juloo.keyboard2.suggestions.CandidatesView; public final class EditorConfig { diff --git a/srcs/juloo.keyboard2/KeyEventHandler.java b/srcs/juloo.keyboard2/KeyEventHandler.java index a0487d7..18e0592 100644 --- a/srcs/juloo.keyboard2/KeyEventHandler.java +++ b/srcs/juloo.keyboard2/KeyEventHandler.java @@ -1,14 +1,15 @@ package juloo.keyboard2; import android.annotation.SuppressLint; -import android.os.Looper; import android.os.Handler; +import android.os.Looper; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import java.util.Iterator; +import juloo.keyboard2.suggestions.Suggestions; public final class KeyEventHandler implements Config.IKeyEventHandler, diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java index 4d008d9..25af0d0 100644 --- a/srcs/juloo.keyboard2/Keyboard2.java +++ b/srcs/juloo.keyboard2/Keyboard2.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import juloo.keyboard2.prefs.LayoutsPreference; +import juloo.keyboard2.suggestions.CandidatesView; public class Keyboard2 extends InputMethodService implements SharedPreferences.OnSharedPreferenceChangeListener @@ -164,7 +165,11 @@ public class Keyboard2 extends InputMethodService private void refresh_candidates_view() { - boolean should_show = _config.editor_config.should_show_candidates_view; + boolean should_show = + _config.suggestions_enabled + && _config.editor_config.should_show_candidates_view; + if (should_show) + _candidates_view.refresh_config(_config); _candidates_view.setVisibility(should_show ? View.VISIBLE : View.GONE); } @@ -185,6 +190,7 @@ public class Keyboard2 extends InputMethodService // Set keyboard background opacity _container_view.getBackground().setAlpha(_config.keyboardOpacity); _keyboardView.reset(); + refresh_candidates_view(); } private KeyboardData refresh_special_layout() @@ -205,7 +211,6 @@ public class Keyboard2 extends InputMethodService { _config.editor_config.refresh(info, getResources()); refresh_config(); - refresh_candidates_view(); _currentSpecialLayout = refresh_special_layout(); _keyboardView.setKeyboard(current_layout()); _keyeventhandler.started(_config); diff --git a/srcs/juloo.keyboard2/Logs.java b/srcs/juloo.keyboard2/Logs.java index a956305..1bef51c 100644 --- a/srcs/juloo.keyboard2/Logs.java +++ b/srcs/juloo.keyboard2/Logs.java @@ -26,8 +26,6 @@ public final class Logs _debug_logs.println("swapEnterActionKey: " +conf.editor_config.swapEnterActionKey); _debug_logs.println("actionLabel: "+conf.editor_config.actionLabel); - _debug_logs.println("should_show_candidates_view: " - +conf.should_show_candidates_view); } public static void debug_config_migration(int from_version, int to_version) diff --git a/srcs/juloo.keyboard2/Suggestions.java b/srcs/juloo.keyboard2/Suggestions.java deleted file mode 100644 index 4c0c97a..0000000 --- a/srcs/juloo.keyboard2/Suggestions.java +++ /dev/null @@ -1,36 +0,0 @@ -package juloo.keyboard2; - -import java.util.List; -import java.util.Arrays; - -/** Keep track of the word being typed and provide suggestions for - [CandidatesView]. */ -public final class Suggestions -{ - Callback _callback; - - public Suggestions(Callback c) - { - _callback = c; - } - - public void currently_typed_word(String word) - { - if (word.equals("")) - { - _callback.set_suggestions(NO_SUGGESTIONS); - } - else - { - // TODO - _callback.set_suggestions(Arrays.asList(word)); - } - } - - static final List NO_SUGGESTIONS = Arrays.asList(); - - public static interface Callback - { - public void set_suggestions(List suggestions); - } -} diff --git a/srcs/juloo.keyboard2/Theme.java b/srcs/juloo.keyboard2/Theme.java index 9af0d01..f291f36 100644 --- a/srcs/juloo.keyboard2/Theme.java +++ b/srcs/juloo.keyboard2/Theme.java @@ -102,14 +102,10 @@ public class Theme public Computed(Theme theme, Config config, float keyWidth, KeyboardData layout) { - // Rows height is proportional to the keyboard height, meaning it doesn't - // change for layouts with more or less rows. 3.95 is the usual height of - // a layout in KeyboardData unit. The keyboard will be higher if the - // layout has more rows and smaller if it has less because rows stay the - // same height. - row_height = Math.min( - config.screenHeightPixels * config.keyboardHeightPercent / 100 / 3.95f, - config.screenHeightPixels / layout.keysHeight); + // Make sure that the layout isn't higher than the screen. Take the + // height of the candidates view into account. + row_height = Math.min(config.keyboard_rows_height_pixels, + (config.screenHeightPixels - config.keyboard_rows_height_pixels) / layout.keysHeight); vertical_margin = config.key_vertical_margin * row_height; horizontal_margin = config.key_horizontal_margin * keyWidth; // Add half of the key margin on the left and on the top as it's also diff --git a/srcs/juloo.keyboard2/suggestions/CandidatesView.java b/srcs/juloo.keyboard2/suggestions/CandidatesView.java new file mode 100644 index 0000000..259db35 --- /dev/null +++ b/srcs/juloo.keyboard2/suggestions/CandidatesView.java @@ -0,0 +1,161 @@ +package juloo.keyboard2.suggestions; + +import android.content.Context; +import android.text.InputType; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.widget.LinearLayout; +import android.widget.TextView; +import java.util.ArrayList; +import java.util.List; +import juloo.keyboard2.Config; +import juloo.keyboard2.R; + +public class CandidatesView extends LinearLayout +{ + static final int NUM_CANDIDATES = 3; + + /** Candidates currently visible. Entries can be [null] when there are less + than [NUM_CANDIDATES] suggestions. */ + String[] _items = new String[NUM_CANDIDATES]; + + /** Text views showing the candidates in [_items]. Text views visibility is + set to [GONE] when there are less than [NUM_CANDIDATES] suggestions. */ + TextView[] _item_views = new TextView[NUM_CANDIDATES]; + + /** Optional view showing a message to the user. Visible when no candidates + are shown. Might be [null]. */ + View _status_no_dict = null; // Dictionary not installed + + public CandidatesView(Context context, AttributeSet attrs) + { + super(context, attrs); + } + + @Override + protected void onFinishInflate() + { + super.onFinishInflate(); + setup_item_view(0, R.id.candidates_middle); + setup_item_view(1, R.id.candidates_right); + setup_item_view(2, R.id.candidates_left); + } + + public void set_candidates(List suggestions) + { + int s_count = suggestions.size(); + // Hide the status message when showing candidates. + if (s_count != 0 && _status_no_dict != null) + _status_no_dict.setVisibility(View.GONE); + for (int i = 0; i < _item_views.length; i++) + { + TextView v = _item_views[i]; + if (i < s_count) + { + String it = suggestions.get(i); + _items[i] = it; + v.setText(it); + v.setVisibility(View.VISIBLE); + } + else + { + _items[i] = null; + v.setVisibility(View.GONE); + } + } + } + + public void refresh_config(Config config) + { + set_candidates(Suggestions.NO_SUGGESTIONS); + // The status message indicates whether the dictionaries should be + // installed. + _status_no_dict = inflate_and_show(_status_no_dict, + true, + R.layout.candidates_status_no_dict); + set_height(config); + } + + void set_height(Config config) + { + // Make the candidates view about as high as a keyboard row. + int height = (int)(config.keyboard_rows_height_pixels * (1 - config.key_vertical_margin)); + // Match the size of labels on the keyboard, increased by 15%. + float text_size = height * config.characterSize * config.labelTextSize * 1.15f; + for (int i = 0; i < NUM_CANDIDATES; i++) + { + TextView v = _item_views[i]; + ViewGroup.MarginLayoutParams p = + (ViewGroup.MarginLayoutParams)v.getLayoutParams(); + p.height = height; + v.setLayoutParams(p); + v.setTextSize(TypedValue.COMPLEX_UNIT_PX, text_size); + } + } + + /** Show or hide a status view and inflate it if needed. */ + View inflate_and_show(View v, boolean show, int layout_id) + { + if (!show) + { + if (v != null) + v.setVisibility(View.GONE); + } + else + { + if (v == null) + { + v = View.inflate(getContext(), layout_id, null); + addView(v); + } + v.setVisibility(View.VISIBLE); + } + return v; + } + + private void setup_item_view(final int item_index, int item_id) + { + TextView v = (TextView)findViewById(item_id); + v.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View _v) + { + String it = _items[item_index]; + if (it != null) + Config.globalConfig().handler.suggestion_entered(it); + } + }); + v.setVisibility(View.GONE); + _item_views[item_index] = v; + } + + /** Whether the candidates view should be shown for a given editor. */ + public static boolean should_show(EditorInfo info) + { + int variation = info.inputType & InputType.TYPE_MASK_VARIATION; + int flags = info.inputType & InputType.TYPE_MASK_FLAGS; + switch (info.inputType & InputType.TYPE_MASK_CLASS) + { + case InputType.TYPE_CLASS_TEXT: + switch (variation) + { + case InputType.TYPE_TEXT_VARIATION_PASSWORD: + case InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD: + case InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD: + return false; + default: + if ((flags & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) + return false; // Editor requested that we don't show suggestions + return true; + } + case InputType.TYPE_CLASS_NUMBER: + // Beware of TYPE_NUMBER_VARIATION_PASSWORD + return false; + default: return false; + } + } +} diff --git a/srcs/juloo.keyboard2/suggestions/Suggestions.java b/srcs/juloo.keyboard2/suggestions/Suggestions.java new file mode 100644 index 0000000..50c64e0 --- /dev/null +++ b/srcs/juloo.keyboard2/suggestions/Suggestions.java @@ -0,0 +1,36 @@ +package juloo.keyboard2.suggestions; + +import java.util.Arrays; +import java.util.List; + +/** Keep track of the word being typed and provide suggestions for + [CandidatesView]. */ +public final class Suggestions +{ + Callback _callback; + + public Suggestions(Callback c) + { + _callback = c; + } + + public void currently_typed_word(String word) + { + if (word.equals("")) + { + _callback.set_suggestions(NO_SUGGESTIONS); + } + else + { + // TODO + _callback.set_suggestions(Arrays.asList(word)); + } + } + + static final List NO_SUGGESTIONS = Arrays.asList(); + + public static interface Callback + { + public void set_suggestions(List suggestions); + } +} -- cgit v1.2.3