1
0
Fork 0

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:
Dimo Karaivanov 2022-10-11 10:53:03 +03:00 committed by GitHub
parent 78b6681812
commit 8d85215444
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 195 additions and 159 deletions

View file

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

View file

@ -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 " +

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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