1
0
Fork 0

* each input mode is now in a separate file (#75)

* fixed mode 123 being incorrectly forced after typing in a numeric field, then going to a text field

* simplified context usage everywhere

* added some missing translations

* moved the Soft Key view to the SoftKey class to avoid memory leaks and to simplify the code a bit
This commit is contained in:
Dimo Karaivanov 2022-10-14 18:00:45 +03:00 committed by GitHub
parent 8d85215444
commit 575293edb9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 606 additions and 393 deletions

View file

@ -19,4 +19,5 @@
<string name="dictionary_loading_user_dict">Lade Benutzerwörterbuch…</string> <string name="dictionary_loading_user_dict">Lade Benutzerwörterbuch…</string>
<string name="dictionary_load_title">Wörterbuch laden</string> <string name="dictionary_load_title">Wörterbuch laden</string>
<string name="dictionary_not_found">Wird nicht geladen. Wörterbuch für %1$s nicht gefunden.</string> <string name="dictionary_not_found">Wird nicht geladen. Wörterbuch für %1$s nicht gefunden.</string>
<string name="pref_truncatedict">Wörterbuch löschen</string>
</resources> </resources>

View file

@ -19,4 +19,5 @@
<string name="dictionary_loading_user_dict">Chargement du dictionnaire utilisateur…</string> <string name="dictionary_loading_user_dict">Chargement du dictionnaire utilisateur…</string>
<string name="dictionary_load_title">Charger le dictionnaire</string> <string name="dictionary_load_title">Charger le dictionnaire</string>
<string name="dictionary_not_found">Echec du chargement. Dictionnaire %1$s introuvable.</string> <string name="dictionary_not_found">Echec du chargement. Dictionnaire %1$s introuvable.</string>
<string name="pref_truncatedict">Supprimer le dictionaire</string>
</resources> </resources>

View file

@ -19,5 +19,6 @@
<string name="dictionary_loading_user_dict">Caricamento dizionario utente…</string> <string name="dictionary_loading_user_dict">Caricamento dizionario utente…</string>
<string name="dictionary_load_title">Caricamento dizionario</string> <string name="dictionary_load_title">Caricamento dizionario</string>
<string name="dictionary_not_found">Impossibile caricare. Dizionario per %1$s non trovato.</string> <string name="dictionary_not_found">Impossibile caricare. Dizionario per %1$s non trovato.</string>
<string name="pref_truncatedict">Eliminare il dizionario</string>
</resources> </resources>

View file

@ -19,4 +19,5 @@
<string name="dictionary_loading_user_dict">Завантаження словника користувача…</string> <string name="dictionary_loading_user_dict">Завантаження словника користувача…</string>
<string name="dictionary_load_title">Завантажити словник</string> <string name="dictionary_load_title">Завантажити словник</string>
<string name="dictionary_not_found">Помилка завантаження. Словник %1$s не знайдено.</string> <string name="dictionary_not_found">Помилка завантаження. Словник %1$s не знайдено.</string>
<string name="pref_truncatedict">Очистити словник</string>
</resources> </resources>

View file

@ -1,6 +1,5 @@
package io.github.sspanak.tt9.db; package io.github.sspanak.tt9.db;
import android.content.Context;
import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteConstraintException;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@ -15,6 +14,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import io.github.sspanak.tt9.Logger; 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.InvalidLanguageException;
import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.Language;
@ -43,32 +43,32 @@ public class DictionaryDb {
}; };
private static synchronized void createInstance(Context context) { private static synchronized void createInstance() {
dbInstance = Room.databaseBuilder(context, T9RoomDb.class, "t9dict.db") dbInstance = Room.databaseBuilder(TraditionalT9.getMainContext(), T9RoomDb.class, "t9dict.db")
.addCallback(TRIGGER_CALLBACK) .addCallback(TRIGGER_CALLBACK)
.build(); .build();
} }
public static T9RoomDb getInstance(Context context) { private static T9RoomDb getInstance() {
if (dbInstance == null) { if (dbInstance == null) {
createInstance(context); createInstance();
} }
return dbInstance; return dbInstance;
} }
public static void beginTransaction(Context context) { public static void beginTransaction() {
getInstance(context).beginTransaction(); getInstance().beginTransaction();
} }
public static void endTransaction(Context context, boolean success) { public static void endTransaction(boolean success) {
if (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() { new Thread() {
@Override @Override
public void run() { public void run() {
getInstance(context).clearAllTables(); getInstance().clearAllTables();
handler.sendEmptyMessage(0); handler.sendEmptyMessage(0);
} }
}.start(); }.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) { if (language == null) {
throw new InvalidLanguageException(); throw new InvalidLanguageException();
} }
@ -111,8 +111,8 @@ public class DictionaryDb {
@Override @Override
public void run() { public void run() {
try { try {
getInstance(context).wordsDao().insert(dbWord); getInstance().wordsDao().insert(dbWord);
getInstance(context).wordsDao().incrementFrequency(dbWord.langId, dbWord.word, dbWord.sequence); getInstance().wordsDao().incrementFrequency(dbWord.langId, dbWord.word, dbWord.sequence);
handler.sendEmptyMessage(0); handler.sendEmptyMessage(0);
} catch (SQLiteConstraintException e) { } catch (SQLiteConstraintException e) {
String msg = "Constraint violation when inserting a word: '" + dbWord.word + "' / sequence: '" + dbWord.sequence + "', for language: " + dbWord.langId; 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<Word> words) { public static void insertWordsSync(List<Word> words) {
getInstance(context).wordsDao().insertMany(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) { if (language == null) {
throw new InvalidLanguageException(); throw new InvalidLanguageException();
} }
@ -153,7 +153,7 @@ public class DictionaryDb {
@Override @Override
public void run() { public void run() {
try { try {
getInstance(context).wordsDao().incrementFrequency(language.getId(), word, sequence); getInstance().wordsDao().incrementFrequency(language.getId(), word, sequence);
} catch (Exception e) { } catch (Exception e) {
Logger.e( Logger.e(
DictionaryDb.class.getName(), 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 minWords = Math.max(minimumWords, 0);
final int maxWords = Math.max(maximumWords, minimumWords); final int maxWords = Math.max(maximumWords, minimumWords);
@ -173,24 +173,24 @@ public class DictionaryDb {
@Override @Override
public void run() { public void run() {
if (sequence == null || sequence.length() == 0) { 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<>()); sendSuggestions(handler, new ArrayList<>());
return; return;
} }
if (language == null) { 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<>()); sendSuggestions(handler, new ArrayList<>());
return; return;
} }
// get exact sequence matches, for example: "9422" -> "what" // get exact sequence matches, for example: "9422" -> "what"
List<Word> exactMatches = getInstance(context).wordsDao().getMany(language.getId(), sequence, maxWords); List<Word> exactMatches = getInstance().wordsDao().getMany(language.getId(), sequence, maxWords);
Logger.d("getWords", "Exact matches: " + exactMatches.size()); Logger.d("db.getSuggestions", "Exact matches: " + exactMatches.size());
ArrayList<String> suggestions = new ArrayList<>(); ArrayList<String> suggestions = new ArrayList<>();
for (Word word : exactMatches) { 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); suggestions.add(word.word);
} }
@ -198,15 +198,19 @@ public class DictionaryDb {
// for example: "rol" => "roll", "roller", "rolling", ... // for example: "rol" => "roll", "roller", "rolling", ...
if (exactMatches.size() < minWords && sequence.length() >= 2) { if (exactMatches.size() < minWords && sequence.length() >= 2) {
int extraWordsNeeded = minWords - exactMatches.size(); int extraWordsNeeded = minWords - exactMatches.size();
List<Word> extraWords = getInstance(context).wordsDao().getFuzzy(language.getId(), sequence, extraWordsNeeded); List<Word> extraWords = getInstance().wordsDao().getFuzzy(language.getId(), sequence, extraWordsNeeded);
Logger.d("getWords", "Fuzzy matches: " + extraWords.size()); Logger.d("db.getSuggestions", "Fuzzy matches: " + extraWords.size());
for (Word word : extraWords) { 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); 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 // pack the words in a message and send it to the calling thread
sendSuggestions(handler, suggestions); sendSuggestions(handler, suggestions);
} }

View file

@ -10,6 +10,8 @@ import java.util.ArrayList;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import io.github.sspanak.tt9.ime.modes.InputMode;
class InputFieldHelper { class InputFieldHelper {
public static boolean isThereText(InputConnection currentInputConnection) { public static boolean isThereText(InputConnection currentInputConnection) {
@ -79,7 +81,7 @@ class InputFieldHelper {
ArrayList<Integer> allowedModes = new ArrayList<>(); ArrayList<Integer> allowedModes = new ArrayList<>();
if (inputField == null) { if (inputField == null) {
allowedModes.add(TraditionalT9.MODE_123); allowedModes.add(InputMode.MODE_123);
return allowedModes; return allowedModes;
} }
@ -90,8 +92,8 @@ class InputFieldHelper {
&& inputField.privateImeOptions.equals("io.github.sspanak.tt9.addword=true") && inputField.privateImeOptions.equals("io.github.sspanak.tt9.addword=true")
) )
) { ) {
allowedModes.add(TraditionalT9.MODE_123); allowedModes.add(InputMode.MODE_123);
allowedModes.add(TraditionalT9.MODE_ABC); allowedModes.add(InputMode.MODE_ABC);
return allowedModes; return allowedModes;
} }
@ -103,7 +105,7 @@ class InputFieldHelper {
case InputType.TYPE_CLASS_PHONE: case InputType.TYPE_CLASS_PHONE:
// Phones will also default to the symbols keyboard, though // Phones will also default to the symbols keyboard, though
// often you will want to have a dedicated phone keyboard. // often you will want to have a dedicated phone keyboard.
allowedModes.add(TraditionalT9.MODE_123); allowedModes.add(InputMode.MODE_123);
return allowedModes; return allowedModes;
case InputType.TYPE_CLASS_TEXT: case InputType.TYPE_CLASS_TEXT:
@ -112,7 +114,7 @@ class InputFieldHelper {
// be doing predictive text (showing candidates as the // be doing predictive text (showing candidates as the
// user types). // user types).
if (!isSpecializedTextField(inputField)) { if (!isSpecializedTextField(inputField)) {
allowedModes.add(TraditionalT9.MODE_PREDICTIVE); allowedModes.add(InputMode.MODE_PREDICTIVE);
} }
// fallthrough to add ABC and 123 modes // fallthrough to add ABC and 123 modes
@ -120,8 +122,8 @@ class InputFieldHelper {
default: default:
// For all unknown input types, default to the alphabetic // For all unknown input types, default to the alphabetic
// keyboard with no special features. // keyboard with no special features.
allowedModes.add(TraditionalT9.MODE_123); allowedModes.add(InputMode.MODE_123);
allowedModes.add(TraditionalT9.MODE_ABC); allowedModes.add(InputMode.MODE_ABC);
return allowedModes; return allowedModes;
} }

View file

@ -3,9 +3,10 @@ package io.github.sspanak.tt9.ime;
import java.util.ArrayList; import java.util.ArrayList;
import io.github.sspanak.tt9.Logger; 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.Language;
import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.languages.LanguageCollection;
import io.github.sspanak.tt9.languages.definitions.English;
import io.github.sspanak.tt9.preferences.T9Preferences; import io.github.sspanak.tt9.preferences.T9Preferences;
public class InputModeValidator { public class InputModeValidator {
@ -42,16 +43,16 @@ public class InputModeValidator {
return validLanguage; return validLanguage;
} }
public static int validateMode(T9Preferences prefs, int inputMode, ArrayList<Integer> allowedModes) { public static InputMode validateMode(T9Preferences prefs, InputMode inputMode, ArrayList<Integer> allowedModes) {
if (allowedModes.size() > 0 && allowedModes.contains(inputMode)) { if (allowedModes.size() > 0 && allowedModes.contains(inputMode.getId())) {
return inputMode; 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); prefs.saveInputMode(newMode);
if (newMode != inputMode) { if (newMode.getId() != inputMode.getId()) {
Logger.w("tt9/validateMode", "Invalid input mode: " + inputMode + " Enforcing: " + newMode); Logger.w("tt9/validateMode", "Invalid input mode: " + inputMode.getId() + " Enforcing: " + newMode.getId());
} }
return newMode; return newMode;
@ -62,7 +63,7 @@ public class InputModeValidator {
return textCase; 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); prefs.saveTextCase(newCase);
if (textCase != newCase) { if (textCase != newCase) {

View file

@ -7,8 +7,8 @@ import android.view.View;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnection;
import io.github.sspanak.tt9.ui.CandidateView;
import io.github.sspanak.tt9.preferences.T9Preferences; import io.github.sspanak.tt9.preferences.T9Preferences;
import io.github.sspanak.tt9.ui.CandidateView;
abstract class KeyPadHandler extends InputMethodService { abstract class KeyPadHandler extends InputMethodService {
@ -28,7 +28,7 @@ abstract class KeyPadHandler extends InputMethodService {
// temporal key handling // temporal key handling
private int ignoreNextKeyUp = 0; private int ignoreNextKeyUp = 0;
private int lastKeyCode = 0; private int lastKeyCode = 0;
protected boolean isNumKeyRepeated = false; private boolean isNumKeyRepeated = false;
// throttling // throttling
private static final int BACKSPACE_DEBOUNCE_TIME = 80; private static final int BACKSPACE_DEBOUNCE_TIME = 80;
@ -42,7 +42,7 @@ abstract class KeyPadHandler extends InputMethodService {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
prefs = T9Preferences.getInstance(this); prefs = new T9Preferences(getApplicationContext());
onInit(); onInit();
} }
@ -95,8 +95,8 @@ abstract class KeyPadHandler extends InputMethodService {
@Override @Override
public void onStartInput(EditorInfo inputField, boolean restarting) { public void onStartInput(EditorInfo inputField, boolean restarting) {
currentInputConnection = getCurrentInputConnection(); currentInputConnection = getCurrentInputConnection();
// Logger.d("T9.onStartInput", "INPUTTYPE: " + inputField.inputType + " FIELDID: " + inputField.fieldId + // Logger.d("T9.onStartInput", "inputType: " + inputField.inputType + " fieldId: " + inputField.fieldId +
// " FIELDNAME: " + inputField.fieldName + " PACKAGE NAME: " + inputField.packageName); // " fieldName: " + inputField.fieldName + " packageName: " + inputField.packageName);
mEditing = NON_EDIT; mEditing = NON_EDIT;
@ -219,7 +219,7 @@ abstract class KeyPadHandler extends InputMethodService {
} }
switch (keyCode) { switch (keyCode) {
case KeyEvent.KEYCODE_0: return on0(true); case KeyEvent.KEYCODE_0:
case KeyEvent.KEYCODE_1: case KeyEvent.KEYCODE_1:
case KeyEvent.KEYCODE_2: case KeyEvent.KEYCODE_2:
case KeyEvent.KEYCODE_3: case KeyEvent.KEYCODE_3:
@ -229,7 +229,7 @@ abstract class KeyPadHandler extends InputMethodService {
case KeyEvent.KEYCODE_7: case KeyEvent.KEYCODE_7:
case KeyEvent.KEYCODE_8: case KeyEvent.KEYCODE_8:
case KeyEvent.KEYCODE_9: case KeyEvent.KEYCODE_9:
return on1to9(keyCodeToKeyNumber(keyCode), true); return onNumber(keyCodeToKeyNumber(keyCode), true, false);
} }
ignoreNextKeyUp = 0; ignoreNextKeyUp = 0;
@ -275,7 +275,7 @@ abstract class KeyPadHandler extends InputMethodService {
} }
if (keyCode == KeyEvent.KEYCODE_0) { 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" // 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_7:
case KeyEvent.KEYCODE_8: case KeyEvent.KEYCODE_8:
case KeyEvent.KEYCODE_9: case KeyEvent.KEYCODE_9:
return on1to9(keyCodeToKeyNumber(keyCode), false); return onNumber(keyCodeToKeyNumber(keyCode), false, isNumKeyRepeated);
case KeyEvent.KEYCODE_STAR: return onStar(); case KeyEvent.KEYCODE_STAR: return onStar();
case KeyEvent.KEYCODE_POUND: return onPound(); case KeyEvent.KEYCODE_POUND: return onPound();
} }
@ -378,8 +378,7 @@ abstract class KeyPadHandler extends InputMethodService {
abstract public boolean onOK(); abstract public boolean onOK();
abstract protected boolean onUp(); abstract protected boolean onUp();
abstract protected boolean onDown(); abstract protected boolean onDown();
abstract protected boolean on0(boolean hold); abstract protected boolean onNumber(int key, boolean hold, boolean repeat);
abstract protected boolean on1to9(int key, boolean hold);
abstract protected boolean onStar(); abstract protected boolean onStar();
abstract protected boolean onPound(); abstract protected boolean onPound();

View file

@ -1,5 +1,6 @@
package io.github.sspanak.tt9.ime; package io.github.sspanak.tt9.ime;
import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
@ -8,18 +9,39 @@ import io.github.sspanak.tt9.ui.UI;
class SoftKeyHandler implements View.OnTouchListener { class SoftKeyHandler implements View.OnTouchListener {
private static final int[] buttons = { R.id.main_left, R.id.main_right, R.id.main_mid }; 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) { public SoftKeyHandler(LayoutInflater layoutInflater, TraditionalT9 tt9) {
this.parent = tt9; this.tt9 = tt9;
changeView(mainView);
createView(layoutInflater);
} }
public void changeView(View v) { View createView(LayoutInflater layoutInflater) {
for (int buttonId : buttons) { if (view == null) {
View button = v.findViewById(buttonId); view = layoutInflater.inflate(R.layout.mainview, null);
button.setOnTouchListener(this);
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(); int buttonId = view.getId();
if (buttonId == R.id.main_left && action == MotionEvent.ACTION_UP) { if (buttonId == R.id.main_left && action == MotionEvent.ACTION_UP) {
UI.showPreferencesScreen(parent); UI.showPreferencesScreen(tt9);
return view.performClick(); return view.performClick();
} }
if (buttonId == R.id.main_mid && action == MotionEvent.ACTION_UP) { if (buttonId == R.id.main_mid && action == MotionEvent.ACTION_UP) {
parent.onOK(); tt9.onOK();
return view.performClick(); return view.performClick();
} }
if (buttonId == R.id.main_right && action == MotionEvent.AXIS_PRESSURE) { if (buttonId == R.id.main_right && action == MotionEvent.AXIS_PRESSURE) {
return parent.handleBackspaceHold(); return tt9.handleBackspaceHold();
} }
return false; return false;

View file

@ -1,5 +1,6 @@
package io.github.sspanak.tt9.ime; package io.github.sspanak.tt9.ime;
import android.content.Context;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@ -9,31 +10,24 @@ import android.view.View;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import io.github.sspanak.tt9.Logger; import io.github.sspanak.tt9.Logger;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.ime.modes.InputMode;
import io.github.sspanak.tt9.db.DictionaryDb;
import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.languages.LanguageCollection;
import io.github.sspanak.tt9.languages.Punctuation;
import io.github.sspanak.tt9.ui.UI; import io.github.sspanak.tt9.ui.UI;
public class TraditionalT9 extends KeyPadHandler { public class TraditionalT9 extends KeyPadHandler {
private static TraditionalT9 self;
// input mode // 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<Integer> allowedInputModes = new ArrayList<>(); private ArrayList<Integer> allowedInputModes = new ArrayList<>();
private int mInputMode = MODE_123; private InputMode mInputMode;
// text case // 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<Integer> allowedTextCases = new ArrayList<>(); private ArrayList<Integer> allowedTextCases = new ArrayList<>();
private int mTextCase = CASE_LOWER; private int mTextCase = InputMode.CASE_LOWER;
// language // language
protected ArrayList<Integer> mEnabledLanguages; protected ArrayList<Integer> mEnabledLanguages;
@ -41,16 +35,18 @@ public class TraditionalT9 extends KeyPadHandler {
// soft key view // soft key view
private SoftKeyHandler softKeyHandler = null; 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() { private void loadPreferences() {
mLanguage = LanguageCollection.getLanguage(prefs.getInputLanguage()); mLanguage = LanguageCollection.getLanguage(prefs.getInputLanguage());
mEnabledLanguages = prefs.getEnabledLanguages(); mEnabledLanguages = prefs.getEnabledLanguages();
mInputMode = prefs.getInputMode(); mInputMode = InputMode.getInstance(prefs.getInputMode());
mTextCase = prefs.getTextCase(); mTextCase = prefs.getTextCase();
} }
@ -67,8 +63,10 @@ public class TraditionalT9 extends KeyPadHandler {
protected void onInit() { protected void onInit() {
self = this;
if (softKeyHandler == null) { if (softKeyHandler == null) {
softKeyHandler = new SoftKeyHandler(getLayoutInflater().inflate(R.layout.mainview, null), this); softKeyHandler = new SoftKeyHandler(getLayoutInflater(), this);
} }
loadPreferences(); loadPreferences();
@ -77,15 +75,18 @@ public class TraditionalT9 extends KeyPadHandler {
protected void onRestart(EditorInfo inputField) { protected void onRestart(EditorInfo inputField) {
// in case we are back from Preferences screen, update the language list // determine the valid state for the current input field and preferences
mEnabledLanguages = prefs.getEnabledLanguages(); determineAllowedInputModes(inputField);
validatePreferences(); determineAllowedTextCases();
mEnabledLanguages = prefs.getEnabledLanguages(); // in case we are back from Preferences screen, update the language list
// reset all UI elements // enforce a valid initial state
predictionSequence = ""; validatePreferences();
clearSuggestions(); clearSuggestions();
// build the UI
UI.updateStatusIcon(this, mLanguage, mInputMode, mTextCase); UI.updateStatusIcon(this, mLanguage, mInputMode, mTextCase);
displaySoftKeyMenu(); softKeyHandler.show();
if (!isInputViewShown()) { if (!isInputViewShown()) {
showWindow(true); showWindow(true);
} }
@ -93,37 +94,31 @@ public class TraditionalT9 extends KeyPadHandler {
requestShowSelf(1); requestShowSelf(1);
} }
determineAllowedInputModes(inputField);
determineAllowedTextCases();
restoreAddedWordIfAny(); restoreAddedWordIfAny();
} }
protected void onFinish() { protected void onFinish() {
predictionSequence = "";
clearSuggestions(); clearSuggestions();
hideStatusIcon(); hideStatusIcon();
hideWindow(); hideWindow();
if (softKeyView != null) { softKeyHandler.hide();
softKeyView.setVisibility(View.GONE);
}
} }
public boolean onBackspace() { public boolean onBackspace() {
if (!InputFieldHelper.isThereText(currentInputConnection)) { if (!InputFieldHelper.isThereText(currentInputConnection)) {
Logger.d("onBackspace", "backspace ignored"); Logger.d("onBackspace", "backspace ignored");
mInputMode.reset();
return false; return false;
} }
resetKeyRepeat(); resetKeyRepeat();
if (mInputMode == MODE_PREDICTIVE && predictionSequence.length() > 1) { if (mInputMode.onBackspace()) {
predictionSequence = predictionSequence.substring(0, predictionSequence.length() - 1); getSuggestions();
applyPredictionSequence();
} else { } else {
commitCurrentSuggestion(); commitCurrentSuggestion();
super.sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); super.sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
@ -139,6 +134,7 @@ public class TraditionalT9 extends KeyPadHandler {
acceptCurrentSuggestion(); acceptCurrentSuggestion();
resetKeyRepeat(); resetKeyRepeat();
mInputMode.reset();
return !isSuggestionViewHidden(); return !isSuggestionViewHidden();
} }
@ -153,72 +149,46 @@ public class TraditionalT9 extends KeyPadHandler {
return nextSuggestion(); 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 (!mInputMode.onNumber(mLanguage, key, hold, repeat)) {
if (!hold && nextSuggestionInModeAbc()) { return false;
}
if (mInputMode.shouldSelectNextSuggestion() && !isSuggestionViewHidden()) {
nextSuggestion();
return true; return true;
} }
acceptCurrentSuggestion(); if (mInputMode.getWord() != null) {
setText(mInputMode.getWord());
setSuggestions( } else {
mInputMode == MODE_ABC && !hold ? mLanguage.getKeyCharacters(0) : null, getSuggestions();
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);
} }
return true; 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() { protected boolean onPound() {
currentInputConnection.commitText("#", 1); setText("#");
return true; return true;
} }
protected boolean onStar() { protected boolean onStar() {
currentInputConnection.commitText("*", 1); setText("*");
return true; return true;
} }
@ -231,7 +201,7 @@ public class TraditionalT9 extends KeyPadHandler {
if (hold) { if (hold) {
nextLang(); nextLang();
} else { } else {
nextKeyMode(); nextInputMode();
} }
return true; return true;
@ -254,7 +224,7 @@ public class TraditionalT9 extends KeyPadHandler {
protected boolean shouldTrackNumPress() { protected boolean shouldTrackNumPress() {
return mInputMode != TraditionalT9.MODE_123; return mInputMode.shouldTrackNumPress();
} }
@ -274,9 +244,7 @@ public class TraditionalT9 extends KeyPadHandler {
} }
mSuggestionView.scrollToSuggestion(-1); mSuggestionView.scrollToSuggestion(-1);
setComposingTextFromCurrentSuggestion();
String word = mSuggestionView.getCurrentSuggestion();
currentInputConnection.setComposingText(word, word.length());
return true; return true;
} }
@ -288,143 +256,46 @@ public class TraditionalT9 extends KeyPadHandler {
} }
mSuggestionView.scrollToSuggestion(1); 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(); setComposingTextFromCurrentSuggestion();
return true; return true;
} }
private void handleSuggestions(ArrayList<String> suggestions, int maxWordLength) {
setSuggestions(suggestions);
private boolean wordInPredictiveMode(int key) { // Put the first suggestion in the text field,
if ( // but cut it off to the length of the sequence (how many keys were pressed),
mInputMode != MODE_PREDICTIVE || // for a more intuitive experience.
// 0 is not a word, but space, so we handle it in on0(). String word = mSuggestionView.getCurrentSuggestion();
key == 0 || word = word.substring(0, Math.min(maxWordLength, word.length()));
// double 1 is not a word, but an emoticon and it is handled elsewhere setComposingText(word);
(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;
} }
private final Handler handleSuggestions = new Handler(Looper.getMainLooper()) { private final Handler handleSuggestionsAsync = new Handler(Looper.getMainLooper()) {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
ArrayList<String> suggestions = msg.getData().getStringArrayList("suggestions"); handleSuggestions(
suggestions = guessSuggestionsWhenNone(suggestions, mSuggestionView.getCurrentSuggestion()); msg.getData().getStringArrayList("suggestions"),
msg.getData().getInt("maxWordLength", 1000)
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());
} }
}; };
private void applyPredictionSequence() {
if (predictionSequence.length() == 0) {
return;
}
DictionaryDb.getSuggestions(
this,
handleSuggestions,
mLanguage,
predictionSequence,
prefs.getSuggestionsMin(),
prefs.getSuggestionsMax()
);
}
private ArrayList<String> guessSuggestionsWhenNone(ArrayList<String> 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() { private void acceptCurrentSuggestion() {
// bring this word up in the suggestions list next time mInputMode.onAcceptSuggestion(mLanguage, mSuggestionView.getCurrentSuggestion());
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());
}
}
commitCurrentSuggestion(); commitCurrentSuggestion();
} }
private void commitCurrentSuggestion() { private void commitCurrentSuggestion() {
predictionSequence = "";
// commit the current suggestion to the input field // commit the current suggestion to the input field
if (!isSuggestionViewHidden()) { if (!isSuggestionViewHidden()) {
if (mSuggestionView.getCurrentSuggestion().equals(" ")) { if (mSuggestionView.getCurrentSuggestion().equals(" ")) {
// finishComposingText() seems to ignore a single space, // finishComposingText() seems to ignore a single space,
// so we have to force commit it. // so we have to force commit it.
currentInputConnection.commitText(" ", 1); setText(" ");
} else { } else {
currentInputConnection.finishComposingText(); currentInputConnection.finishComposingText();
} }
@ -435,34 +306,47 @@ public class TraditionalT9 extends KeyPadHandler {
private void clearSuggestions() { private void clearSuggestions() {
setSuggestions(null);
if (currentInputConnection != null) { if (currentInputConnection != null) {
currentInputConnection.setComposingText("", 1); setComposingTextFromCurrentSuggestion();
currentInputConnection.finishComposingText(); currentInputConnection.finishComposingText();
} }
setSuggestions(null);
} }
protected void setSuggestions(List<String> suggestions) { private void getSuggestions() {
setSuggestions(suggestions, 0); if (!mInputMode.getSuggestionsAsync(handleSuggestionsAsync, mLanguage, mSuggestionView.getCurrentSuggestion())) {
handleSuggestions(mInputMode.getSuggestions(), 1);
}
} }
protected void setSuggestions(List<String> suggestions, int initialSel) { private void setSuggestions(List<String> suggestions) {
if (mSuggestionView == null) { if (mSuggestionView == null) {
return; return;
} }
boolean show = suggestions != null && suggestions.size() > 0; boolean show = suggestions != null && suggestions.size() > 0;
mSuggestionView.setSuggestions(suggestions, initialSel); mSuggestionView.setSuggestions(suggestions, 0);
mSuggestionView.changeCase(mTextCase, mLanguage.getLocale());
setCandidatesViewShown(show); 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) { if (mEditing == EDITING_STRICT_NUMERIC || mEditing == EDITING_DIALER) {
clearSuggestions(); clearSuggestions();
mInputMode = MODE_123; mInputMode = InputMode.getInstance(InputMode.MODE_123);
} }
// when typing a word or viewing scrolling the suggestions, only change the case // when typing a word or viewing scrolling the suggestions, only change the case
else if (!isSuggestionViewHidden()) { else if (!isSuggestionViewHidden()) {
@ -475,13 +359,13 @@ public class TraditionalT9 extends KeyPadHandler {
setComposingTextFromCurrentSuggestion(); setComposingTextFromCurrentSuggestion();
} }
// make "abc" and "ABC" separate modes from user perspective // make "abc" and "ABC" separate modes from user perspective
else if (mInputMode == MODE_ABC && mTextCase == CASE_LOWER) { else if (mInputMode.isABC() && mTextCase == InputMode.CASE_LOWER) {
mTextCase = CASE_UPPER; mTextCase = InputMode.CASE_UPPER;
} else { } else {
int modeIndex = (allowedInputModes.indexOf(mInputMode) + 1) % allowedInputModes.size(); int modeIndex = (allowedInputModes.indexOf(mInputMode.getId()) + 1) % allowedInputModes.size();
mInputMode = allowedInputModes.get(modeIndex); 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 // save the settings for the next time
@ -493,7 +377,7 @@ public class TraditionalT9 extends KeyPadHandler {
private void setComposingTextFromCurrentSuggestion() { private void setComposingTextFromCurrentSuggestion() {
if (!isSuggestionViewHidden()) { if (!isSuggestionViewHidden()) {
currentInputConnection.setComposingText(mSuggestionView.getCurrentSuggestion(), 1); setComposingText(mSuggestionView.getCurrentSuggestion());
} }
} }
@ -522,18 +406,18 @@ public class TraditionalT9 extends KeyPadHandler {
private void determineAllowedInputModes(EditorInfo inputField) { private void determineAllowedInputModes(EditorInfo inputField) {
allowedInputModes = InputFieldHelper.determineInputModes(inputField); allowedInputModes = InputFieldHelper.determineInputModes(inputField);
int lastInputMode = prefs.getInputMode(); int lastInputModeId = prefs.getInputMode();
if (allowedInputModes.contains(lastInputMode)) { if (allowedInputModes.contains(lastInputModeId)) {
mInputMode = lastInputMode; mInputMode = InputMode.getInstance(lastInputModeId);
} else if (allowedInputModes.contains(TraditionalT9.MODE_ABC)) { } else if (allowedInputModes.contains(InputMode.MODE_ABC)) {
mInputMode = TraditionalT9.MODE_ABC; mInputMode = InputMode.getInstance(InputMode.MODE_ABC);
} else { } else {
mInputMode = allowedInputModes.get(0); mInputMode = InputMode.getInstance(allowedInputModes.get(0));
} }
if (InputFieldHelper.isDialerField(inputField)) { if (InputFieldHelper.isDialerField(inputField)) {
mEditing = EDITING_DIALER; mEditing = EDITING_DIALER;
} else if (mInputMode == TraditionalT9.MODE_123 && allowedInputModes.size() == 1) { } else if (mInputMode.is123() && allowedInputModes.size() == 1) {
mEditing = EDITING_STRICT_NUMERIC; mEditing = EDITING_STRICT_NUMERIC;
} else { } else {
mEditing = InputFieldHelper.isFilterTextField(inputField) ? EDITING_NOSHOW : EDITING; mEditing = InputFieldHelper.isFilterTextField(inputField) ? EDITING_NOSHOW : EDITING;
@ -542,20 +426,8 @@ public class TraditionalT9 extends KeyPadHandler {
private void determineAllowedTextCases() { private void determineAllowedTextCases() {
// @todo: determine case from input allowedTextCases = mInputMode.getAllowedTextCases();
// @todo: determine the text case of the input and validate using the allowed ones
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);
}
} }
@ -581,8 +453,8 @@ public class TraditionalT9 extends KeyPadHandler {
try { try {
Logger.d("restoreAddedWordIfAny", "Restoring word: '" + word + "'..."); Logger.d("restoreAddedWordIfAny", "Restoring word: '" + word + "'...");
predictionSequence = mLanguage.getDigitSequenceForWord(word); setText(word);
currentInputConnection.commitText(word, word.length()); mInputMode.reset();
} catch (Exception e) { } catch (Exception e) {
Logger.w("tt9/restoreLastWord", "Could not restore the last added word. " + e.getMessage()); 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. * Generates the actual UI of TT9.
*/ */
protected View createSoftKeyView() { protected View createSoftKeyView() {
if (softKeyView == null) { return softKeyHandler.createView(getLayoutInflater());
softKeyView = getLayoutInflater().inflate(R.layout.mainview, null);
}
softKeyHandler.changeView(softKeyView);
return softKeyView;
}
private void displaySoftKeyMenu() {
createSoftKeyView();
softKeyView.setVisibility(View.VISIBLE);
} }
} }

View file

@ -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<Integer> allowedTextCases = new ArrayList<>();
// data
protected ArrayList<String> 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<String> getSuggestions() { return suggestions; }
public boolean getSuggestionsAsync(Handler handler, Language language, String lastWord) { return false; }
protected void sendSuggestions(Handler handler, ArrayList<String> 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<Integer> 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; }
}

View file

@ -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; }
}

View file

@ -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;
}
}

View file

@ -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<String> 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<String> generateSuggestionWhenNone(ArrayList<String> 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());
}
}
}

View file

@ -39,15 +39,11 @@ public class Language {
} }
public ArrayList<String> getKeyCharacters(int key) { public ArrayList<String> getKeyCharacters(int key) {
return getKeyCharacters(key, true);
}
public ArrayList<String> getKeyCharacters(int key, boolean lowerCase) {
if (key < 0 || key >= characterMap.size()) { if (key < 0 || key >= characterMap.size()) {
return new ArrayList<>(); return new ArrayList<>();
} }
ArrayList<String> chars = lowerCase ? new ArrayList<>(characterMap.get(key)) : getUpperCaseChars(key); ArrayList<String> chars = new ArrayList<>(characterMap.get(key));
if (chars.size() > 0) { if (chars.size() > 0) {
chars.add(String.valueOf(key)); chars.add(String.valueOf(key));
} }
@ -55,15 +51,6 @@ public class Language {
return chars; return chars;
} }
private ArrayList<String> getUpperCaseChars(int mapId) {
ArrayList<String> uppercaseChars = new ArrayList<>();
for (String ch : characterMap.get(mapId)) {
uppercaseChars.add(ch.toUpperCase(locale));
}
return uppercaseChars;
}
public String getDigitSequenceForWord(String word) throws Exception { public String getDigitSequenceForWord(String word) throws Exception {
StringBuilder sequence = new StringBuilder(); StringBuilder sequence = new StringBuilder();
String lowerCaseWord = word.toLowerCase(locale); String lowerCaseWord = word.toLowerCase(locale);

View file

@ -11,6 +11,7 @@ import java.util.Arrays;
import io.github.sspanak.tt9.Logger; import io.github.sspanak.tt9.Logger;
import io.github.sspanak.tt9.ime.TraditionalT9; import io.github.sspanak.tt9.ime.TraditionalT9;
import io.github.sspanak.tt9.ime.modes.InputMode;
import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.languages.LanguageCollection;
@ -23,13 +24,13 @@ public class T9Preferences {
private final SharedPreferences.Editor prefsEditor; private final SharedPreferences.Editor prefsEditor;
public T9Preferences (Context context) { public T9Preferences (Context context) {
prefs = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefsEditor = prefs.edit(); prefsEditor = prefs.edit();
} }
public static T9Preferences getInstance(Context context) { public static T9Preferences getInstance() {
if (self == null) { if (self == null) {
self = new T9Preferences(context); self = new T9Preferences(TraditionalT9.getMainContext());
} }
return self; return self;
@ -101,13 +102,13 @@ public class T9Preferences {
} }
public int getTextCase() { 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) { public void saveTextCase(int textCase) {
boolean isTextCaseValid = isIntInList( boolean isTextCaseValid = isIntInList(
textCase, 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", "tt9/saveTextCase",
"Not saving invalid text case: " + textCase "Not saving invalid text case: " + textCase
); );
@ -131,21 +132,17 @@ public class T9Preferences {
} }
public int getInputMode() { 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) { public void saveInputMode(InputMode mode) {
boolean isModeValid = isIntInList( if (mode == null) {
mode, Logger.w("tt9/saveInputMode", "Not saving NULL input mode");
new ArrayList<>(Arrays.asList(TraditionalT9.MODE_123, TraditionalT9.MODE_ABC, TraditionalT9.MODE_PREDICTIVE)), return;
"tt9/saveInputMode",
"Not saving invalid text case: " + mode
);
if (isModeValid) {
prefsEditor.putInt("pref_input_mode", mode);
prefsEditor.apply();
} }
prefsEditor.putInt("pref_input_mode", mode.getId());
prefsEditor.apply();
} }

View file

@ -17,7 +17,7 @@ public class SettingMultiList extends SettingList {
public SettingMultiList (Context context, AttributeSet attrs, Object[] isettings) { public SettingMultiList (Context context, AttributeSet attrs, Object[] isettings) {
super(context, attrs, isettings); super(context, attrs, isettings);
selectedEntries = new boolean[entries.length]; 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 selectedEntries[langId - 1] = true; // languages are 1-based, unlike arrays
} }
summary = buildItems(); summary = buildItems();
@ -39,7 +39,7 @@ public class SettingMultiList extends SettingList {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
if (id.equals("pref_lang_support")) { if (id.equals("pref_lang_support")) {
T9Preferences.getInstance(context).saveEnabledLanguages(buildSelection()); T9Preferences.getInstance().saveEnabledLanguages(buildSelection());
} }
summary = buildItems(); summary = buildItems();
dialog.dismiss(); dialog.dismiss();

View file

@ -32,7 +32,7 @@ public class AddWordAct extends Activity {
View v = getLayoutInflater().inflate(R.layout.addwordview, null); 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.setText(word);
et.setSelection(word.length()); et.setSelection(word.length());
setContentView(v); setContentView(v);
@ -46,7 +46,7 @@ public class AddWordAct extends Activity {
switch (msg.what) { switch (msg.what) {
case 0: case 0:
Logger.d("onAddedWord", "Added word: '" + word + "'..."); Logger.d("onAddedWord", "Added word: '" + word + "'...");
T9Preferences.getInstance(main.getContext()).saveLastWord(word); T9Preferences.getInstance().saveLastWord(word);
break; break;
case 1: case 1:
@ -71,7 +71,7 @@ public class AddWordAct extends Activity {
word = ((EditText) main.findViewById(R.id.add_word_text)).getText().toString(); word = ((EditText) main.findViewById(R.id.add_word_text)).getText().toString();
Logger.d("addWord", "Attempting to add word: '" + word + "'..."); 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) { } catch (InsertBlankWordException e) {
Logger.e("AddWordAct.addWord", e.getMessage()); Logger.e("AddWordAct.addWord", e.getMessage());
UI.toastLong(this, R.string.add_word_blank); UI.toastLong(this, R.string.add_word_blank);

View file

@ -13,7 +13,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import io.github.sspanak.tt9.R; 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 { public class CandidateView extends View {
@ -200,9 +200,9 @@ public class CandidateView extends View {
ArrayList<String> newSuggestions = new ArrayList<>(); ArrayList<String> newSuggestions = new ArrayList<>();
for (String s : mSuggestions) { for (String s : mSuggestions) {
if (textCase == TraditionalT9.CASE_LOWER) { if (textCase == InputMode.CASE_LOWER) {
newSuggestions.add(s.toLowerCase(locale)); 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); String cs = s.substring(0, 1).toUpperCase(locale) + s.substring(1).toLowerCase(locale);
newSuggestions.add(cs); newSuggestions.add(cs);
} else { } else {

View file

@ -22,17 +22,6 @@ import android.widget.ListView;
import com.stackoverflow.answer.UnicodeBOMInputStream; 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.BufferedReader;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
@ -47,6 +36,17 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Properties; 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 { public class TraditionalT9Settings extends ListActivity implements DialogInterface.OnCancelListener {
AsyncTask<String, Integer, Reply> task = null; AsyncTask<String, Integer, Reply> task = null;
@ -205,7 +205,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
// add characters first, then dictionary: // add characters first, then dictionary:
Logger.d("doInBackground", "Adding characters..."); Logger.d("doInBackground", "Adding characters...");
processChars(mContext, mSupportedLanguages); processChars(mSupportedLanguages);
Logger.d("doInBackground", "Characters added."); Logger.d("doInBackground", "Characters added.");
Logger.d("doInBackground", "Adding dict(s)..."); Logger.d("doInBackground", "Adding dict(s)...");
@ -217,7 +217,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
if (internal) { if (internal) {
try { try {
dictstream = getAssets().open(dicts[x]); 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) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
reply.status = false; reply.status = false;
@ -227,7 +227,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
try { try {
dictstream = new FileInputStream(new File( dictstream = new FileInputStream(new File(
new File(Environment.getExternalStorageDirectory(), sddir), dicts[x])); 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) { } catch (FileNotFoundException e) {
reply.status = false; reply.status = false;
reply.forceMsg("File not found: " + e.getMessage()); reply.forceMsg("File not found: " + e.getMessage());
@ -264,13 +264,13 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
* processChars * processChars
* Inserts single characters. * Inserts single characters.
*/ */
private void processChars(Context context, List<Language> allLanguages) { private void processChars(List<Language> allLanguages) {
ArrayList<Word> list = new ArrayList<>(); ArrayList<Word> list = new ArrayList<>();
try { try {
for (Language lang : allLanguages) { for (Language lang : allLanguages) {
for (int key = 0; key <= 9; key++) { 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') { 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 // 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 // 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) { } catch (Exception e) {
Logger.e("processChars", e.getMessage()); Logger.e("processChars", e.getMessage());
} }
@ -306,7 +306,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
return null; 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 { throws LoadException, IOException {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
@ -327,7 +327,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
try { try {
DictionaryDb.beginTransaction(context); DictionaryDb.beginTransaction();
while (fileWord != null) { while (fileWord != null) {
if (isCancelled()) { if (isCancelled()) {
@ -365,7 +365,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
dbWords.add(word); dbWords.add(word);
if (linecount % insertChunkSize == 0) { if (linecount % insertChunkSize == 0) {
DictionaryDb.insertWordsSync(context, dbWords); DictionaryDb.insertWordsSync(dbWords);
dbWords.clear(); dbWords.clear();
} }
@ -376,13 +376,13 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
fileWord = getLine(br, rpl, fname); fileWord = getLine(br, rpl, fname);
} }
DictionaryDb.insertWordsSync(context, dbWords); DictionaryDb.insertWordsSync(dbWords);
DictionaryDb.endTransaction(context, true); DictionaryDb.endTransaction(true);
dbWords.clear(); dbWords.clear();
publishProgress((int) ((float) pos / size * 10000)); publishProgress((int) ((float) pos / size * 10000));
} catch (Exception e) { } catch (Exception e) {
DictionaryDb.endTransaction(context, false); DictionaryDb.endTransaction(false);
Logger.e("processFile", e.getMessage()); Logger.e("processFile", e.getMessage());
} finally { } finally {
br.close(); br.close();
@ -416,7 +416,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
// http://stackoverflow.com/questions/7645880/listview-with-onitemclicklistener-android // http://stackoverflow.com/questions/7645880/listview-with-onitemclicklistener-android
// get settings // get settings
T9Preferences prefs = new T9Preferences(this); T9Preferences prefs = new T9Preferences(getApplicationContext());
Object[] settings = { Object[] settings = {
prefs.getInputMode() prefs.getInputMode()
}; };
@ -462,7 +462,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
UI.toast(mContext, R.string.dictionary_truncated); 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( task = new LoadDictTask(
msgid, msgid,
internal, internal,
LanguageCollection.getAll(T9Preferences.getInstance(mContext).getEnabledLanguages()) LanguageCollection.getAll(T9Preferences.getInstance().getEnabledLanguages())
); );
task.execute(); task.execute();
} }

View file

@ -7,6 +7,7 @@ import android.widget.Toast;
import io.github.sspanak.tt9.Logger; import io.github.sspanak.tt9.Logger;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.ime.TraditionalT9; import io.github.sspanak.tt9.ime.TraditionalT9;
import io.github.sspanak.tt9.ime.modes.InputMode;
import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.Language;
public class UI { public class UI {
@ -34,21 +35,16 @@ public class UI {
* Set the status icon that is appropriate in current mode (based on * Set the status icon that is appropriate in current mode (based on
* openwmm-legacy) * openwmm-legacy)
*/ */
public static void updateStatusIcon(TraditionalT9 tt9, Language inputLanguage, int inputMode, int textCase) { public static void updateStatusIcon(TraditionalT9 tt9, Language inputLanguage, InputMode inputMode, int textCase) {
switch (inputMode) { if (inputMode.isABC()) {
case TraditionalT9.MODE_ABC: tt9.showStatusIcon(inputLanguage.getAbcIcon(textCase == InputMode.CASE_LOWER));
tt9.showStatusIcon(inputLanguage.getAbcIcon(textCase == TraditionalT9.CASE_LOWER)); } else if (inputMode.isPredictive()) {
break; tt9.showStatusIcon(inputLanguage.getIcon());
case TraditionalT9.MODE_PREDICTIVE: } else if (inputMode.is123()) {
tt9.showStatusIcon(inputLanguage.getIcon()); tt9.showStatusIcon(R.drawable.ime_number);
break; } else {
case TraditionalT9.MODE_123: Logger.w("tt9.UI", "Unknown inputMode mode: " + inputMode + ". Hiding status icon.");
tt9.showStatusIcon(R.drawable.ime_number); tt9.hideStatusIcon();
break;
default:
Logger.w("tt9.UI", "Unknown inputMode mode: " + inputMode + ". Hiding status icon.");
tt9.hideStatusIcon();
break;
} }
} }