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

@ -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<Word> words) {
getInstance(context).wordsDao().insertMany(words);
public static void insertWordsSync(List<Word> 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<Word> exactMatches = getInstance(context).wordsDao().getMany(language.getId(), sequence, maxWords);
Logger.d("getWords", "Exact matches: " + exactMatches.size());
List<Word> exactMatches = getInstance().wordsDao().getMany(language.getId(), sequence, maxWords);
Logger.d("db.getSuggestions", "Exact matches: " + exactMatches.size());
ArrayList<String> 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<Word> extraWords = getInstance(context).wordsDao().getFuzzy(language.getId(), sequence, extraWordsNeeded);
Logger.d("getWords", "Fuzzy matches: " + extraWords.size());
List<Word> 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);
}

View file

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

View file

@ -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<Integer> allowedModes) {
if (allowedModes.size() > 0 && allowedModes.contains(inputMode)) {
public static InputMode validateMode(T9Preferences prefs, InputMode inputMode, ArrayList<Integer> 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) {

View file

@ -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();

View file

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

View file

@ -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<Integer> 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<Integer> allowedTextCases = new ArrayList<>();
private int mTextCase = CASE_LOWER;
private int mTextCase = InputMode.CASE_LOWER;
// language
protected ArrayList<Integer> 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<String> 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<String> 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<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() {
// 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<String> suggestions) {
setSuggestions(suggestions, 0);
private void getSuggestions() {
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) {
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());
}
}

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) {
return getKeyCharacters(key, true);
}
public ArrayList<String> getKeyCharacters(int key, boolean lowerCase) {
if (key < 0 || key >= characterMap.size()) {
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) {
chars.add(String.valueOf(key));
}
@ -55,15 +51,6 @@ public class Language {
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 {
StringBuilder sequence = new StringBuilder();
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.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();
}

View file

@ -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();

View file

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

View file

@ -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<String> 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 {

View file

@ -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<String, Integer, Reply> 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<Language> allLanguages) {
private void processChars(List<Language> allLanguages) {
ArrayList<Word> 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();
}

View file

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