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.---
res/layout/candidates_status_no_dict.xml | 5 +
res/layout/keyboard.xml | 4 +-
res/values/strings.xml | 5 +
res/values/styles.xml | 7 +-
res/xml/settings.xml | 3 +
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 +++++
16 files changed, 243 insertions(+), 164 deletions(-)
create mode 100644 res/layout/candidates_status_no_dict.xml
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
diff --git a/res/layout/candidates_status_no_dict.xml b/res/layout/candidates_status_no_dict.xml
new file mode 100644
index 0000000..6ba20b9
--- /dev/null
+++ b/res/layout/candidates_status_no_dict.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/res/layout/keyboard.xml b/res/layout/keyboard.xml
index 82575be..f512979 100644
--- a/res/layout/keyboard.xml
+++ b/res/layout/keyboard.xml
@@ -1,9 +1,9 @@
-
+
-
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6b1c329..e01e98f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -33,6 +33,9 @@
Add keys to the keyboardAdd custom keysSelect keys to add to the keyboard
+ Suggestions
+ Suggestions
+ Enable word completion and spell checkingTypingSwiping distanceDistance of characters in the corners of the keys (%s)
@@ -151,4 +154,6 @@
At most 30 minutesUntil the app stopsCustom definition
+ No dictionary installed
+ Install
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 5b00ca3..5defb7b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -3,7 +3,12 @@
+
diff --git a/res/xml/settings.xml b/res/xml/settings.xml
index 175617a..ca7aea8 100644
--- a/res/xml/settings.xml
+++ b/res/xml/settings.xml
@@ -12,6 +12,9 @@
+
+
+
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