From 77c4a27c4c37b3620defcab94ffd1b2f536c88cb Mon Sep 17 00:00:00 2001 From: Jules Aguillon Date: Mon, 2 Feb 2026 00:20:00 +0100 Subject: Spell checking (#1137) This adds dictionary-based spell checking to the keyboard. The keyboard looks at the word being typed and matches it against a dictionary to either complete the rest of the word or find alternative spellings. The core of this feature is implemented in cdict, which is included as a submodule in vendor/cidct. Cdict is developped at https://github.com/Julow/cdict The dictionaries are hosted at https://github.com/Julow/Unexpected-Keyboard-dictionaries/ The wordlists used to build the dictionaries are the same ones used by HeliBoard from https://codeberg.org/Helium314/aosp-dictionaries - Add an activity accessible from the launcher app that lists available dictionaries with a download button. The DictionaryListView view shows the list of available dictionaries and handles downloading and installing them. - The Dictionaries class manages installed dictionaries. Dictionaries are installed as individual files into the app's private directory. - Available dictionaries are listed in dictionaries.xml, which is generated when building Unexpected-Keyboard-dictionaries. method.xml mentions the dictionary name for each locales. --- srcs/juloo.keyboard2/Config.java | 18 +- srcs/juloo.keyboard2/DeviceLocales.java | 2 + srcs/juloo.keyboard2/KeyEventHandler.java | 4 +- srcs/juloo.keyboard2/Keyboard2.java | 45 ++++- srcs/juloo.keyboard2/LauncherActivity.java | 7 + srcs/juloo.keyboard2/Utils.java | 12 ++ srcs/juloo.keyboard2/dict/Dictionaries.java | 149 ++++++++++++++++ .../juloo.keyboard2/dict/DictionariesActivity.java | 15 ++ srcs/juloo.keyboard2/dict/DictionaryListView.java | 191 +++++++++++++++++++++ .../dict/SupportedDictionaries.java | 33 ++++ .../suggestions/CandidatesView.java | 2 +- srcs/juloo.keyboard2/suggestions/Suggestions.java | 27 ++- 12 files changed, 483 insertions(+), 22 deletions(-) create mode 100644 srcs/juloo.keyboard2/dict/Dictionaries.java create mode 100644 srcs/juloo.keyboard2/dict/DictionariesActivity.java create mode 100644 srcs/juloo.keyboard2/dict/DictionaryListView.java create mode 100644 srcs/juloo.keyboard2/dict/SupportedDictionaries.java (limited to 'srcs/juloo.keyboard2') diff --git a/srcs/juloo.keyboard2/Config.java b/srcs/juloo.keyboard2/Config.java index 16a92ef..18e9fe7 100644 --- a/srcs/juloo.keyboard2/Config.java +++ b/srcs/juloo.keyboard2/Config.java @@ -8,6 +8,8 @@ import android.util.TypedValue; import java.util.ArrayList; import java.util.List; import java.util.Map; +import juloo.cdict.Cdict; +import juloo.keyboard2.dict.Dictionaries; import juloo.keyboard2.prefs.CustomExtraKeysPreference; import juloo.keyboard2.prefs.ExtraKeysPreference; import juloo.keyboard2.prefs.LayoutsPreference; @@ -80,8 +82,8 @@ public final class Config public ExtraKeys extra_keys_subtype; public Map extra_keys_param; public Map extra_keys_custom; - - public final IKeyEventHandler handler; + public Cdict current_dictionary = null; // Might be 'null'. + public IKeyEventHandler handler; public boolean orientation_landscape = false; public boolean foldable_unfolded = false; public boolean wide_screen = false; @@ -90,7 +92,8 @@ public final class Config int current_layout_narrow; int current_layout_wide; - private Config(SharedPreferences prefs, Resources res, IKeyEventHandler h, Boolean foldableUnfolded) + private Config(SharedPreferences prefs, Resources res, + Boolean foldableUnfolded, Dictionaries dicts) { _prefs = prefs; editor_config = new EditorConfig(); @@ -100,17 +103,16 @@ public final class Config labelTextSize = 0.33f; sublabelTextSize = 0.22f; // from prefs - refresh(res, foldableUnfolded); + refresh(res, foldableUnfolded, dicts); // initialized later shouldOfferVoiceTyping = false; extra_keys_subtype = null; - handler = h; } /* ** Reload prefs */ - public void refresh(Resources res, Boolean foldableUnfolded) + public void refresh(Resources res, Boolean foldableUnfolded, Dictionaries dicts) { DisplayMetrics dm = res.getDisplayMetrics(); orientation_landscape = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; @@ -289,10 +291,10 @@ public final class Config private static Config _globalConfig = null; public static void initGlobalConfig(SharedPreferences prefs, Resources res, - IKeyEventHandler handler, Boolean foldableUnfolded) + Boolean foldableUnfolded, Dictionaries dicts) { migrate(prefs); - _globalConfig = new Config(prefs, res, handler, foldableUnfolded); + _globalConfig = new Config(prefs, res, foldableUnfolded, dicts); LayoutModifier.init(_globalConfig, res); } diff --git a/srcs/juloo.keyboard2/DeviceLocales.java b/srcs/juloo.keyboard2/DeviceLocales.java index a6cfdaf..c0c489c 100644 --- a/srcs/juloo.keyboard2/DeviceLocales.java +++ b/srcs/juloo.keyboard2/DeviceLocales.java @@ -36,6 +36,7 @@ public final class DeviceLocales public final String script; public final String default_layout; // Might be [null] public final ExtraKeys extra_keys; + public final String dictionary; // Might be [null] public Loc(InputMethodSubtype st) { @@ -45,6 +46,7 @@ public final class DeviceLocales String extra_keys_s = st.getExtraValueOf("extra_keys"); extra_keys = (extra_keys_s != null) ? ExtraKeys.parse(script, extra_keys_s) : ExtraKeys.EMPTY; + dictionary = st.getExtraValueOf("dictionary"); } } diff --git a/srcs/juloo.keyboard2/KeyEventHandler.java b/srcs/juloo.keyboard2/KeyEventHandler.java index 18e0592..2bda20c 100644 --- a/srcs/juloo.keyboard2/KeyEventHandler.java +++ b/srcs/juloo.keyboard2/KeyEventHandler.java @@ -31,14 +31,14 @@ public final class KeyEventHandler [setSelection] could be used instead. */ boolean _move_cursor_force_fallback = false; - public KeyEventHandler(IReceiver recv) + public KeyEventHandler(IReceiver recv, Config config) { _recv = recv; Handler handler = recv.getHandler(); _autocap = new Autocapitalisation(handler, this.new Autocapitalisation_callback()); _mods = Pointers.Modifiers.EMPTY; - _suggestions = new Suggestions(recv); + _suggestions = new Suggestions(recv, config); _typedword = new CurrentlyTypedWord(handler, this); } diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java index 25af0d0..ffe99ac 100644 --- a/srcs/juloo.keyboard2/Keyboard2.java +++ b/srcs/juloo.keyboard2/Keyboard2.java @@ -23,8 +23,11 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import juloo.keyboard2.dict.Dictionaries; +import juloo.keyboard2.dict.DictionariesActivity; import juloo.keyboard2.prefs.LayoutsPreference; import juloo.keyboard2.suggestions.CandidatesView; +import juloo.cdict.Cdict; public class Keyboard2 extends InputMethodService implements SharedPreferences.OnSharedPreferenceChangeListener @@ -40,6 +43,7 @@ public class Keyboard2 extends InputMethodService private KeyboardData _localeTextLayout; /** Installed and current locales. */ private DeviceLocales _device_locales; + private Dictionaries _dictionaries; private ViewGroup _emojiPane = null; private ViewGroup _clipboard_pane = null; private Handler _handler; @@ -115,11 +119,14 @@ public class Keyboard2 extends InputMethodService super.onCreate(); SharedPreferences prefs = DirectBootAwarePreferences.get_shared_preferences(this); _handler = new Handler(getMainLooper()); - _keyeventhandler = new KeyEventHandler(this.new Receiver()); _foldStateTracker = new FoldStateTracker(this); - Config.initGlobalConfig(prefs, getResources(), _keyeventhandler, _foldStateTracker.isUnfolded()); - prefs.registerOnSharedPreferenceChangeListener(this); + _dictionaries = Dictionaries.instance(this); + Config.initGlobalConfig(prefs, getResources(), + _foldStateTracker.isUnfolded(), _dictionaries); _config = Config.globalConfig(); + _keyeventhandler = new KeyEventHandler(this.new Receiver(), _config); + _config.handler = _keyeventhandler; + prefs.registerOnSharedPreferenceChangeListener(this); Logs.set_debug_logs(getResources().getBoolean(R.bool.debug_logs)); refreshSubtypeImm(); create_keyboard_view(); @@ -163,6 +170,18 @@ public class Keyboard2 extends InputMethodService _localeTextLayout = default_layout; } + private void refresh_current_dictionary() + { + _config.current_dictionary = null; + String current = _device_locales.default_.dictionary; + if (current == null) + return; + Cdict[] dicts = _dictionaries.load(current); + if (dicts == null) + return; + _config.current_dictionary = Dictionaries.find_by_name(dicts, "main"); + } + private void refresh_candidates_view() { boolean should_show = @@ -178,7 +197,8 @@ public class Keyboard2 extends InputMethodService private void refresh_config() { int prev_theme = _config.theme; - _config.refresh(getResources(), _foldStateTracker.isUnfolded()); + _config.refresh(getResources(), _foldStateTracker.isUnfolded(), _dictionaries); + refresh_current_dictionary(); // Refreshing the theme config requires re-creating the views if (prev_theme != _config.theme) { @@ -331,6 +351,19 @@ public class Keyboard2 extends InputMethodService return false; } + /** Called from [onClick] attributes. */ + public void launch_dictionaries_activity(View v) + { + start_activity(DictionariesActivity.class); + } + + void start_activity(Class cls) + { + Intent intent = new Intent(this, cls); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + /** Not static */ public class Receiver implements KeyEventHandler.IReceiver { @@ -339,9 +372,7 @@ public class Keyboard2 extends InputMethodService switch (ev) { case CONFIG: - Intent intent = new Intent(Keyboard2.this, SettingsActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); + start_activity(SettingsActivity.class); break; case SWITCH_TEXT: diff --git a/srcs/juloo.keyboard2/LauncherActivity.java b/srcs/juloo.keyboard2/LauncherActivity.java index ee769fb..d708619 100644 --- a/srcs/juloo.keyboard2/LauncherActivity.java +++ b/srcs/juloo.keyboard2/LauncherActivity.java @@ -21,6 +21,8 @@ import android.widget.ImageView; import android.widget.TextView; import java.util.ArrayList; import java.util.List; +import juloo.keyboard2.dict.DictionariesActivity; +import juloo.keyboard2.R; public class LauncherActivity extends Activity implements Handler.Callback { @@ -96,6 +98,11 @@ public class LauncherActivity extends Activity implements Handler.Callback imm.showInputMethodPicker(); } + public void launch_dictionaries_activity(View v) + { + startActivity(new Intent(this, DictionariesActivity.class)); + } + Animatable find_anim(int id) { ImageView img = (ImageView)findViewById(id); diff --git a/srcs/juloo.keyboard2/Utils.java b/srcs/juloo.keyboard2/Utils.java index 5cc1664..ae82474 100644 --- a/srcs/juloo.keyboard2/Utils.java +++ b/srcs/juloo.keyboard2/Utils.java @@ -9,6 +9,8 @@ import android.view.View; import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Locale; @@ -49,4 +51,14 @@ public final class Utils out.append(buff, 0, l); return out.toString(); } + + public static byte[] read_all_bytes(InputStream inp) throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buff = new byte[128000]; + int l; + while ((l = inp.read(buff)) != -1) + out.write(buff, 0, l); + return out.toByteArray(); + } } diff --git a/srcs/juloo.keyboard2/dict/Dictionaries.java b/srcs/juloo.keyboard2/dict/Dictionaries.java new file mode 100644 index 0000000..ac43c9b --- /dev/null +++ b/srcs/juloo.keyboard2/dict/Dictionaries.java @@ -0,0 +1,149 @@ +package juloo.keyboard2.dict; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Resources; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import juloo.cdict.Cdict; +import juloo.keyboard2.Logs; +import juloo.keyboard2.Utils; + +/** Manage and load installed dictionaries. */ +public final class Dictionaries +{ + public static Dictionaries instance(Context ctx) + { + if (_instance == null) + _instance = new Dictionaries(ctx); + return _instance; + } + + /** Util for finding a dictionary by name. Returns [null] if not found. */ + public static Cdict find_by_name(Cdict[] dicts, String name) + { + for (Cdict d : dicts) + if (d.name.equals(name)) + return d; + return null; + } + + /** Load an installed dictionary. Return [null] if the requested dictionary + is not installed or the dictionary couldn't be loaded. */ + public Cdict[] load(String dict_name) + { + if (_loaded_dictionaries.containsKey(dict_name)) + return _loaded_dictionaries.get(dict_name); + Cdict[] dict = load_uncached(dict_name); + _loaded_dictionaries.put(dict_name, dict); + return dict; + } + + public Set get_installed() { return _installed_dictionaries; } + + public void install(String dict_name, byte[] data) throws IOException + { + FileOutputStream outp = _context.openFileOutput(dict_file_name(dict_name), + Context.MODE_PRIVATE); + outp.write(data); + outp.close(); + set_installed(dict_name); + } + + /** Return the absolute path used to store the dictionary with the given + name. Return the same result whether the dictionary is installed or not. */ + public File get_install_location(String dict_name) + { + return _context.getFileStreamPath(dict_file_name(dict_name)); + } + + /** Declare a dictionary as installed. A dictionary file must exist at the + path returned by [get_install_location(dict_name)]. */ + public void set_installed(String dict_name) + { + _installed_dictionaries.add(dict_name); + _loaded_dictionaries.remove(dict_name); + save(); + } + + public void uninstall(String dict_name) + { + _context.deleteFile(dict_file_name(dict_name)); + _installed_dictionaries.remove(dict_name); + _loaded_dictionaries.remove(dict_name); + save(); + } + + /** Private */ + + Context _context; + Set _installed_dictionaries; + /** Might be 'null' when safe storage is not available. */ + SharedPreferences _shared_prefs; + Map _loaded_dictionaries; + + static Dictionaries _instance = null; + + static final String PREF_INSTALLED_DICTS = "installed"; + + Dictionaries(Context ctx) + { + _context = ctx; + _installed_dictionaries = new HashSet(); + _loaded_dictionaries = new TreeMap(); + load_prefs(); + } + + void load_prefs() + { + _shared_prefs = null; + try + { + _shared_prefs = + _context.getSharedPreferences("dictionaries", Context.MODE_PRIVATE); + Set s = _shared_prefs.getStringSet(PREF_INSTALLED_DICTS, null); + if (s != null) + _installed_dictionaries.addAll(s); + } + catch (Exception e) + { + Logs.exn("", e); + } + } + + Cdict[] load_uncached(String dict_name) + { + if (!_installed_dictionaries.contains(dict_name)) + return null; + try + { + FileInputStream inp = _context.openFileInput(dict_file_name(dict_name)); + byte[] data = Utils.read_all_bytes(inp); + inp.close(); + return Cdict.of_bytes(data); + } + catch (IOException e) { return null; } + catch (Cdict.ConstructionError e) { return null; } + } + + void save() + { + if (_shared_prefs == null) + return; + _shared_prefs.edit() + .putStringSet(PREF_INSTALLED_DICTS, _installed_dictionaries) + .commit(); + } + + static String dict_file_name(String dict_name) + { + return dict_name + ".dict"; + } +} diff --git a/srcs/juloo.keyboard2/dict/DictionariesActivity.java b/srcs/juloo.keyboard2/dict/DictionariesActivity.java new file mode 100644 index 0000000..4143107 --- /dev/null +++ b/srcs/juloo.keyboard2/dict/DictionariesActivity.java @@ -0,0 +1,15 @@ +package juloo.keyboard2.dict; + +import android.app.Activity; +import android.os.Bundle; +import juloo.keyboard2.R; + +public class DictionariesActivity extends Activity +{ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + setContentView(R.layout.dictionaries_activity); + } +} diff --git a/srcs/juloo.keyboard2/dict/DictionaryListView.java b/srcs/juloo.keyboard2/dict/DictionaryListView.java new file mode 100644 index 0000000..465d373 --- /dev/null +++ b/srcs/juloo.keyboard2/dict/DictionaryListView.java @@ -0,0 +1,191 @@ +package juloo.keyboard2.dict; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.zip.GZIPInputStream; +import juloo.cdict.Cdict; +import juloo.keyboard2.Config; +import juloo.keyboard2.DeviceLocales; +import juloo.keyboard2.Logs; +import juloo.keyboard2.R; +import juloo.keyboard2.Utils; + +public class DictionaryListView extends LinearLayout +{ + List _dict_views; + Dictionaries _dictionaries; + Set _pending = new HashSet(); + + public DictionaryListView(Context ctx, AttributeSet attrs) + { + super(ctx, attrs); + setOrientation(LinearLayout.VERTICAL); + _dictionaries = Dictionaries.instance(ctx); + inflate_views(ctx); + } + + void inflate_views(Context ctx) + { + DeviceLocales locales = DeviceLocales.load(ctx); + SupportedDictionaries ds = new SupportedDictionaries(ctx.getResources()); + DownloadBtnListener listener = this.new DownloadBtnListener(); + _dict_views = new ArrayList(); + for (DeviceLocales.Loc loc : locales.installed) + { + int idx = (loc.dictionary != null) ? ds.find(loc.dictionary) : -1; + if (idx >= 0) + { + DictView dv = new DictView(ctx, ds, idx, listener); + addView(dv.view); + _dict_views.add(dv); + } + } + refresh(); + } + + /** Update the "installed" status of item views. Meaning whether the + "download" or "delete" button is shown. */ + void refresh() + { + Set installed = _dictionaries.get_installed(); + for (DictView d : _dict_views) + d.refresh(installed, _pending); + } + + void toggle_installed(String dict_name) + { + run_dictionary_action(dict_name, new Runnable() + { + public void run() + { + if (_dictionaries.get_installed().contains(dict_name)) + _dictionaries.uninstall(dict_name); + else if (install_dictionary_from_internet(dict_name)) + post_toast(R.string.dictionaries_download_success); + else + post_toast(R.string.dictionaries_download_failed); + } + }); + } + + /** Run action [r] for dictionary [name] if no action is already running for + that dictionary. Calls [refresh] after the action completed. */ + void run_dictionary_action(String name, Runnable r) + { + if (_pending.contains(name)) + return; + _pending.add(name); + (new Thread() + { + public void run() + { + r.run(); + post(new Runnable() + { + public void run() + { + _pending.remove(name); + refresh(); + } + }); + } + }).start(); + refresh(); + } + + final class DownloadBtnListener implements View.OnClickListener + { + @Override + public void onClick(View v) + { + for (DictView dv : _dict_views) + if (dv.download_button == v) + toggle_installed(dv.dict_name); + } + } + + static final class DictView + { + public final View view; + public final String dict_name; + public final View download_button; + + public DictView(Context ctx, SupportedDictionaries ds, int dict_index, + DownloadBtnListener on_click) + { + view = View.inflate(ctx, R.layout.dictionary_download_item, null); + dict_name = ds.dict_name(dict_index); + float size_mb = ds.size(dict_index) / 1048576.f; + ((TextView)view.findViewById(R.id.dictionary_download_locale)) + .setText(ds.display_name(dict_index)); + ((TextView)view.findViewById(R.id.dictionary_download_size)) + .setText(NumberFormat.getInstance().format(size_mb) + "MB"); + download_button = view.findViewById(R.id.dictionary_download_button); + download_button.setOnClickListener(on_click); + } + + public void refresh(Set installed, Set pending) + { + download_button.setBackgroundResource(installed.contains(dict_name) + ? R.drawable.ic_delete : R.drawable.ic_download); + download_button.setVisibility(pending.contains(dict_name) + ? View.GONE : View.VISIBLE); + } + } + + static final String DICT_REPO_URL = + "https://github.com/Julow/Unexpected-Keyboard-dictionaries/raw/refs/heads/main"; + + static URL url_of_dictionary(String dict_name) + throws MalformedURLException + { + int format_version = 0; + return new URL(DICT_REPO_URL + "/v" + format_version + "/" + dict_name + + ".dict"); + } + + /** Returns [true] on success. */ + boolean install_dictionary_from_internet(String dict_name) + { + try + { + // Remote files are compressed with gzip at rest. Do not use server side + // compression and force decompression. + URLConnection con = url_of_dictionary(dict_name).openConnection(); + con.setRequestProperty("Accept-Encoding", "identity"); + byte[] data = Utils.read_all_bytes(new GZIPInputStream(con.getInputStream())); + Cdict.of_bytes(data); // Check that the dictionary can load. + _dictionaries.install(dict_name, data); + return true; + } + catch (Exception e) + { + Logs.exn("", e); + return false; + } + } + + void post_toast(int msg_id) + { + post(new Runnable() + { + public void run() + { + Toast.makeText(getContext(), msg_id, Toast.LENGTH_SHORT).show(); + } + }); + } +} diff --git a/srcs/juloo.keyboard2/dict/SupportedDictionaries.java b/srcs/juloo.keyboard2/dict/SupportedDictionaries.java new file mode 100644 index 0000000..879fccd --- /dev/null +++ b/srcs/juloo.keyboard2/dict/SupportedDictionaries.java @@ -0,0 +1,33 @@ +package juloo.keyboard2.dict; + +import android.content.res.Resources; +import java.util.Arrays; +import juloo.keyboard2.R; + +/** Access arrays in [dictionaries.xml]. */ +public class SupportedDictionaries +{ + public String[] locales; + public String[] names; + public int[] sizes; + + public SupportedDictionaries(Resources res) + { + locales = res.getStringArray(R.array.dictionaries_locale); + names = res.getStringArray(R.array.dictionaries_name); + sizes = res.getIntArray(R.array.dictionaries_size); + } + + /** Find the index for a given dictionary name. Return [-1] if not found. */ + public int find(String dict_name) + { + int i = Arrays.binarySearch(locales, dict_name); + return (i < 0) ? -1 : i; + } + + public int length() { return locales.length; } + + public String dict_name(int i) { return locales[i]; } + public String display_name(int i) { return names[i]; } + public int size(int i) { return sizes[i]; } +} diff --git a/srcs/juloo.keyboard2/suggestions/CandidatesView.java b/srcs/juloo.keyboard2/suggestions/CandidatesView.java index 259db35..b41177f 100644 --- a/srcs/juloo.keyboard2/suggestions/CandidatesView.java +++ b/srcs/juloo.keyboard2/suggestions/CandidatesView.java @@ -74,7 +74,7 @@ public class CandidatesView extends LinearLayout // The status message indicates whether the dictionaries should be // installed. _status_no_dict = inflate_and_show(_status_no_dict, - true, + (config.current_dictionary == null), R.layout.candidates_status_no_dict); set_height(config); } diff --git a/srcs/juloo.keyboard2/suggestions/Suggestions.java b/srcs/juloo.keyboard2/suggestions/Suggestions.java index 50c64e0..998d40d 100644 --- a/srcs/juloo.keyboard2/suggestions/Suggestions.java +++ b/srcs/juloo.keyboard2/suggestions/Suggestions.java @@ -2,28 +2,47 @@ package juloo.keyboard2.suggestions; import java.util.Arrays; import java.util.List; +import juloo.cdict.Cdict; +import juloo.keyboard2.dict.Dictionaries; +import juloo.keyboard2.Config; /** Keep track of the word being typed and provide suggestions for [CandidatesView]. */ public final class Suggestions { Callback _callback; + Config _config; - public Suggestions(Callback c) + public Suggestions(Callback c, Config conf) { _callback = c; + _config = conf; } public void currently_typed_word(String word) { - if (word.equals("")) + Cdict dict = _config.current_dictionary; + if (word.length() < 2 || dict == null) { _callback.set_suggestions(NO_SUGGESTIONS); } else { - // TODO - _callback.set_suggestions(Arrays.asList(word)); + Cdict.Result r = dict.find(word); + String[] suggestions = new String[3]; + int i = 0; + if (r.found) + suggestions[i++] = word; + int[] suffixes = dict.suffixes(r, 3); + int[] dist = dict.distance(word, 1, 3); + for (int j = 0; j < 3 && i < 3; j++) + { + if (suffixes.length > j) + suggestions[i++] = dict.word(suffixes[j]); + if (dist.length > j && i < 3) + suggestions[i++] = dict.word(dist[j]); + } + _callback.set_suggestions(Arrays.asList(suggestions)); } } -- cgit v1.2.3