Restore Add word (#73)
* adding words to the dictionary is possible again * removed some unused code * updated Readme files * special key handlers now validate the input mode themselves * improved language validation and error handling here and there
This commit is contained in:
parent
78b6681812
commit
8d85215444
18 changed files with 195 additions and 159 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package io.github.sspanak.tt9.db;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteConstraintException;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
|
@ -10,12 +11,12 @@ import androidx.room.Room;
|
|||
import androidx.room.RoomDatabase;
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||
|
||||
import java.io.NotActiveException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.languages.InvalidLanguageException;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
|
||||
public class DictionaryDb {
|
||||
private static T9RoomDb dbInstance;
|
||||
|
|
@ -91,28 +92,50 @@ public class DictionaryDb {
|
|||
}
|
||||
|
||||
|
||||
public static void insertWord(Context context, String word, int languageId) throws InvalidLanguageException, InsertBlankWordException, NotActiveException {
|
||||
if (languageId <= 0) {
|
||||
throw new InvalidLanguageException("Cannot insert a word for an invalid language with ID: '" + languageId + "'");
|
||||
public static void insertWord(Context context, Handler handler, Language language, String word) throws Exception {
|
||||
if (language == null) {
|
||||
throw new InvalidLanguageException();
|
||||
}
|
||||
|
||||
if (word == null || word.length() == 0) {
|
||||
throw new InsertBlankWordException();
|
||||
}
|
||||
|
||||
// @todo: insert async with max priority for this sequence.
|
||||
throw new NotActiveException("Adding new words is disabled in this version. Please, check for updates.");
|
||||
Word dbWord = new Word();
|
||||
dbWord.langId = language.getId();
|
||||
dbWord.sequence = language.getDigitSequenceForWord(word);
|
||||
dbWord.word = word.toLowerCase(language.getLocale());
|
||||
dbWord.frequency = 1;
|
||||
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
getInstance(context).wordsDao().insert(dbWord);
|
||||
getInstance(context).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;
|
||||
Logger.e("tt9/insertWord", msg);
|
||||
handler.sendEmptyMessage(1);
|
||||
} catch (Exception e) {
|
||||
String msg = "Failed inserting word: '" + dbWord.word + "' / sequence: '" + dbWord.sequence + "', for language: " + dbWord.langId;
|
||||
Logger.e("tt9/insertWord", msg);
|
||||
handler.sendEmptyMessage(2);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
|
||||
public static void insertWordsSync(Context context, List<Word> words) {
|
||||
getInstance(context).wordsDao().insertWords(words);
|
||||
getInstance(context).wordsDao().insertMany(words);
|
||||
}
|
||||
|
||||
|
||||
public static void incrementWordFrequency(Context context, int langId, String word, String sequence) throws Exception {
|
||||
if (langId <= 0) {
|
||||
throw new InvalidLanguageException("Cannot increment word frequency for an invalid language: '" + langId + "'");
|
||||
public static void incrementWordFrequency(Context context, Language language, String word, String sequence) throws Exception {
|
||||
if (language == null) {
|
||||
throw new InvalidLanguageException();
|
||||
}
|
||||
|
||||
// If both are empty, it is the same as changing the frequency of: "", which is simply a no-op.
|
||||
|
|
@ -130,7 +153,7 @@ public class DictionaryDb {
|
|||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
getInstance(context).wordsDao().incrementFrequency(langId, word, sequence);
|
||||
getInstance(context).wordsDao().incrementFrequency(language.getId(), word, sequence);
|
||||
} catch (Exception e) {
|
||||
Logger.e(
|
||||
DictionaryDb.class.getName(),
|
||||
|
|
@ -142,7 +165,7 @@ public class DictionaryDb {
|
|||
}
|
||||
|
||||
|
||||
public static void getSuggestions(Context context, Handler handler, int langId, String sequence, int minimumWords, int maximumWords) {
|
||||
public static void getSuggestions(Context context, Handler handler, Language language, String sequence, int minimumWords, int maximumWords) {
|
||||
final int minWords = Math.max(minimumWords, 0);
|
||||
final int maxWords = Math.max(maximumWords, minimumWords);
|
||||
|
||||
|
|
@ -150,12 +173,19 @@ 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.");
|
||||
sendSuggestions(handler, new ArrayList<>());
|
||||
return;
|
||||
}
|
||||
|
||||
if (language == null) {
|
||||
Logger.w("tt9/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(langId, sequence, maxWords);
|
||||
List<Word> exactMatches = getInstance(context).wordsDao().getMany(language.getId(), sequence, maxWords);
|
||||
Logger.d("getWords", "Exact matches: " + exactMatches.size());
|
||||
|
||||
ArrayList<String> suggestions = new ArrayList<>();
|
||||
|
|
@ -168,7 +198,7 @@ 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(langId, sequence, extraWordsNeeded);
|
||||
List<Word> extraWords = getInstance(context).wordsDao().getFuzzy(language.getId(), sequence, extraWordsNeeded);
|
||||
Logger.d("getWords", "Fuzzy matches: " + extraWords.size());
|
||||
|
||||
for (Word word : extraWords) {
|
||||
|
|
|
|||
|
|
@ -27,8 +27,11 @@ interface WordsDao {
|
|||
)
|
||||
List<Word> getFuzzy(int langId, String sequence, int limit);
|
||||
|
||||
@Insert
|
||||
void insert(Word word);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
void insertWords(List<Word> words);
|
||||
void insertMany(List<Word> words);
|
||||
|
||||
@Query(
|
||||
"UPDATE words " +
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
package io.github.sspanak.tt9.ime;
|
||||
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
class InputFieldHelper {
|
||||
|
|
@ -157,23 +157,12 @@ class InputFieldHelper {
|
|||
|
||||
|
||||
public static String getSurroundingWord(InputConnection currentInputConnection) {
|
||||
CharSequence before = currentInputConnection.getTextBeforeCursor(50, 0);
|
||||
CharSequence after = currentInputConnection.getTextAfterCursor(50, 0);
|
||||
int bounds = -1;
|
||||
if (!TextUtils.isEmpty(before)) {
|
||||
bounds = before.length() -1;
|
||||
while (bounds > 0 && !Character.isWhitespace(before.charAt(bounds))) {
|
||||
bounds--;
|
||||
}
|
||||
before = before.subSequence(bounds, before.length());
|
||||
}
|
||||
if (!TextUtils.isEmpty(after)) {
|
||||
bounds = 0;
|
||||
while (bounds < after.length() && !Character.isWhitespace(after.charAt(bounds))) {
|
||||
bounds++;
|
||||
}
|
||||
after = after.subSequence(0, bounds);
|
||||
}
|
||||
return before.toString().trim() + after.toString().trim();
|
||||
String before = (String) currentInputConnection.getTextBeforeCursor(50, 0);
|
||||
String after = (String) currentInputConnection.getTextAfterCursor(50, 0);
|
||||
|
||||
Matcher beforeMatch = Pattern.compile("(\\w+)$").matcher(before);
|
||||
Matcher afterMatch = Pattern.compile("^(\\w+)").matcher(after);
|
||||
|
||||
return (beforeMatch.find() ? beforeMatch.group(1) : "") + (afterMatch.find() ? afterMatch.group(1) : "");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import android.view.KeyEvent;
|
|||
import android.view.View;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
|
||||
import java.io.NotActiveException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
|
@ -16,8 +15,6 @@ 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.db.InsertBlankWordException;
|
||||
import io.github.sspanak.tt9.languages.InvalidLanguageException;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||
import io.github.sspanak.tt9.languages.Punctuation;
|
||||
|
|
@ -75,6 +72,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
loadPreferences();
|
||||
prefs.clearLastWord();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -98,7 +96,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
determineAllowedInputModes(inputField);
|
||||
determineAllowedTextCases();
|
||||
|
||||
// @todo: handle word adding
|
||||
restoreAddedWordIfAny();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -106,8 +104,6 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
predictionSequence = "";
|
||||
clearSuggestions();
|
||||
|
||||
// @todo: clear previous word
|
||||
|
||||
hideStatusIcon();
|
||||
hideWindow();
|
||||
|
||||
|
|
@ -228,6 +224,10 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
|
||||
protected boolean onKeyInputMode(boolean hold) {
|
||||
if (mEditing == EDITING_DIALER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hold) {
|
||||
nextLang();
|
||||
} else {
|
||||
|
|
@ -239,6 +239,10 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
|
||||
protected boolean onKeyOtherAction(boolean hold) {
|
||||
if (mEditing == EDITING_NOSHOW || mEditing == EDITING_DIALER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hold) {
|
||||
UI.showPreferencesScreen(this);
|
||||
} else {
|
||||
|
|
@ -358,7 +362,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
DictionaryDb.getSuggestions(
|
||||
this,
|
||||
handleSuggestions,
|
||||
mLanguage.getId(),
|
||||
mLanguage,
|
||||
predictionSequence,
|
||||
prefs.getSuggestionsMin(),
|
||||
prefs.getSuggestionsMax()
|
||||
|
|
@ -402,7 +406,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
try {
|
||||
String sequence = mLanguage.getDigitSequenceForWord(currentWord);
|
||||
DictionaryDb.incrementWordFrequency(this, mLanguage.getId(), currentWord, sequence);
|
||||
DictionaryDb.incrementWordFrequency(this, mLanguage, currentWord, sequence);
|
||||
} catch (Exception e) {
|
||||
Logger.e(getClass().getName(), "Failed incrementing priority of word: '" + currentWord + "'. " + e.getMessage());
|
||||
}
|
||||
|
|
@ -556,50 +560,35 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
|
||||
private void showAddWord() {
|
||||
if (mInputMode != MODE_PREDICTIVE) {
|
||||
UI.toastLong(this, R.string.add_word_only_in_predictive_mode);
|
||||
return;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// @todo: remove this try..catch in #55 and display the dialog
|
||||
try {
|
||||
DictionaryDb.insertWord(this, "a", mLanguage.getId());
|
||||
} catch (InsertBlankWordException e) {
|
||||
Logger.e("tt9/showAddWord", e.getMessage());
|
||||
UI.toastLong(this, R.string.add_word_blank);
|
||||
return;
|
||||
} catch (InvalidLanguageException e) {
|
||||
Logger.e("tt9/showAddWord", e.getMessage());
|
||||
UI.toastLong(this, R.string.add_word_invalid_language);
|
||||
return;
|
||||
} catch (NotActiveException e) {
|
||||
UI.toastLong(this, e.getMessage());
|
||||
return;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
acceptCurrentSuggestion();
|
||||
clearSuggestions();
|
||||
|
||||
String template = "";
|
||||
|
||||
// @todo: get the current word template from the input connection
|
||||
// template = getSurroundingWord();
|
||||
|
||||
UI.showAddWordDialog(this, mLanguage.getId(), template);
|
||||
UI.showAddWordDialog(this, mLanguage.getId(), InputFieldHelper.getSurroundingWord(currentInputConnection));
|
||||
}
|
||||
|
||||
|
||||
private void restoreLastWordIfAny() {
|
||||
// mAddingWord = false;
|
||||
/**
|
||||
* restoreAddedWordIfAny
|
||||
* If a new word was added to the dictionary, this function will append add it to the current input field.
|
||||
*/
|
||||
private void restoreAddedWordIfAny() {
|
||||
String word = prefs.getLastWord();
|
||||
if (word.equals("")) {
|
||||
prefs.saveLastWord("");
|
||||
prefs.clearLastWord();
|
||||
|
||||
// @todo: push the word to the text field
|
||||
if (word.length() == 0 || word.equals(InputFieldHelper.getSurroundingWord(currentInputConnection))) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Logger.d("restoreAddedWordIfAny", "Restoring word: '" + word + "'...");
|
||||
predictionSequence = mLanguage.getDigitSequenceForWord(word);
|
||||
currentInputConnection.commitText(word, word.length());
|
||||
} catch (Exception e) {
|
||||
Logger.w("tt9/restoreLastWord", "Could not restore the last added word. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* createSoftKeyView
|
||||
* Generates the actual UI of TT9.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package io.github.sspanak.tt9.languages;
|
||||
|
||||
public class InvalidLanguageException extends Exception {
|
||||
public InvalidLanguageException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
public InvalidLanguageException() { super("Invalid Language"); }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ public class T9Preferences {
|
|||
|
||||
public void saveEnabledLanguages(ArrayList<Integer> languageIds) {
|
||||
int languageMask = 0;
|
||||
for (Integer langId : languageIds) {
|
||||
for (int langId : languageIds) {
|
||||
if (!validateSavedLanguage(langId, "tt9/saveEnabledLanguages")){
|
||||
continue;
|
||||
}
|
||||
|
|
@ -172,4 +172,9 @@ public class T9Preferences {
|
|||
prefsEditor.putString("last_word", lastWord);
|
||||
prefsEditor.apply();
|
||||
}
|
||||
|
||||
public void clearLastWord() {
|
||||
this.saveLastWord("");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,17 @@ package io.github.sspanak.tt9.ui;
|
|||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
|
||||
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.InsertBlankWordException;
|
||||
import io.github.sspanak.tt9.languages.InvalidLanguageException;
|
||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||
import io.github.sspanak.tt9.preferences.T9Preferences;
|
||||
|
||||
|
|
@ -17,55 +21,71 @@ public class AddWordAct extends Activity {
|
|||
|
||||
View main;
|
||||
int lang;
|
||||
String origword;
|
||||
String word;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedData) {
|
||||
super.onCreate(savedData);
|
||||
View v = getLayoutInflater().inflate(R.layout.addwordview, null);
|
||||
EditText et = (EditText) v.findViewById(R.id.add_word_text);
|
||||
Intent i = getIntent();
|
||||
origword = i.getStringExtra("io.github.sspanak.tt9.word");
|
||||
|
||||
word = i.getStringExtra("io.github.sspanak.tt9.word");
|
||||
lang = i.getIntExtra("io.github.sspanak.tt9.lang", -1);
|
||||
if (lang == -1) {
|
||||
Logger.e("AddWordAct.onCreate", "lang is invalid. How?");
|
||||
}
|
||||
// Logger.d("AddWord", "data.get: " + word);
|
||||
et.setText(origword);
|
||||
et.setSelection(origword.length());
|
||||
|
||||
View v = getLayoutInflater().inflate(R.layout.addwordview, null);
|
||||
|
||||
EditText et = (EditText) v.findViewById(R.id.add_word_text);
|
||||
et.setText(word);
|
||||
et.setSelection(word.length());
|
||||
setContentView(v);
|
||||
main = v;
|
||||
}
|
||||
|
||||
public void addWordButton(View v) {
|
||||
EditText et = (EditText) main.findViewById(R.id.add_word_text);
|
||||
// Logger.d("AddWordAct", "adding word: " + et.getText());
|
||||
doAddWord(et.getText().toString());
|
||||
this.finish();
|
||||
}
|
||||
|
||||
public void doAddWord(String text) {
|
||||
try {
|
||||
DictionaryDb.insertWord(this, text, LanguageCollection.getLanguage(lang).getId());
|
||||
} catch (Exception e) {
|
||||
UI.toast(this, e.getMessage());
|
||||
return;
|
||||
private final Handler onAddedWord = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case 0:
|
||||
Logger.d("onAddedWord", "Added word: '" + word + "'...");
|
||||
T9Preferences.getInstance(main.getContext()).saveLastWord(word);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
UI.toastLong(
|
||||
main.getContext(),
|
||||
getResources().getString(R.string.add_word_exist, word)
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
UI.toastLong(main.getContext(), R.string.error_unexpected);
|
||||
break;
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
};
|
||||
|
||||
public void addWord(View v) {
|
||||
try {
|
||||
// re-fetch the word, in case the user has changed it after the initialization
|
||||
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);
|
||||
} catch (InsertBlankWordException e) {
|
||||
Logger.e("AddWordAct.addWord", e.getMessage());
|
||||
UI.toastLong(this, R.string.add_word_blank);
|
||||
} catch (InvalidLanguageException e) {
|
||||
Logger.e("AddWordAct.addWord", "Cannot insert a word for language with ID: '" + lang + "'. " + e.getMessage());
|
||||
UI.toastLong(this, R.string.add_word_invalid_language);
|
||||
} catch (Exception e) {
|
||||
Logger.e("AddWordAct.addWord", e.getMessage());
|
||||
UI.toastLong(this, e.getMessage());
|
||||
}
|
||||
T9Preferences.getInstance(this).saveLastWord(text);
|
||||
}
|
||||
|
||||
|
||||
public void cancelButton(View v) {
|
||||
// Logger.d("AddWordAct", "Cancelled...");
|
||||
public void cancelAddingWord(View v) {
|
||||
this.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
getMenuInflater().inflate(R.menu.add_word, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -418,11 +418,7 @@ public class TraditionalT9Settings extends ListActivity implements DialogInterfa
|
|||
// get settings
|
||||
T9Preferences prefs = new T9Preferences(this);
|
||||
Object[] settings = {
|
||||
prefs.getInputMode(),
|
||||
0, // input languages; not used, remove in #29
|
||||
null, // MODE_NOTIFY; not used, remove in #29
|
||||
false, // KEY_REMAP; not used, remove in #29
|
||||
true, // SPACE_ZERO; not used, remove in #29
|
||||
prefs.getInputMode()
|
||||
};
|
||||
ListAdapter settingitems;
|
||||
try {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue