diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 9ced7d3b..45de385d 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -19,4 +19,5 @@
Lade Benutzerwörterbuch…
Wörterbuch laden
Wird nicht geladen. Wörterbuch für %1$s nicht gefunden.
+ Wörterbuch löschen
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 8ee0e4e0..b7d2f0a5 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -19,4 +19,5 @@
Chargement du dictionnaire utilisateur…
Charger le dictionnaire
Echec du chargement. Dictionnaire %1$s introuvable.
+ Supprimer le dictionaire
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index aeaaf8f2..f15cca57 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -19,5 +19,6 @@
Caricamento dizionario utente…
Caricamento dizionario
Impossibile caricare. Dizionario per %1$s non trovato.
+ Eliminare il dizionario
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 5b9281cc..245f06dc 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -19,4 +19,5 @@
Завантаження словника користувача…
Завантажити словник
Помилка завантаження. Словник %1$s не знайдено.
+ Очистити словник
diff --git a/src/io/github/sspanak/tt9/db/DictionaryDb.java b/src/io/github/sspanak/tt9/db/DictionaryDb.java
index a897a084..7ebd17c7 100644
--- a/src/io/github/sspanak/tt9/db/DictionaryDb.java
+++ b/src/io/github/sspanak/tt9/db/DictionaryDb.java
@@ -1,6 +1,5 @@
package io.github.sspanak.tt9.db;
-import android.content.Context;
import android.database.sqlite.SQLiteConstraintException;
import android.os.Bundle;
import android.os.Handler;
@@ -15,6 +14,7 @@ import java.util.ArrayList;
import java.util.List;
import io.github.sspanak.tt9.Logger;
+import io.github.sspanak.tt9.ime.TraditionalT9;
import io.github.sspanak.tt9.languages.InvalidLanguageException;
import io.github.sspanak.tt9.languages.Language;
@@ -43,32 +43,32 @@ public class DictionaryDb {
};
- private static synchronized void createInstance(Context context) {
- dbInstance = Room.databaseBuilder(context, T9RoomDb.class, "t9dict.db")
+ private static synchronized void createInstance() {
+ dbInstance = Room.databaseBuilder(TraditionalT9.getMainContext(), T9RoomDb.class, "t9dict.db")
.addCallback(TRIGGER_CALLBACK)
.build();
}
- public static T9RoomDb getInstance(Context context) {
+ private static T9RoomDb getInstance() {
if (dbInstance == null) {
- createInstance(context);
+ createInstance();
}
return dbInstance;
}
- public static void beginTransaction(Context context) {
- getInstance(context).beginTransaction();
+ public static void beginTransaction() {
+ getInstance().beginTransaction();
}
- public static void endTransaction(Context context, boolean success) {
+ public static void endTransaction(boolean success) {
if (success) {
- getInstance(context).setTransactionSuccessful();
+ getInstance().setTransactionSuccessful();
}
- getInstance(context).endTransaction();
+ getInstance().endTransaction();
}
@@ -81,18 +81,18 @@ public class DictionaryDb {
}
- public static void truncateWords(Context context, Handler handler) {
+ public static void truncateWords(Handler handler) {
new Thread() {
@Override
public void run() {
- getInstance(context).clearAllTables();
+ getInstance().clearAllTables();
handler.sendEmptyMessage(0);
}
}.start();
}
- public static void insertWord(Context context, Handler handler, Language language, String word) throws Exception {
+ public static void insertWord(Handler handler, Language language, String word) throws Exception {
if (language == null) {
throw new InvalidLanguageException();
}
@@ -111,8 +111,8 @@ public class DictionaryDb {
@Override
public void run() {
try {
- getInstance(context).wordsDao().insert(dbWord);
- getInstance(context).wordsDao().incrementFrequency(dbWord.langId, dbWord.word, dbWord.sequence);
+ getInstance().wordsDao().insert(dbWord);
+ getInstance().wordsDao().incrementFrequency(dbWord.langId, dbWord.word, dbWord.sequence);
handler.sendEmptyMessage(0);
} catch (SQLiteConstraintException e) {
String msg = "Constraint violation when inserting a word: '" + dbWord.word + "' / sequence: '" + dbWord.sequence + "', for language: " + dbWord.langId;
@@ -128,12 +128,12 @@ public class DictionaryDb {
}
- public static void insertWordsSync(Context context, List words) {
- getInstance(context).wordsDao().insertMany(words);
+ public static void insertWordsSync(List words) {
+ getInstance().wordsDao().insertMany(words);
}
- public static void incrementWordFrequency(Context context, Language language, String word, String sequence) throws Exception {
+ public static void incrementWordFrequency(Language language, String word, String sequence) throws Exception {
if (language == null) {
throw new InvalidLanguageException();
}
@@ -153,7 +153,7 @@ public class DictionaryDb {
@Override
public void run() {
try {
- getInstance(context).wordsDao().incrementFrequency(language.getId(), word, sequence);
+ getInstance().wordsDao().incrementFrequency(language.getId(), word, sequence);
} catch (Exception e) {
Logger.e(
DictionaryDb.class.getName(),
@@ -165,7 +165,7 @@ public class DictionaryDb {
}
- public static void getSuggestions(Context context, Handler handler, Language language, String sequence, int minimumWords, int maximumWords) {
+ public static void getSuggestions(Handler handler, Language language, String sequence, int minimumWords, int maximumWords) {
final int minWords = Math.max(minimumWords, 0);
final int maxWords = Math.max(maximumWords, minimumWords);
@@ -173,24 +173,24 @@ public class DictionaryDb {
@Override
public void run() {
if (sequence == null || sequence.length() == 0) {
- Logger.w("tt9/getSuggestions", "Attempting to get suggestions for an empty sequence.");
+ Logger.w("tt9/db.getSuggestions", "Attempting to get suggestions for an empty sequence.");
sendSuggestions(handler, new ArrayList<>());
return;
}
if (language == null) {
- Logger.w("tt9/getSuggestions", "Attempting to get suggestions for NULL language.");
+ Logger.w("tt9/db.getSuggestions", "Attempting to get suggestions for NULL language.");
sendSuggestions(handler, new ArrayList<>());
return;
}
// get exact sequence matches, for example: "9422" -> "what"
- List exactMatches = getInstance(context).wordsDao().getMany(language.getId(), sequence, maxWords);
- Logger.d("getWords", "Exact matches: " + exactMatches.size());
+ List exactMatches = getInstance().wordsDao().getMany(language.getId(), sequence, maxWords);
+ Logger.d("db.getSuggestions", "Exact matches: " + exactMatches.size());
ArrayList suggestions = new ArrayList<>();
for (Word word : exactMatches) {
- Logger.d("getWords", "exact match: " + word.word + ", priority: " + word.frequency);
+ Logger.d("db.getSuggestions", "exact match: " + word.word + ", priority: " + word.frequency);
suggestions.add(word.word);
}
@@ -198,15 +198,19 @@ public class DictionaryDb {
// for example: "rol" => "roll", "roller", "rolling", ...
if (exactMatches.size() < minWords && sequence.length() >= 2) {
int extraWordsNeeded = minWords - exactMatches.size();
- List extraWords = getInstance(context).wordsDao().getFuzzy(language.getId(), sequence, extraWordsNeeded);
- Logger.d("getWords", "Fuzzy matches: " + extraWords.size());
+ List extraWords = getInstance().wordsDao().getFuzzy(language.getId(), sequence, extraWordsNeeded);
+ Logger.d("db.getSuggestions", "Fuzzy matches: " + extraWords.size());
for (Word word : extraWords) {
- Logger.d("getWords", "fuzzy match: " + word.word + ", sequence: " + word.sequence);
+ Logger.d("db.getSuggestions", "fuzzy match: " + word.word + ", sequence: " + word.sequence);
suggestions.add(word.word);
}
}
+ if (suggestions.size() == 0) {
+ Logger.i("db.getSuggestions", "No suggestions for sequence: " + sequence);
+ }
+
// pack the words in a message and send it to the calling thread
sendSuggestions(handler, suggestions);
}
diff --git a/src/io/github/sspanak/tt9/ime/InputFieldHelper.java b/src/io/github/sspanak/tt9/ime/InputFieldHelper.java
index f35813c7..da4ed7a6 100644
--- a/src/io/github/sspanak/tt9/ime/InputFieldHelper.java
+++ b/src/io/github/sspanak/tt9/ime/InputFieldHelper.java
@@ -10,6 +10,8 @@ import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import io.github.sspanak.tt9.ime.modes.InputMode;
+
class InputFieldHelper {
public static boolean isThereText(InputConnection currentInputConnection) {
@@ -79,7 +81,7 @@ class InputFieldHelper {
ArrayList allowedModes = new ArrayList<>();
if (inputField == null) {
- allowedModes.add(TraditionalT9.MODE_123);
+ allowedModes.add(InputMode.MODE_123);
return allowedModes;
}
@@ -90,8 +92,8 @@ class InputFieldHelper {
&& inputField.privateImeOptions.equals("io.github.sspanak.tt9.addword=true")
)
) {
- allowedModes.add(TraditionalT9.MODE_123);
- allowedModes.add(TraditionalT9.MODE_ABC);
+ allowedModes.add(InputMode.MODE_123);
+ allowedModes.add(InputMode.MODE_ABC);
return allowedModes;
}
@@ -103,7 +105,7 @@ class InputFieldHelper {
case InputType.TYPE_CLASS_PHONE:
// Phones will also default to the symbols keyboard, though
// often you will want to have a dedicated phone keyboard.
- allowedModes.add(TraditionalT9.MODE_123);
+ allowedModes.add(InputMode.MODE_123);
return allowedModes;
case InputType.TYPE_CLASS_TEXT:
@@ -112,7 +114,7 @@ class InputFieldHelper {
// be doing predictive text (showing candidates as the
// user types).
if (!isSpecializedTextField(inputField)) {
- allowedModes.add(TraditionalT9.MODE_PREDICTIVE);
+ allowedModes.add(InputMode.MODE_PREDICTIVE);
}
// ↓ fallthrough to add ABC and 123 modes ↓
@@ -120,8 +122,8 @@ class InputFieldHelper {
default:
// For all unknown input types, default to the alphabetic
// keyboard with no special features.
- allowedModes.add(TraditionalT9.MODE_123);
- allowedModes.add(TraditionalT9.MODE_ABC);
+ allowedModes.add(InputMode.MODE_123);
+ allowedModes.add(InputMode.MODE_ABC);
return allowedModes;
}
diff --git a/src/io/github/sspanak/tt9/ime/InputModeValidator.java b/src/io/github/sspanak/tt9/ime/InputModeValidator.java
index 80be48b6..c129587a 100644
--- a/src/io/github/sspanak/tt9/ime/InputModeValidator.java
+++ b/src/io/github/sspanak/tt9/ime/InputModeValidator.java
@@ -3,9 +3,10 @@ package io.github.sspanak.tt9.ime;
import java.util.ArrayList;
import io.github.sspanak.tt9.Logger;
-import io.github.sspanak.tt9.languages.definitions.English;
+import io.github.sspanak.tt9.ime.modes.InputMode;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection;
+import io.github.sspanak.tt9.languages.definitions.English;
import io.github.sspanak.tt9.preferences.T9Preferences;
public class InputModeValidator {
@@ -42,16 +43,16 @@ public class InputModeValidator {
return validLanguage;
}
- public static int validateMode(T9Preferences prefs, int inputMode, ArrayList allowedModes) {
- if (allowedModes.size() > 0 && allowedModes.contains(inputMode)) {
+ public static InputMode validateMode(T9Preferences prefs, InputMode inputMode, ArrayList allowedModes) {
+ if (allowedModes.size() > 0 && allowedModes.contains(inputMode.getId())) {
return inputMode;
}
- int newMode = allowedModes.size() > 0 ? allowedModes.get(0) : TraditionalT9.MODE_123;
+ InputMode newMode = InputMode.getInstance(allowedModes.size() > 0 ? allowedModes.get(0) : InputMode.MODE_123);
prefs.saveInputMode(newMode);
- if (newMode != inputMode) {
- Logger.w("tt9/validateMode", "Invalid input mode: " + inputMode + " Enforcing: " + newMode);
+ if (newMode.getId() != inputMode.getId()) {
+ Logger.w("tt9/validateMode", "Invalid input mode: " + inputMode.getId() + " Enforcing: " + newMode.getId());
}
return newMode;
@@ -62,7 +63,7 @@ public class InputModeValidator {
return textCase;
}
- int newCase = allowedTextCases.size() > 0 ? allowedTextCases.get(0) : TraditionalT9.CASE_LOWER;
+ int newCase = allowedTextCases.size() > 0 ? allowedTextCases.get(0) : InputMode.CASE_LOWER;
prefs.saveTextCase(newCase);
if (textCase != newCase) {
diff --git a/src/io/github/sspanak/tt9/ime/KeyPadHandler.java b/src/io/github/sspanak/tt9/ime/KeyPadHandler.java
index d8517748..23a098c4 100644
--- a/src/io/github/sspanak/tt9/ime/KeyPadHandler.java
+++ b/src/io/github/sspanak/tt9/ime/KeyPadHandler.java
@@ -7,8 +7,8 @@ import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
-import io.github.sspanak.tt9.ui.CandidateView;
import io.github.sspanak.tt9.preferences.T9Preferences;
+import io.github.sspanak.tt9.ui.CandidateView;
abstract class KeyPadHandler extends InputMethodService {
@@ -28,7 +28,7 @@ abstract class KeyPadHandler extends InputMethodService {
// temporal key handling
private int ignoreNextKeyUp = 0;
private int lastKeyCode = 0;
- protected boolean isNumKeyRepeated = false;
+ private boolean isNumKeyRepeated = false;
// throttling
private static final int BACKSPACE_DEBOUNCE_TIME = 80;
@@ -42,7 +42,7 @@ abstract class KeyPadHandler extends InputMethodService {
@Override
public void onCreate() {
super.onCreate();
- prefs = T9Preferences.getInstance(this);
+ prefs = new T9Preferences(getApplicationContext());
onInit();
}
@@ -95,8 +95,8 @@ abstract class KeyPadHandler extends InputMethodService {
@Override
public void onStartInput(EditorInfo inputField, boolean restarting) {
currentInputConnection = getCurrentInputConnection();
- // Logger.d("T9.onStartInput", "INPUTTYPE: " + inputField.inputType + " FIELDID: " + inputField.fieldId +
- // " FIELDNAME: " + inputField.fieldName + " PACKAGE NAME: " + inputField.packageName);
+ // Logger.d("T9.onStartInput", "inputType: " + inputField.inputType + " fieldId: " + inputField.fieldId +
+ // " fieldName: " + inputField.fieldName + " packageName: " + inputField.packageName);
mEditing = NON_EDIT;
@@ -219,7 +219,7 @@ abstract class KeyPadHandler extends InputMethodService {
}
switch (keyCode) {
- case KeyEvent.KEYCODE_0: return on0(true);
+ case KeyEvent.KEYCODE_0:
case KeyEvent.KEYCODE_1:
case KeyEvent.KEYCODE_2:
case KeyEvent.KEYCODE_3:
@@ -229,7 +229,7 @@ abstract class KeyPadHandler extends InputMethodService {
case KeyEvent.KEYCODE_7:
case KeyEvent.KEYCODE_8:
case KeyEvent.KEYCODE_9:
- return on1to9(keyCodeToKeyNumber(keyCode), true);
+ return onNumber(keyCodeToKeyNumber(keyCode), true, false);
}
ignoreNextKeyUp = 0;
@@ -275,7 +275,7 @@ abstract class KeyPadHandler extends InputMethodService {
}
if (keyCode == KeyEvent.KEYCODE_0) {
- return on0(false);
+ return onNumber(0, false, isNumKeyRepeated);
}
// dialer fields are similar to pure numeric fields, but for user convenience, holding "0"
@@ -305,7 +305,7 @@ abstract class KeyPadHandler extends InputMethodService {
case KeyEvent.KEYCODE_7:
case KeyEvent.KEYCODE_8:
case KeyEvent.KEYCODE_9:
- return on1to9(keyCodeToKeyNumber(keyCode), false);
+ return onNumber(keyCodeToKeyNumber(keyCode), false, isNumKeyRepeated);
case KeyEvent.KEYCODE_STAR: return onStar();
case KeyEvent.KEYCODE_POUND: return onPound();
}
@@ -378,8 +378,7 @@ abstract class KeyPadHandler extends InputMethodService {
abstract public boolean onOK();
abstract protected boolean onUp();
abstract protected boolean onDown();
- abstract protected boolean on0(boolean hold);
- abstract protected boolean on1to9(int key, boolean hold);
+ abstract protected boolean onNumber(int key, boolean hold, boolean repeat);
abstract protected boolean onStar();
abstract protected boolean onPound();
diff --git a/src/io/github/sspanak/tt9/ime/SoftKeyHandler.java b/src/io/github/sspanak/tt9/ime/SoftKeyHandler.java
index 274ad679..090f84e8 100644
--- a/src/io/github/sspanak/tt9/ime/SoftKeyHandler.java
+++ b/src/io/github/sspanak/tt9/ime/SoftKeyHandler.java
@@ -1,5 +1,6 @@
package io.github.sspanak.tt9.ime;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -8,18 +9,39 @@ import io.github.sspanak.tt9.ui.UI;
class SoftKeyHandler implements View.OnTouchListener {
private static final int[] buttons = { R.id.main_left, R.id.main_right, R.id.main_mid };
- private final TraditionalT9 parent;
+ private final TraditionalT9 tt9;
+ private View view = null;
- public SoftKeyHandler(View mainView, TraditionalT9 tt9) {
- this.parent = tt9;
- changeView(mainView);
+ public SoftKeyHandler(LayoutInflater layoutInflater, TraditionalT9 tt9) {
+ this.tt9 = tt9;
+
+ createView(layoutInflater);
}
- public void changeView(View v) {
- for (int buttonId : buttons) {
- View button = v.findViewById(buttonId);
- button.setOnTouchListener(this);
+ View createView(LayoutInflater layoutInflater) {
+ if (view == null) {
+ view = layoutInflater.inflate(R.layout.mainview, null);
+
+ for (int buttonId : buttons) {
+ view.findViewById(buttonId).setOnTouchListener(this);
+ }
+ }
+
+ return view;
+ }
+
+
+ void show() {
+ if (view != null) {
+ view.setVisibility(View.VISIBLE);
+ }
+ }
+
+
+ void hide() {
+ if (view != null) {
+ view.setVisibility(View.GONE);
}
}
@@ -30,17 +52,17 @@ class SoftKeyHandler implements View.OnTouchListener {
int buttonId = view.getId();
if (buttonId == R.id.main_left && action == MotionEvent.ACTION_UP) {
- UI.showPreferencesScreen(parent);
+ UI.showPreferencesScreen(tt9);
return view.performClick();
}
if (buttonId == R.id.main_mid && action == MotionEvent.ACTION_UP) {
- parent.onOK();
+ tt9.onOK();
return view.performClick();
}
if (buttonId == R.id.main_right && action == MotionEvent.AXIS_PRESSURE) {
- return parent.handleBackspaceHold();
+ return tt9.handleBackspaceHold();
}
return false;
diff --git a/src/io/github/sspanak/tt9/ime/TraditionalT9.java b/src/io/github/sspanak/tt9/ime/TraditionalT9.java
index 7c6cda70..5fbca7cf 100644
--- a/src/io/github/sspanak/tt9/ime/TraditionalT9.java
+++ b/src/io/github/sspanak/tt9/ime/TraditionalT9.java
@@ -1,5 +1,6 @@
package io.github.sspanak.tt9.ime;
+import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -9,31 +10,24 @@ import android.view.View;
import android.view.inputmethod.EditorInfo;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import io.github.sspanak.tt9.Logger;
-import io.github.sspanak.tt9.R;
-import io.github.sspanak.tt9.db.DictionaryDb;
+import io.github.sspanak.tt9.ime.modes.InputMode;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection;
-import io.github.sspanak.tt9.languages.Punctuation;
import io.github.sspanak.tt9.ui.UI;
public class TraditionalT9 extends KeyPadHandler {
+ private static TraditionalT9 self;
+
// input mode
- public static final int MODE_PREDICTIVE = 0;
- public static final int MODE_ABC = 1;
- public static final int MODE_123 = 2;
private ArrayList allowedInputModes = new ArrayList<>();
- private int mInputMode = MODE_123;
+ private InputMode mInputMode;
// text case
- public static final int CASE_LOWER = 0;
- public static final int CASE_CAPITALIZE = 1;
- public static final int CASE_UPPER = 2;
private ArrayList allowedTextCases = new ArrayList<>();
- private int mTextCase = CASE_LOWER;
+ private int mTextCase = InputMode.CASE_LOWER;
// language
protected ArrayList mEnabledLanguages;
@@ -41,16 +35,18 @@ public class TraditionalT9 extends KeyPadHandler {
// soft key view
private SoftKeyHandler softKeyHandler = null;
- private View softKeyView = null;
- // @todo: move predictive mode stuff in its own class in #66
- private String predictionSequence = "";
+
+
+ public static Context getMainContext() {
+ return self.getApplicationContext();
+ }
private void loadPreferences() {
mLanguage = LanguageCollection.getLanguage(prefs.getInputLanguage());
mEnabledLanguages = prefs.getEnabledLanguages();
- mInputMode = prefs.getInputMode();
+ mInputMode = InputMode.getInstance(prefs.getInputMode());
mTextCase = prefs.getTextCase();
}
@@ -67,8 +63,10 @@ public class TraditionalT9 extends KeyPadHandler {
protected void onInit() {
+ self = this;
+
if (softKeyHandler == null) {
- softKeyHandler = new SoftKeyHandler(getLayoutInflater().inflate(R.layout.mainview, null), this);
+ softKeyHandler = new SoftKeyHandler(getLayoutInflater(), this);
}
loadPreferences();
@@ -77,15 +75,18 @@ public class TraditionalT9 extends KeyPadHandler {
protected void onRestart(EditorInfo inputField) {
- // in case we are back from Preferences screen, update the language list
- mEnabledLanguages = prefs.getEnabledLanguages();
- validatePreferences();
+ // determine the valid state for the current input field and preferences
+ determineAllowedInputModes(inputField);
+ determineAllowedTextCases();
+ mEnabledLanguages = prefs.getEnabledLanguages(); // in case we are back from Preferences screen, update the language list
- // reset all UI elements
- predictionSequence = "";
+ // enforce a valid initial state
+ validatePreferences();
clearSuggestions();
+
+ // build the UI
UI.updateStatusIcon(this, mLanguage, mInputMode, mTextCase);
- displaySoftKeyMenu();
+ softKeyHandler.show();
if (!isInputViewShown()) {
showWindow(true);
}
@@ -93,37 +94,31 @@ public class TraditionalT9 extends KeyPadHandler {
requestShowSelf(1);
}
- determineAllowedInputModes(inputField);
- determineAllowedTextCases();
-
restoreAddedWordIfAny();
}
protected void onFinish() {
- predictionSequence = "";
clearSuggestions();
hideStatusIcon();
hideWindow();
- if (softKeyView != null) {
- softKeyView.setVisibility(View.GONE);
- }
+ softKeyHandler.hide();
}
public boolean onBackspace() {
if (!InputFieldHelper.isThereText(currentInputConnection)) {
Logger.d("onBackspace", "backspace ignored");
+ mInputMode.reset();
return false;
}
resetKeyRepeat();
- if (mInputMode == MODE_PREDICTIVE && predictionSequence.length() > 1) {
- predictionSequence = predictionSequence.substring(0, predictionSequence.length() - 1);
- applyPredictionSequence();
+ if (mInputMode.onBackspace()) {
+ getSuggestions();
} else {
commitCurrentSuggestion();
super.sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
@@ -139,6 +134,7 @@ public class TraditionalT9 extends KeyPadHandler {
acceptCurrentSuggestion();
resetKeyRepeat();
+ mInputMode.reset();
return !isSuggestionViewHidden();
}
@@ -153,72 +149,46 @@ public class TraditionalT9 extends KeyPadHandler {
return nextSuggestion();
}
+ /**
+ * onNumber
+ *
+ * @param key Must be a number from 1 to 9, not a "KeyEvent.KEYCODE_X"
+ * @param hold If "true" we are calling the handler, because the key is being held.
+ * @param repeat If "true" we are calling the handler, because the key was pressed more than once
+ * @return boolean
+ */
+ protected boolean onNumber(int key, boolean hold, boolean repeat) {
+ if (mInputMode.shouldAcceptCurrentSuggestion(key, hold, repeat)) {
+ acceptCurrentSuggestion();
+ }
- protected boolean on0(boolean hold) {
- if (!hold && nextSuggestionInModeAbc()) {
+ if (!mInputMode.onNumber(mLanguage, key, hold, repeat)) {
+ return false;
+ }
+
+ if (mInputMode.shouldSelectNextSuggestion() && !isSuggestionViewHidden()) {
+ nextSuggestion();
return true;
}
- acceptCurrentSuggestion();
-
- setSuggestions(
- mInputMode == MODE_ABC && !hold ? mLanguage.getKeyCharacters(0) : null,
- 0
- );
-
- if (hold) {
- String chr = mInputMode == MODE_123 ? "+" : "0";
- currentInputConnection.commitText(chr, 1);
- } else if (mInputMode == MODE_PREDICTIVE) {
- currentInputConnection.commitText(" ", 1);
- } else if (mInputMode == MODE_123) {
- currentInputConnection.commitText("0", 1);
+ if (mInputMode.getWord() != null) {
+ setText(mInputMode.getWord());
+ } else {
+ getSuggestions();
}
return true;
}
- /**
- * on1to9
- *
- * @param key Must be a number from 1 to 9, not a "KeyEvent.KEYCODE_X"
- * @param hold If "true" we are calling the handler, because the key is being held.
- * @return boolean
- */
- protected boolean on1to9(int key, boolean hold) {
- if (mInputMode == MODE_123) {
- return false;
- }
-
- if (hold) {
- commitCurrentSuggestion(); // commit the previous one before adding the "hold" character
- currentInputConnection.commitText(String.valueOf(key), 1);
- return true;
- } else if (wordInPredictiveMode(key)) {
- return true;
- } else if (emoticonInPredictiveMode(key)) {
- return true;
- } else if (nextSuggestionInModeAbc()) {
- return true;
- } else if (mInputMode == MODE_ABC || mInputMode == MODE_PREDICTIVE) {
- commitCurrentSuggestion(); // commit the previous one before suggesting the new one
- setSuggestions(mLanguage.getKeyCharacters(key, mTextCase == CASE_LOWER));
- setComposingTextFromCurrentSuggestion();
- return true;
- }
-
- return false;
- }
-
protected boolean onPound() {
- currentInputConnection.commitText("#", 1);
+ setText("#");
return true;
}
protected boolean onStar() {
- currentInputConnection.commitText("*", 1);
+ setText("*");
return true;
}
@@ -231,7 +201,7 @@ public class TraditionalT9 extends KeyPadHandler {
if (hold) {
nextLang();
} else {
- nextKeyMode();
+ nextInputMode();
}
return true;
@@ -254,7 +224,7 @@ public class TraditionalT9 extends KeyPadHandler {
protected boolean shouldTrackNumPress() {
- return mInputMode != TraditionalT9.MODE_123;
+ return mInputMode.shouldTrackNumPress();
}
@@ -274,9 +244,7 @@ public class TraditionalT9 extends KeyPadHandler {
}
mSuggestionView.scrollToSuggestion(-1);
-
- String word = mSuggestionView.getCurrentSuggestion();
- currentInputConnection.setComposingText(word, word.length());
+ setComposingTextFromCurrentSuggestion();
return true;
}
@@ -288,143 +256,46 @@ public class TraditionalT9 extends KeyPadHandler {
}
mSuggestionView.scrollToSuggestion(1);
-
- String word = mSuggestionView.getCurrentSuggestion();
- currentInputConnection.setComposingText(word, word.length());
-
- return true;
- }
-
-
- private boolean emoticonInPredictiveMode(int key) {
- if (key != 1 || mInputMode != MODE_PREDICTIVE || !isNumKeyRepeated) {
- return false;
- }
-
- setSuggestions(Punctuation.Emoticons);
setComposingTextFromCurrentSuggestion();
return true;
}
+ private void handleSuggestions(ArrayList suggestions, int maxWordLength) {
+ setSuggestions(suggestions);
- private boolean wordInPredictiveMode(int key) {
- if (
- mInputMode != MODE_PREDICTIVE ||
- // 0 is not a word, but space, so we handle it in on0().
- key == 0 ||
- // double 1 is not a word, but an emoticon and it is handled elsewhere
- (key == 1 && isNumKeyRepeated)
- ) {
- return false;
- }
-
-
- if (
- // Punctuation is considered "a word", so that we can increase the priority as needed
- // Also, it must break the current word.
- (key == 1 && predictionSequence.length() > 0) ||
- // On the other hand, letters also "break" punctuation.
- (key != 1 && predictionSequence.endsWith("1"))
- ) {
- acceptCurrentSuggestion();
- }
-
- predictionSequence += key;
- applyPredictionSequence();
-
- return true;
+ // Put the first suggestion in the text field,
+ // but cut it off to the length of the sequence (how many keys were pressed),
+ // for a more intuitive experience.
+ String word = mSuggestionView.getCurrentSuggestion();
+ word = word.substring(0, Math.min(maxWordLength, word.length()));
+ setComposingText(word);
}
- private final Handler handleSuggestions = new Handler(Looper.getMainLooper()) {
+ private final Handler handleSuggestionsAsync = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
- ArrayList suggestions = msg.getData().getStringArrayList("suggestions");
- suggestions = guessSuggestionsWhenNone(suggestions, mSuggestionView.getCurrentSuggestion());
-
- setSuggestions(suggestions);
- mSuggestionView.changeCase(mTextCase, mLanguage.getLocale());
-
- // Put the first suggestion in the text field,
- // but cut it off to the length of the sequence (how many keys were pressed),
- // for a more intuitive experience.
- String word = mSuggestionView.getCurrentSuggestion();
- word = mSuggestionView.getCurrentSuggestion().substring(0, Math.min(predictionSequence.length(), word.length()));
- currentInputConnection.setComposingText(word, word.length());
+ handleSuggestions(
+ msg.getData().getStringArrayList("suggestions"),
+ msg.getData().getInt("maxWordLength", 1000)
+ );
}
};
- private void applyPredictionSequence() {
- if (predictionSequence.length() == 0) {
- return;
- }
-
- DictionaryDb.getSuggestions(
- this,
- handleSuggestions,
- mLanguage,
- predictionSequence,
- prefs.getSuggestionsMin(),
- prefs.getSuggestionsMax()
- );
- }
-
- private ArrayList guessSuggestionsWhenNone(ArrayList suggestions, String lastWord) {
- if (
- (suggestions != null && suggestions.size() > 0) ||
- predictionSequence.length() == 0 ||
- predictionSequence.charAt(0) == '1'
- ) {
- return suggestions;
- }
-
- lastWord = lastWord.substring(0, Math.min(predictionSequence.length() - 1, lastWord.length()));
- try {
- int lastDigit = predictionSequence.charAt(predictionSequence.length() - 1) - '0';
- lastWord += mLanguage.getKeyCharacters(lastDigit).get(0);
- } catch (Exception e) {
- lastWord += predictionSequence.charAt(predictionSequence.length() - 1);
- }
-
- return new ArrayList<>(Collections.singletonList(lastWord));
- }
-
-
- private boolean nextSuggestionInModeAbc() {
- return isNumKeyRepeated && mInputMode == MODE_ABC && nextSuggestion();
- }
-
private void acceptCurrentSuggestion() {
- // bring this word up in the suggestions list next time
- if (mInputMode == MODE_PREDICTIVE) {
- String currentWord = mSuggestionView.getCurrentSuggestion();
- if (currentWord.length() == 0) {
- Logger.i("acceptCurrentSuggestion", "Current word is empty. Nothing to accept.");
- return;
- }
-
- try {
- String sequence = mLanguage.getDigitSequenceForWord(currentWord);
- DictionaryDb.incrementWordFrequency(this, mLanguage, currentWord, sequence);
- } catch (Exception e) {
- Logger.e(getClass().getName(), "Failed incrementing priority of word: '" + currentWord + "'. " + e.getMessage());
- }
- }
-
+ mInputMode.onAcceptSuggestion(mLanguage, mSuggestionView.getCurrentSuggestion());
commitCurrentSuggestion();
}
private void commitCurrentSuggestion() {
- predictionSequence = "";
-
// commit the current suggestion to the input field
if (!isSuggestionViewHidden()) {
if (mSuggestionView.getCurrentSuggestion().equals(" ")) {
// finishComposingText() seems to ignore a single space,
// so we have to force commit it.
- currentInputConnection.commitText(" ", 1);
+ setText(" ");
} else {
currentInputConnection.finishComposingText();
}
@@ -435,34 +306,47 @@ public class TraditionalT9 extends KeyPadHandler {
private void clearSuggestions() {
+ setSuggestions(null);
+
if (currentInputConnection != null) {
- currentInputConnection.setComposingText("", 1);
+ setComposingTextFromCurrentSuggestion();
currentInputConnection.finishComposingText();
}
-
- setSuggestions(null);
}
- protected void setSuggestions(List suggestions) {
- setSuggestions(suggestions, 0);
+ private void getSuggestions() {
+ if (!mInputMode.getSuggestionsAsync(handleSuggestionsAsync, mLanguage, mSuggestionView.getCurrentSuggestion())) {
+ handleSuggestions(mInputMode.getSuggestions(), 1);
+ }
}
- protected void setSuggestions(List suggestions, int initialSel) {
+ private void setSuggestions(List suggestions) {
if (mSuggestionView == null) {
return;
}
boolean show = suggestions != null && suggestions.size() > 0;
- mSuggestionView.setSuggestions(suggestions, initialSel);
+ mSuggestionView.setSuggestions(suggestions, 0);
+ mSuggestionView.changeCase(mTextCase, mLanguage.getLocale());
setCandidatesViewShown(show);
}
+ private void setText(String text) {
+ if (text != null) {
+ currentInputConnection.commitText(text, text.length());
+ }
+ }
- private void nextKeyMode() {
+ private void setComposingText(String text) {
+ currentInputConnection.setComposingText(text, 1);
+ }
+
+
+ private void nextInputMode() {
if (mEditing == EDITING_STRICT_NUMERIC || mEditing == EDITING_DIALER) {
clearSuggestions();
- mInputMode = MODE_123;
+ mInputMode = InputMode.getInstance(InputMode.MODE_123);
}
// when typing a word or viewing scrolling the suggestions, only change the case
else if (!isSuggestionViewHidden()) {
@@ -475,13 +359,13 @@ public class TraditionalT9 extends KeyPadHandler {
setComposingTextFromCurrentSuggestion();
}
// make "abc" and "ABC" separate modes from user perspective
- else if (mInputMode == MODE_ABC && mTextCase == CASE_LOWER) {
- mTextCase = CASE_UPPER;
+ else if (mInputMode.isABC() && mTextCase == InputMode.CASE_LOWER) {
+ mTextCase = InputMode.CASE_UPPER;
} else {
- int modeIndex = (allowedInputModes.indexOf(mInputMode) + 1) % allowedInputModes.size();
- mInputMode = allowedInputModes.get(modeIndex);
+ int modeIndex = (allowedInputModes.indexOf(mInputMode.getId()) + 1) % allowedInputModes.size();
+ mInputMode = InputMode.getInstance(allowedInputModes.get(modeIndex));
- mTextCase = mInputMode == MODE_PREDICTIVE ? CASE_CAPITALIZE : CASE_LOWER;
+ mTextCase = mInputMode.isPredictive() ? InputMode.CASE_CAPITALIZE : InputMode.CASE_LOWER;
}
// save the settings for the next time
@@ -493,7 +377,7 @@ public class TraditionalT9 extends KeyPadHandler {
private void setComposingTextFromCurrentSuggestion() {
if (!isSuggestionViewHidden()) {
- currentInputConnection.setComposingText(mSuggestionView.getCurrentSuggestion(), 1);
+ setComposingText(mSuggestionView.getCurrentSuggestion());
}
}
@@ -522,18 +406,18 @@ public class TraditionalT9 extends KeyPadHandler {
private void determineAllowedInputModes(EditorInfo inputField) {
allowedInputModes = InputFieldHelper.determineInputModes(inputField);
- int lastInputMode = prefs.getInputMode();
- if (allowedInputModes.contains(lastInputMode)) {
- mInputMode = lastInputMode;
- } else if (allowedInputModes.contains(TraditionalT9.MODE_ABC)) {
- mInputMode = TraditionalT9.MODE_ABC;
+ int lastInputModeId = prefs.getInputMode();
+ if (allowedInputModes.contains(lastInputModeId)) {
+ mInputMode = InputMode.getInstance(lastInputModeId);
+ } else if (allowedInputModes.contains(InputMode.MODE_ABC)) {
+ mInputMode = InputMode.getInstance(InputMode.MODE_ABC);
} else {
- mInputMode = allowedInputModes.get(0);
+ mInputMode = InputMode.getInstance(allowedInputModes.get(0));
}
if (InputFieldHelper.isDialerField(inputField)) {
mEditing = EDITING_DIALER;
- } else if (mInputMode == TraditionalT9.MODE_123 && allowedInputModes.size() == 1) {
+ } else if (mInputMode.is123() && allowedInputModes.size() == 1) {
mEditing = EDITING_STRICT_NUMERIC;
} else {
mEditing = InputFieldHelper.isFilterTextField(inputField) ? EDITING_NOSHOW : EDITING;
@@ -542,20 +426,8 @@ public class TraditionalT9 extends KeyPadHandler {
private void determineAllowedTextCases() {
- // @todo: determine case from input
-
- allowedTextCases = new ArrayList<>();
-
- if (mInputMode == TraditionalT9.MODE_PREDICTIVE) {
- allowedTextCases.add(TraditionalT9.CASE_LOWER);
- allowedTextCases.add(TraditionalT9.CASE_CAPITALIZE);
- allowedTextCases.add(TraditionalT9.CASE_UPPER);
- } else if (mInputMode == TraditionalT9.MODE_ABC) {
- allowedTextCases.add(TraditionalT9.CASE_LOWER);
- allowedTextCases.add(TraditionalT9.CASE_UPPER);
- } else {
- allowedTextCases.add(TraditionalT9.CASE_LOWER);
- }
+ allowedTextCases = mInputMode.getAllowedTextCases();
+ // @todo: determine the text case of the input and validate using the allowed ones
}
@@ -581,8 +453,8 @@ public class TraditionalT9 extends KeyPadHandler {
try {
Logger.d("restoreAddedWordIfAny", "Restoring word: '" + word + "'...");
- predictionSequence = mLanguage.getDigitSequenceForWord(word);
- currentInputConnection.commitText(word, word.length());
+ setText(word);
+ mInputMode.reset();
} catch (Exception e) {
Logger.w("tt9/restoreLastWord", "Could not restore the last added word. " + e.getMessage());
}
@@ -594,16 +466,6 @@ public class TraditionalT9 extends KeyPadHandler {
* Generates the actual UI of TT9.
*/
protected View createSoftKeyView() {
- if (softKeyView == null) {
- softKeyView = getLayoutInflater().inflate(R.layout.mainview, null);
- }
- softKeyHandler.changeView(softKeyView);
- return softKeyView;
- }
-
-
- private void displaySoftKeyMenu() {
- createSoftKeyView();
- softKeyView.setVisibility(View.VISIBLE);
+ return softKeyHandler.createView(getLayoutInflater());
}
}
diff --git a/src/io/github/sspanak/tt9/ime/modes/InputMode.java b/src/io/github/sspanak/tt9/ime/modes/InputMode.java
new file mode 100644
index 00000000..6cd007a9
--- /dev/null
+++ b/src/io/github/sspanak/tt9/ime/modes/InputMode.java
@@ -0,0 +1,77 @@
+package io.github.sspanak.tt9.ime.modes;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+
+import java.util.ArrayList;
+
+import io.github.sspanak.tt9.Logger;
+import io.github.sspanak.tt9.languages.Language;
+
+abstract public class InputMode {
+ // typing mode
+ public static final int MODE_PREDICTIVE = 0;
+ public static final int MODE_ABC = 1;
+ public static final int MODE_123 = 2;
+
+ // text case
+ public static final int CASE_LOWER = 0;
+ public static final int CASE_CAPITALIZE = 1;
+ public static final int CASE_UPPER = 2;
+ protected ArrayList allowedTextCases = new ArrayList<>();
+
+ // data
+ protected ArrayList suggestions = new ArrayList<>();
+ protected String word = null;
+
+
+ public static InputMode getInstance(int mode) {
+ switch(mode) {
+ case MODE_PREDICTIVE:
+ return new ModePredictive();
+ case MODE_ABC:
+ return new ModeABC();
+ default:
+ Logger.w("tt9/InputMode", "Defaulting to mode: " + Mode123.class.getName() + " for unknown InputMode: " + mode);
+ case MODE_123:
+ return new Mode123();
+ }
+ }
+
+ // Key handlers. Return "true" when handling the key or "false", when is nothing to do.
+ public boolean onBackspace() { return false; }
+ abstract public boolean onNumber(Language language, int key, boolean hold, boolean repeat);
+
+ // Suggestions
+ public void onAcceptSuggestion(Language language, String suggestion) {}
+ public ArrayList getSuggestions() { return suggestions; }
+ public boolean getSuggestionsAsync(Handler handler, Language language, String lastWord) { return false; }
+ protected void sendSuggestions(Handler handler, ArrayList suggestions, int maxWordLength) {
+ Bundle bundle = new Bundle();
+ bundle.putStringArrayList("suggestions", suggestions);
+ bundle.putInt("maxWordLength", maxWordLength);
+ Message msg = new Message();
+ msg.setData(bundle);
+ handler.sendMessage(msg);
+ }
+
+ // Word
+ public String getWord() { return word; }
+
+ // Mode identifiers
+ public boolean isPredictive() { return false; }
+ public boolean isABC() { return false; }
+ public boolean is123() { return false; }
+
+ // Utility
+ abstract public int getId();
+ public ArrayList getAllowedTextCases() { return allowedTextCases; }
+ public void reset() {
+ suggestions = new ArrayList<>();
+ word = null;
+ }
+ public boolean shouldTrackNumPress() { return true; }
+ public boolean shouldAcceptCurrentSuggestion(int key, boolean hold, boolean repeat) { return false; }
+ public boolean shouldSelectNextSuggestion() { return false; }
+}
diff --git a/src/io/github/sspanak/tt9/ime/modes/Mode123.java b/src/io/github/sspanak/tt9/ime/modes/Mode123.java
new file mode 100644
index 00000000..6d264415
--- /dev/null
+++ b/src/io/github/sspanak/tt9/ime/modes/Mode123.java
@@ -0,0 +1,28 @@
+package io.github.sspanak.tt9.ime.modes;
+
+import java.util.ArrayList;
+
+import io.github.sspanak.tt9.languages.Language;
+
+public class Mode123 extends InputMode {
+ public int getId() { return MODE_123; }
+
+ Mode123() {
+ allowedTextCases.add(CASE_LOWER);
+ }
+
+
+ public boolean onNumber(Language l, int key, boolean hold, boolean repeat) {
+ if (key != 0) {
+ return false;
+ }
+
+ suggestions = new ArrayList<>();
+ word = hold ? "+" : "0";
+ return true;
+ }
+
+
+ final public boolean is123() { return true; }
+ public boolean shouldTrackNumPress() { return false; }
+}
diff --git a/src/io/github/sspanak/tt9/ime/modes/ModeABC.java b/src/io/github/sspanak/tt9/ime/modes/ModeABC.java
new file mode 100644
index 00000000..512599b2
--- /dev/null
+++ b/src/io/github/sspanak/tt9/ime/modes/ModeABC.java
@@ -0,0 +1,38 @@
+package io.github.sspanak.tt9.ime.modes;
+
+import io.github.sspanak.tt9.languages.Language;
+
+public class ModeABC extends InputMode {
+ public int getId() { return MODE_ABC; }
+
+ private boolean shouldSelectNextLetter = false;
+
+ ModeABC() {
+ allowedTextCases.add(CASE_LOWER);
+ allowedTextCases.add(CASE_UPPER);
+ }
+
+
+ public boolean onNumber(Language language, int key, boolean hold, boolean repeat) {
+ shouldSelectNextLetter = false;
+ suggestions = language.getKeyCharacters(key);
+ word = null;
+
+ if (hold) {
+ suggestions = null;
+ word = String.valueOf(key);
+ } else if (repeat) {
+ suggestions = null;
+ shouldSelectNextLetter = true;
+ }
+
+ return true;
+ }
+
+
+ final public boolean isABC() { return true; }
+ public boolean shouldAcceptCurrentSuggestion(int key, boolean hold, boolean repeat) { return hold || !repeat; }
+ public boolean shouldSelectNextSuggestion() {
+ return shouldSelectNextLetter;
+ }
+}
diff --git a/src/io/github/sspanak/tt9/ime/modes/ModePredictive.java b/src/io/github/sspanak/tt9/ime/modes/ModePredictive.java
new file mode 100644
index 00000000..790bdc1b
--- /dev/null
+++ b/src/io/github/sspanak/tt9/ime/modes/ModePredictive.java
@@ -0,0 +1,196 @@
+package io.github.sspanak.tt9.ime.modes;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+import io.github.sspanak.tt9.Logger;
+import io.github.sspanak.tt9.db.DictionaryDb;
+import io.github.sspanak.tt9.languages.Language;
+import io.github.sspanak.tt9.languages.Punctuation;
+import io.github.sspanak.tt9.preferences.T9Preferences;
+
+public class ModePredictive extends InputMode {
+ public int getId() { return MODE_PREDICTIVE; }
+
+ private Language currentLanguage = null;
+ private String digitSequence = "";
+ private boolean isEmoticon = false;
+ private String lastInputFieldWord = "";
+ private static Handler handleSuggestionsExternal;
+
+
+
+ ModePredictive() {
+ allowedTextCases.add(CASE_CAPITALIZE);
+ allowedTextCases.add(CASE_LOWER);
+ allowedTextCases.add(CASE_UPPER);
+ }
+
+
+ public boolean onBackspace() {
+ if (digitSequence.length() < 1) {
+ return false;
+ }
+
+ digitSequence = digitSequence.substring(0, digitSequence.length() - 1);
+ return true;
+ }
+
+
+ public boolean onNumber(Language l, int key, boolean hold, boolean repeat) {
+ isEmoticon = false;
+
+ if (hold) {
+ // hold to type any digit
+ reset();
+ word = String.valueOf(key);
+ } else if (key == 0) {
+ // "0" is " "
+ reset();
+ word = " ";
+ } else if (key == 1 && repeat) {
+ // emoticons
+ reset();
+ isEmoticon = true;
+ suggestions = Punctuation.Emoticons;
+ }
+ else {
+ // words
+ super.reset();
+ digitSequence += key;
+ }
+
+ return true;
+ }
+
+
+ public void reset() {
+ super.reset();
+ digitSequence = "";
+ }
+
+
+ final public boolean isPredictive() {
+ return true;
+ }
+
+
+ /**
+ * shouldAcceptCurrentSuggestion
+ * In this mode, In addition to confirming the suggestion in the input field,
+ * we also increase its' priority. This function determines whether we want to do all this or not.
+ */
+ public boolean shouldAcceptCurrentSuggestion(int key, boolean hold, boolean repeat) {
+ return
+ hold
+ // Quickly accept suggestions using "space" instead of pressing "ok" then "space"
+ || key == 0
+ // Punctuation is considered "a word", so that we can increase the priority as needed
+ // Also, it must break the current word.
+ || (key == 1 && digitSequence.length() > 0 && !digitSequence.endsWith("1"))
+ // On the other hand, letters also "break" punctuation.
+ || (key != 1 && digitSequence.endsWith("1"));
+ }
+
+
+ /**
+ * getSuggestionsAsync
+ * Queries the dictionary database for a list of suggestions matching the current language and
+ * sequence. Returns "false" when there is nothing to do.
+ *
+ * "lastWord" is used for generating suggestions when there are no results.
+ * See: generateSuggestionWhenNone()
+ */
+ public boolean getSuggestionsAsync(Handler handler, Language language, String lastWord) {
+ if (isEmoticon) {
+ super.sendSuggestions(handler, suggestions, 2);
+ return true;
+ }
+
+ if (digitSequence.length() == 0) {
+ return false;
+ }
+
+ handleSuggestionsExternal = handler;
+ lastInputFieldWord = lastWord;
+ currentLanguage = language;
+ super.reset();
+
+ DictionaryDb.getSuggestions(
+ handleSuggestions,
+ language,
+ digitSequence,
+ T9Preferences.getInstance().getSuggestionsMin(),
+ T9Preferences.getInstance().getSuggestionsMax()
+ );
+
+ return true;
+ }
+
+
+ /**
+ * handleSuggestions
+ * Extracts the suggestions from the Message object and passes them to the actual external Handler.
+ * If there were no matches in the database, they will be generated based on the "lastInputFieldWord".
+ */
+ private final Handler handleSuggestions = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ ArrayList suggestions = msg.getData().getStringArrayList("suggestions");
+ suggestions = generateSuggestionWhenNone(suggestions, currentLanguage, lastInputFieldWord);
+
+ ModePredictive.super.sendSuggestions(handleSuggestionsExternal, suggestions, digitSequence.length());
+ }
+ };
+
+ /**
+ * generateSuggestionWhenNone
+ * When there are no matching suggestions after the last key press, generate a list of possible
+ * ones, so that the user can complete the missing word.
+ */
+ private ArrayList generateSuggestionWhenNone(ArrayList suggestions, Language language, String lastWord) {
+ if (
+ (lastWord == null || lastWord.length() == 0) ||
+ (suggestions != null && suggestions.size() > 0) ||
+ digitSequence.length() == 0 ||
+ digitSequence.charAt(0) == '1'
+ ) {
+ return suggestions;
+ }
+
+ lastWord = lastWord.substring(0, Math.min(digitSequence.length() - 1, lastWord.length()));
+ try {
+ int lastDigit = digitSequence.charAt(digitSequence.length() - 1) - '0';
+ lastWord += language.getKeyCharacters(lastDigit).get(0);
+ } catch (Exception e) {
+ lastWord += digitSequence.charAt(digitSequence.length() - 1);
+ }
+
+ return new ArrayList<>(Collections.singletonList(lastWord));
+ }
+
+
+ /**
+ * onAcceptSuggestion
+ * Bring this word up in the suggestions list next time.
+ */
+ public void onAcceptSuggestion(Language language, String currentWord) {
+ digitSequence = "";
+
+ if (currentWord.length() == 0) {
+ Logger.i("acceptCurrentSuggestion", "Current word is empty. Nothing to accept.");
+ return;
+ }
+
+ try {
+ String sequence = language.getDigitSequenceForWord(currentWord);
+ DictionaryDb.incrementWordFrequency(language, currentWord, sequence);
+ } catch (Exception e) {
+ Logger.e("tt9/ModePredictive", "Failed incrementing priority of word: '" + currentWord + "'. " + e.getMessage());
+ }
+ }
+}
diff --git a/src/io/github/sspanak/tt9/languages/Language.java b/src/io/github/sspanak/tt9/languages/Language.java
index e5d5d4c9..5ee4d989 100644
--- a/src/io/github/sspanak/tt9/languages/Language.java
+++ b/src/io/github/sspanak/tt9/languages/Language.java
@@ -39,15 +39,11 @@ public class Language {
}
public ArrayList getKeyCharacters(int key) {
- return getKeyCharacters(key, true);
- }
-
- public ArrayList getKeyCharacters(int key, boolean lowerCase) {
if (key < 0 || key >= characterMap.size()) {
return new ArrayList<>();
}
- ArrayList chars = lowerCase ? new ArrayList<>(characterMap.get(key)) : getUpperCaseChars(key);
+ ArrayList chars = new ArrayList<>(characterMap.get(key));
if (chars.size() > 0) {
chars.add(String.valueOf(key));
}
@@ -55,15 +51,6 @@ public class Language {
return chars;
}
- private ArrayList getUpperCaseChars(int mapId) {
- ArrayList uppercaseChars = new ArrayList<>();
- for (String ch : characterMap.get(mapId)) {
- uppercaseChars.add(ch.toUpperCase(locale));
- }
-
- return uppercaseChars;
- }
-
public String getDigitSequenceForWord(String word) throws Exception {
StringBuilder sequence = new StringBuilder();
String lowerCaseWord = word.toLowerCase(locale);
diff --git a/src/io/github/sspanak/tt9/preferences/T9Preferences.java b/src/io/github/sspanak/tt9/preferences/T9Preferences.java
index b4f95538..eb06b7ea 100644
--- a/src/io/github/sspanak/tt9/preferences/T9Preferences.java
+++ b/src/io/github/sspanak/tt9/preferences/T9Preferences.java
@@ -11,6 +11,7 @@ import java.util.Arrays;
import io.github.sspanak.tt9.Logger;
import io.github.sspanak.tt9.ime.TraditionalT9;
+import io.github.sspanak.tt9.ime.modes.InputMode;
import io.github.sspanak.tt9.languages.LanguageCollection;
@@ -23,13 +24,13 @@ public class T9Preferences {
private final SharedPreferences.Editor prefsEditor;
public T9Preferences (Context context) {
- prefs = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
+ prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefsEditor = prefs.edit();
}
- public static T9Preferences getInstance(Context context) {
+ public static T9Preferences getInstance() {
if (self == null) {
- self = new T9Preferences(context);
+ self = new T9Preferences(TraditionalT9.getMainContext());
}
return self;
@@ -101,13 +102,13 @@ public class T9Preferences {
}
public int getTextCase() {
- return prefs.getInt("pref_text_case", TraditionalT9.CASE_LOWER);
+ return prefs.getInt("pref_text_case", InputMode.CASE_LOWER);
}
public void saveTextCase(int textCase) {
boolean isTextCaseValid = isIntInList(
textCase,
- new ArrayList<>(Arrays.asList(TraditionalT9.CASE_CAPITALIZE, TraditionalT9.CASE_LOWER, TraditionalT9.CASE_UPPER)),
+ new ArrayList<>(Arrays.asList(InputMode.CASE_CAPITALIZE, InputMode.CASE_LOWER, InputMode.CASE_UPPER)),
"tt9/saveTextCase",
"Not saving invalid text case: " + textCase
);
@@ -131,21 +132,17 @@ public class T9Preferences {
}
public int getInputMode() {
- return prefs.getInt("pref_input_mode", TraditionalT9.MODE_PREDICTIVE);
+ return prefs.getInt("pref_input_mode", InputMode.MODE_PREDICTIVE);
}
- public void saveInputMode(int mode) {
- boolean isModeValid = isIntInList(
- mode,
- new ArrayList<>(Arrays.asList(TraditionalT9.MODE_123, TraditionalT9.MODE_ABC, TraditionalT9.MODE_PREDICTIVE)),
- "tt9/saveInputMode",
- "Not saving invalid text case: " + mode
- );
-
- if (isModeValid) {
- prefsEditor.putInt("pref_input_mode", mode);
- prefsEditor.apply();
+ public void saveInputMode(InputMode mode) {
+ if (mode == null) {
+ Logger.w("tt9/saveInputMode", "Not saving NULL input mode");
+ return;
}
+
+ prefsEditor.putInt("pref_input_mode", mode.getId());
+ prefsEditor.apply();
}
diff --git a/src/io/github/sspanak/tt9/settings_legacy/SettingMultiList.java b/src/io/github/sspanak/tt9/settings_legacy/SettingMultiList.java
index 0ee9731a..796f5a64 100644
--- a/src/io/github/sspanak/tt9/settings_legacy/SettingMultiList.java
+++ b/src/io/github/sspanak/tt9/settings_legacy/SettingMultiList.java
@@ -17,7 +17,7 @@ public class SettingMultiList extends SettingList {
public SettingMultiList (Context context, AttributeSet attrs, Object[] isettings) {
super(context, attrs, isettings);
selectedEntries = new boolean[entries.length];
- for (int langId : T9Preferences.getInstance(context).getEnabledLanguages()) {
+ for (int langId : T9Preferences.getInstance().getEnabledLanguages()) {
selectedEntries[langId - 1] = true; // languages are 1-based, unlike arrays
}
summary = buildItems();
@@ -39,7 +39,7 @@ public class SettingMultiList extends SettingList {
@Override
public void onClick(DialogInterface dialog, int which) {
if (id.equals("pref_lang_support")) {
- T9Preferences.getInstance(context).saveEnabledLanguages(buildSelection());
+ T9Preferences.getInstance().saveEnabledLanguages(buildSelection());
}
summary = buildItems();
dialog.dismiss();
diff --git a/src/io/github/sspanak/tt9/ui/AddWordAct.java b/src/io/github/sspanak/tt9/ui/AddWordAct.java
index cfcba285..decf3466 100644
--- a/src/io/github/sspanak/tt9/ui/AddWordAct.java
+++ b/src/io/github/sspanak/tt9/ui/AddWordAct.java
@@ -32,7 +32,7 @@ public class AddWordAct extends Activity {
View v = getLayoutInflater().inflate(R.layout.addwordview, null);
- EditText et = (EditText) v.findViewById(R.id.add_word_text);
+ EditText et = v.findViewById(R.id.add_word_text);
et.setText(word);
et.setSelection(word.length());
setContentView(v);
@@ -46,7 +46,7 @@ public class AddWordAct extends Activity {
switch (msg.what) {
case 0:
Logger.d("onAddedWord", "Added word: '" + word + "'...");
- T9Preferences.getInstance(main.getContext()).saveLastWord(word);
+ T9Preferences.getInstance().saveLastWord(word);
break;
case 1:
@@ -71,7 +71,7 @@ public class AddWordAct extends Activity {
word = ((EditText) main.findViewById(R.id.add_word_text)).getText().toString();
Logger.d("addWord", "Attempting to add word: '" + word + "'...");
- DictionaryDb.insertWord(this, onAddedWord, LanguageCollection.getLanguage(lang), word);
+ DictionaryDb.insertWord(onAddedWord, LanguageCollection.getLanguage(lang), word);
} catch (InsertBlankWordException e) {
Logger.e("AddWordAct.addWord", e.getMessage());
UI.toastLong(this, R.string.add_word_blank);
diff --git a/src/io/github/sspanak/tt9/ui/CandidateView.java b/src/io/github/sspanak/tt9/ui/CandidateView.java
index 2e1ea44e..f0dc34b5 100644
--- a/src/io/github/sspanak/tt9/ui/CandidateView.java
+++ b/src/io/github/sspanak/tt9/ui/CandidateView.java
@@ -13,7 +13,7 @@ import java.util.List;
import java.util.Locale;
import io.github.sspanak.tt9.R;
-import io.github.sspanak.tt9.ime.TraditionalT9;
+import io.github.sspanak.tt9.ime.modes.InputMode;
public class CandidateView extends View {
@@ -200,9 +200,9 @@ public class CandidateView extends View {
ArrayList newSuggestions = new ArrayList<>();
for (String s : mSuggestions) {
- if (textCase == TraditionalT9.CASE_LOWER) {
+ if (textCase == InputMode.CASE_LOWER) {
newSuggestions.add(s.toLowerCase(locale));
- } else if (textCase == TraditionalT9.CASE_CAPITALIZE) {
+ } else if (textCase == InputMode.CASE_CAPITALIZE) {
String cs = s.substring(0, 1).toUpperCase(locale) + s.substring(1).toLowerCase(locale);
newSuggestions.add(cs);
} else {
diff --git a/src/io/github/sspanak/tt9/ui/TraditionalT9Settings.java b/src/io/github/sspanak/tt9/ui/TraditionalT9Settings.java
index 0ac25b14..b9b43a0d 100644
--- a/src/io/github/sspanak/tt9/ui/TraditionalT9Settings.java
+++ b/src/io/github/sspanak/tt9/ui/TraditionalT9Settings.java
@@ -22,17 +22,6 @@ import android.widget.ListView;
import com.stackoverflow.answer.UnicodeBOMInputStream;
-import io.github.sspanak.tt9.Logger;
-import io.github.sspanak.tt9.R;
-import io.github.sspanak.tt9.db.DictionaryDb;
-import io.github.sspanak.tt9.db.Word;
-import io.github.sspanak.tt9.languages.Language;
-import io.github.sspanak.tt9.languages.LanguageCollection;
-import io.github.sspanak.tt9.preferences.T9Preferences;
-import io.github.sspanak.tt9.settings_legacy.CustomInflater;
-import io.github.sspanak.tt9.settings_legacy.Setting;
-import io.github.sspanak.tt9.settings_legacy.SettingAdapter;
-
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
@@ -47,6 +36,17 @@ import java.util.List;
import java.util.Locale;
import java.util.Properties;
+import io.github.sspanak.tt9.Logger;
+import io.github.sspanak.tt9.R;
+import io.github.sspanak.tt9.db.DictionaryDb;
+import io.github.sspanak.tt9.db.Word;
+import io.github.sspanak.tt9.languages.Language;
+import io.github.sspanak.tt9.languages.LanguageCollection;
+import io.github.sspanak.tt9.preferences.T9Preferences;
+import io.github.sspanak.tt9.settings_legacy.CustomInflater;
+import io.github.sspanak.tt9.settings_legacy.Setting;
+import io.github.sspanak.tt9.settings_legacy.SettingAdapter;
+
public class TraditionalT9Settings extends ListActivity implements DialogInterface.OnCancelListener {
AsyncTask task = null;
@@ -205,7 +205,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
// add characters first, then dictionary:
Logger.d("doInBackground", "Adding characters...");
- processChars(mContext, mSupportedLanguages);
+ processChars(mSupportedLanguages);
Logger.d("doInBackground", "Characters added.");
Logger.d("doInBackground", "Adding dict(s)...");
@@ -217,7 +217,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
if (internal) {
try {
dictstream = getAssets().open(dicts[x]);
- reply = processFile(mContext, dictstream, reply, mSupportedLanguages.get(x), dicts[x]);
+ reply = processFile(dictstream, reply, mSupportedLanguages.get(x), dicts[x]);
} catch (IOException e) {
e.printStackTrace();
reply.status = false;
@@ -227,7 +227,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
try {
dictstream = new FileInputStream(new File(
new File(Environment.getExternalStorageDirectory(), sddir), dicts[x]));
- reply = processFile(mContext, dictstream, reply, mSupportedLanguages.get(x), dicts[x]);
+ reply = processFile(dictstream, reply, mSupportedLanguages.get(x), dicts[x]);
} catch (FileNotFoundException e) {
reply.status = false;
reply.forceMsg("File not found: " + e.getMessage());
@@ -264,13 +264,13 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
* processChars
* Inserts single characters.
*/
- private void processChars(Context context, List allLanguages) {
+ private void processChars(List allLanguages) {
ArrayList list = new ArrayList<>();
try {
for (Language lang : allLanguages) {
for (int key = 0; key <= 9; key++) {
- for (String langChar : lang.getKeyCharacters(key, true)) {
+ for (String langChar : lang.getKeyCharacters(key)) {
if (langChar.length() == 1 && langChar.charAt(0) >= '0' && langChar.charAt(0) <= '9') {
// We do not want 0-9 as "word suggestions" in Predictive mode. It looks confusing
// when trying to type a word and also, one can type them by holding the respective
@@ -289,7 +289,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
}
}
- DictionaryDb.insertWordsSync(context, list);
+ DictionaryDb.insertWordsSync(list);
} catch (Exception e) {
Logger.e("processChars", e.getMessage());
}
@@ -306,7 +306,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
return null;
}
- private Reply processFile(Context context, InputStream is, Reply rpl, Language lang, String fname)
+ private Reply processFile(InputStream is, Reply rpl, Language lang, String fname)
throws LoadException, IOException {
long start = System.currentTimeMillis();
@@ -327,7 +327,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
try {
- DictionaryDb.beginTransaction(context);
+ DictionaryDb.beginTransaction();
while (fileWord != null) {
if (isCancelled()) {
@@ -365,7 +365,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
dbWords.add(word);
if (linecount % insertChunkSize == 0) {
- DictionaryDb.insertWordsSync(context, dbWords);
+ DictionaryDb.insertWordsSync(dbWords);
dbWords.clear();
}
@@ -376,13 +376,13 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
fileWord = getLine(br, rpl, fname);
}
- DictionaryDb.insertWordsSync(context, dbWords);
- DictionaryDb.endTransaction(context, true);
+ DictionaryDb.insertWordsSync(dbWords);
+ DictionaryDb.endTransaction(true);
dbWords.clear();
publishProgress((int) ((float) pos / size * 10000));
} catch (Exception e) {
- DictionaryDb.endTransaction(context, false);
+ DictionaryDb.endTransaction(false);
Logger.e("processFile", e.getMessage());
} finally {
br.close();
@@ -416,7 +416,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
// http://stackoverflow.com/questions/7645880/listview-with-onitemclicklistener-android
// get settings
- T9Preferences prefs = new T9Preferences(this);
+ T9Preferences prefs = new T9Preferences(getApplicationContext());
Object[] settings = {
prefs.getInputMode()
};
@@ -462,7 +462,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
UI.toast(mContext, R.string.dictionary_truncated);
}
};
- DictionaryDb.truncateWords(mContext, afterTruncate);
+ DictionaryDb.truncateWords(afterTruncate);
}
@@ -472,7 +472,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
task = new LoadDictTask(
msgid,
internal,
- LanguageCollection.getAll(T9Preferences.getInstance(mContext).getEnabledLanguages())
+ LanguageCollection.getAll(T9Preferences.getInstance().getEnabledLanguages())
);
task.execute();
}
diff --git a/src/io/github/sspanak/tt9/ui/UI.java b/src/io/github/sspanak/tt9/ui/UI.java
index 5e70f6df..cc7ff2bc 100644
--- a/src/io/github/sspanak/tt9/ui/UI.java
+++ b/src/io/github/sspanak/tt9/ui/UI.java
@@ -7,6 +7,7 @@ import android.widget.Toast;
import io.github.sspanak.tt9.Logger;
import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.ime.TraditionalT9;
+import io.github.sspanak.tt9.ime.modes.InputMode;
import io.github.sspanak.tt9.languages.Language;
public class UI {
@@ -34,21 +35,16 @@ public class UI {
* Set the status icon that is appropriate in current mode (based on
* openwmm-legacy)
*/
- public static void updateStatusIcon(TraditionalT9 tt9, Language inputLanguage, int inputMode, int textCase) {
- switch (inputMode) {
- case TraditionalT9.MODE_ABC:
- tt9.showStatusIcon(inputLanguage.getAbcIcon(textCase == TraditionalT9.CASE_LOWER));
- break;
- case TraditionalT9.MODE_PREDICTIVE:
- tt9.showStatusIcon(inputLanguage.getIcon());
- break;
- case TraditionalT9.MODE_123:
- tt9.showStatusIcon(R.drawable.ime_number);
- break;
- default:
- Logger.w("tt9.UI", "Unknown inputMode mode: " + inputMode + ". Hiding status icon.");
- tt9.hideStatusIcon();
- break;
+ public static void updateStatusIcon(TraditionalT9 tt9, Language inputLanguage, InputMode inputMode, int textCase) {
+ if (inputMode.isABC()) {
+ tt9.showStatusIcon(inputLanguage.getAbcIcon(textCase == InputMode.CASE_LOWER));
+ } else if (inputMode.isPredictive()) {
+ tt9.showStatusIcon(inputLanguage.getIcon());
+ } else if (inputMode.is123()) {
+ tt9.showStatusIcon(R.drawable.ime_number);
+ } else {
+ Logger.w("tt9.UI", "Unknown inputMode mode: " + inputMode + ". Hiding status icon.");
+ tt9.hideStatusIcon();
}
}