1
0
Fork 0

added currency character typing in all modes

This commit is contained in:
Dimo Karaivanov 2024-02-13 18:50:51 +02:00
parent e03c788ebb
commit a3e72283f8
11 changed files with 160 additions and 101 deletions

View file

@ -196,7 +196,7 @@ public class DictionaryLoader {
WordBatch letters = new WordBatch(language);
for (int key = 2; key <= 9; key++) {
for (String langChar : language.getKeyCharacters(key, false)) {
for (String langChar : language.getKeyCharacters(key)) {
langChar = (isEnglish && langChar.equals("i")) ? langChar.toUpperCase(Locale.ENGLISH) : langChar;
letters.add(langChar, 0, key);
lettersCount++;

View file

@ -688,10 +688,6 @@ public class TraditionalT9 extends KeyPadHandler {
}
private void refreshComposingText() {
textField.setComposingText(getComposingText());
}
private void setComposingTextWithHighlightedStem(@NonNull String word) {
if (appHacks.setComposingTextWithHighlightedStem(word)) {
@ -718,7 +714,7 @@ public class TraditionalT9 extends KeyPadHandler {
for (int retries = 0; retries < 2 && mLanguage.hasUpperCase(); retries++) {
mInputMode.nextTextCase();
setSuggestions(mInputMode.getSuggestions(), suggestionBar.getCurrentIndex());
refreshComposingText();
textField.setComposingText(suggestionBar.getCurrentSuggestion());
if (!currentSuggestionBefore.equals(getComposingText())) {
break;

View file

@ -29,8 +29,10 @@ abstract public class InputMode {
// data
protected int autoAcceptTimeout = -1;
@NonNull protected String digitSequence = "";
protected Language language;
protected final ArrayList<String> suggestions = new ArrayList<>();
protected int specialCharSelectedGroup = 0;
public static InputMode getInstance(SettingsStore settings, Language language, InputType inputType, int mode) {
@ -44,7 +46,7 @@ abstract public class InputMode {
default:
Logger.w("InputMode", "Defaulting to mode: " + Mode123.class.getName() + " for unknown InputMode: " + mode);
case MODE_123:
return new Mode123(inputType);
return new Mode123(inputType, language);
}
}
@ -102,6 +104,7 @@ abstract public class InputMode {
public void reset() {
autoAcceptTimeout = -1;
specialCharSelectedGroup = 0;
suggestions.clear();
}
@ -125,9 +128,40 @@ abstract public class InputMode {
textCase = allowedTextCases.get(0);
}
public void nextTextCase() {
public boolean nextTextCase() {
if (nextSpecialCharacters()) {
return false;
}
int nextIndex = (allowedTextCases.indexOf(textCase) + 1) % allowedTextCases.size();
textCase = allowedTextCases.get(nextIndex);
return true;
}
/**
* This is used in nextTextCase() for switching to the next set of characters. Obviously,
* special chars do not have a text case, but we use this trick to alternate the char groups.
*/
protected boolean nextSpecialCharacters() {
if (language == null || digitSequence.isEmpty()) {
return false;
}
int key = digitSequence.charAt(0) - '0';
ArrayList<String> chars = language.getKeyCharacters(key, ++specialCharSelectedGroup);
if (chars.isEmpty() && specialCharSelectedGroup == 1) {
specialCharSelectedGroup = 0;
return false;
} else if (chars.isEmpty()) {
specialCharSelectedGroup = 0;
chars = language.getKeyCharacters(key, specialCharSelectedGroup);
}
suggestions.clear();
suggestions.addAll(chars);
return true;
}
public void determineNextWordTextCase(String textBeforeCursor) {}

View file

@ -8,6 +8,7 @@ import java.util.Collections;
import io.github.sspanak.tt9.ime.helpers.InputType;
import io.github.sspanak.tt9.languages.Characters;
import io.github.sspanak.tt9.languages.Language;
public class Mode123 extends ModePassthrough {
@Override public int getId() { return MODE_123; }
@ -21,7 +22,9 @@ public class Mode123 extends ModePassthrough {
private final ArrayList<ArrayList<String>> KEY_CHARACTERS = new ArrayList<>();
public Mode123(InputType inputType) {
public Mode123(InputType inputType, Language language) {
this.language = language;
if (inputType.isPhoneNumber()) {
getPhoneSpecialCharacters();
} else if (inputType.isNumeric()) {
@ -79,14 +82,20 @@ public class Mode123 extends ModePassthrough {
}
@Override
protected boolean nextSpecialCharacters() {
return digitSequence.equals(Language.SPECIAL_CHARS_KEY) && super.nextSpecialCharacters();
}
@Override public boolean onNumber(int number, boolean hold, int repeat) {
reset();
digitSequence = String.valueOf(number);
if (hold && number < KEY_CHARACTERS.size() && KEY_CHARACTERS.get(number).size() > 0) {
suggestions.addAll(KEY_CHARACTERS.get(number));
} else {
autoAcceptTimeout = 0;
suggestions.add(String.valueOf(number));
suggestions.add(digitSequence);
}
return true;
@ -111,4 +120,11 @@ public class Mode123 extends ModePassthrough {
|| (text.charAt(0) > 122 && text.charAt(0) < 127)
);
}
@Override
public void reset() {
super.reset();
digitSequence = "";
}
}

View file

@ -21,6 +21,7 @@ public class ModeABC extends InputMode {
public boolean onNumber(int number, boolean hold, int repeat) {
if (hold) {
reset();
digitSequence = String.valueOf(number);
suggestions.add(language.getKeyNumber(number));
autoAcceptTimeout = 0;
} else if (repeat > 0) {
@ -28,7 +29,9 @@ public class ModeABC extends InputMode {
autoAcceptTimeout = settings.getAbcAutoAcceptTimeout();
} else {
reset();
digitSequence = String.valueOf(number);
suggestions.addAll(language.getKeyCharacters(number));
suggestions.add(language.getKeyNumber(number));
autoAcceptTimeout = settings.getAbcAutoAcceptTimeout();
}
@ -40,6 +43,16 @@ public class ModeABC extends InputMode {
return newTextCase == CASE_UPPER ? word.toUpperCase(language.getLocale()) : word.toLowerCase(language.getLocale());
}
@Override
protected boolean nextSpecialCharacters() {
if (digitSequence.equals(Language.SPECIAL_CHARS_KEY) && super.nextSpecialCharacters()) {
suggestions.add(language.getKeyNumber(digitSequence.charAt(0) - '0'));
return true;
}
return false;
}
@Override
public void changeLanguage(Language language) {
super.changeLanguage(language);
@ -59,6 +72,7 @@ public class ModeABC extends InputMode {
@Override
public void reset() {
super.reset();
digitSequence = "";
shouldSelectNextLetter = false;
}

View file

@ -12,17 +12,20 @@ import io.github.sspanak.tt9.ime.helpers.TextField;
import io.github.sspanak.tt9.ime.modes.helpers.AutoSpace;
import io.github.sspanak.tt9.ime.modes.helpers.AutoTextCase;
import io.github.sspanak.tt9.ime.modes.helpers.Predictions;
import io.github.sspanak.tt9.languages.Characters;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.preferences.SettingsStore;
public class ModePredictive extends InputMode {
private final String LOG_TAG = getClass().getSimpleName();
private final static String PREFERRED_CHAR_SEQUENCE = "00";
private final static String EMOJI_SEQUENCE = "11";
private final SettingsStore settings;
public int getId() { return MODE_PREDICTIVE; }
private String digitSequence = "";
private String lastAcceptedWord = "";
// stem filter
@ -46,9 +49,11 @@ public class ModePredictive extends InputMode {
autoSpace = new AutoSpace(settings);
autoTextCase = new AutoTextCase(settings);
predictions = new Predictions(settings);
predictions = new Predictions();
this.settings = settings;
digitSequence = "";
}
@ -205,10 +210,8 @@ public class ModePredictive extends InputMode {
/**
* loadSuggestions
* Loads the possible list of suggestions for the current digitSequence.
* Returns "false" on invalid sequence.
*
* "currentWord" is used for generating suggestions when there are no results.
* Loads the possible list of suggestions for the current digitSequence. "currentWord" is used
* for generating suggestions when there are no results.
* See: Predictions.generatePossibleCompletions()
*/
@Override
@ -218,6 +221,10 @@ public class ModePredictive extends InputMode {
return;
}
if (loadStaticSuggestions(onLoad)) {
return;
}
onSuggestionsUpdated = onLoad;
predictions
.setDigitSequence(digitSequence)
@ -229,6 +236,29 @@ public class ModePredictive extends InputMode {
.load();
}
/**
* loadStatic
* Loads words that are not in the database and are supposed to be in the same order, such as
* emoji or the preferred character for double "0". Returns "false", when there are no static
* options for the current digitSequence.
*/
private boolean loadStaticSuggestions(Runnable onLoad) {
if (digitSequence.startsWith(EMOJI_SEQUENCE)) {
digitSequence = digitSequence.substring(0, Math.min(digitSequence.length(), Characters.getEmojiLevels() + 1));
specialCharSelectedGroup = digitSequence.length() - 2;
super.nextSpecialCharacters();
onLoad.run();
return true;
} else if (digitSequence.startsWith(PREFERRED_CHAR_SEQUENCE)) {
suggestions.clear();
suggestions.add(settings.getDoubleZeroChar());
onLoad.run();
return true;
}
return false;
}
/**
* getPredictions
@ -270,7 +300,7 @@ public class ModePredictive extends InputMode {
// emoji and punctuation are not in the database, so there is no point in
// running queries that would update nothing
if (!sequence.startsWith("11") && !sequence.equals("1") && !sequence.startsWith("0")) {
if (!sequence.startsWith(Language.PUNCTUATION_KEY) && !sequence.startsWith(Language.SPECIAL_CHARS_KEY)) {
WordStoreAsync.makeTopWord(language, currentWord, sequence);
}
} catch (Exception e) {
@ -284,6 +314,11 @@ public class ModePredictive extends InputMode {
return autoTextCase.adjustSuggestionTextCase(language, word, newTextCase);
}
@Override
protected boolean nextSpecialCharacters() {
return digitSequence.equals(Language.SPECIAL_CHARS_KEY) && super.nextSpecialCharacters();
}
@Override
public void determineNextWordTextCase(String textBeforeCursor) {
textCase = autoTextCase.determineNextWordTextCase(textCase, textFieldTextCase, textBeforeCursor);
@ -296,12 +331,14 @@ public class ModePredictive extends InputMode {
}
@Override
public void nextTextCase() {
textFieldTextCase = CASE_UNDEFINED; // since it's a user's choice, the default matters no more
super.nextTextCase();
public boolean nextTextCase() {
boolean changed = super.nextTextCase();
textFieldTextCase = changed ? CASE_UNDEFINED : textFieldTextCase; // since it's a user's choice, the default matters no more
return changed;
}
/**
* shouldAcceptPreviousSuggestion
* Automatic space assistance. Spaces (and special chars) cause suggestions to be accepted
@ -329,7 +366,7 @@ public class ModePredictive extends InputMode {
}
// special characters always break words
if (autoAcceptTimeout == 0 && !digitSequence.startsWith("0")) {
if (autoAcceptTimeout == 0 && !digitSequence.startsWith(Language.SPECIAL_CHARS_KEY)) {
return true;
}
@ -337,14 +374,14 @@ public class ModePredictive extends InputMode {
if (language.isHebrew() || language.isUkrainian()) {
return
predictions.noDbWords()
&& digitSequence.equals("1");
&& digitSequence.equals(Language.PUNCTUATION_KEY);
}
// punctuation breaks words, unless there are database matches ('s, qu', по-, etc...)
return
!digitSequence.isEmpty()
&& predictions.noDbWords()
&& digitSequence.contains("1")
&& digitSequence.contains(Language.PUNCTUATION_KEY)
&& TextTools.containsOtherThan1(digitSequence);
}

View file

@ -1,17 +1,14 @@
package io.github.sspanak.tt9.ime.modes.helpers;
import java.util.ArrayList;
import java.util.regex.Pattern;
import io.github.sspanak.tt9.db.WordStoreAsync;
import io.github.sspanak.tt9.ime.EmptyDatabaseWarning;
import io.github.sspanak.tt9.languages.Characters;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.preferences.SettingsStore;
public class Predictions {
private final EmptyDatabaseWarning emptyDbWarning;
private final SettingsStore settings;
private Language language;
private String digitSequence;
@ -26,22 +23,9 @@ public class Predictions {
private boolean areThereDbWords = false;
private ArrayList<String> words = new ArrayList<>();
// punctuation/emoji
private final Pattern containsOnly1Regex = Pattern.compile("^1+$");
private final String maxEmojiSequence;
public Predictions(SettingsStore settingsStore) {
public Predictions() {
emptyDbWarning = new EmptyDatabaseWarning();
settings = settingsStore;
// digitSequence limiter when selecting emoji
// "11" = Emoji level 0, "111" = Emoji level 1,... up to the maximum amount of 1s
StringBuilder maxEmojiSequenceBuilder = new StringBuilder();
for (int i = 0; i <= Characters.getEmojiLevels(); i++) {
maxEmojiSequenceBuilder.append("1");
}
maxEmojiSequence = maxEmojiSequenceBuilder.toString();
}
@ -125,54 +109,15 @@ public class Predictions {
return;
}
if (loadStatic()) {
onWordsChanged.run();
} else {
WordStoreAsync.getWords(
(words) -> onDbWords(words, true),
language,
digitSequence,
stem,
SettingsStore.SUGGESTIONS_MIN,
SettingsStore.SUGGESTIONS_MAX
);
}
}
WordStoreAsync.getWords(
(words) -> onDbWords(words, true),
language,
digitSequence,
stem,
SettingsStore.SUGGESTIONS_MIN,
SettingsStore.SUGGESTIONS_MAX
);
/**
* loadStatic
* Similar to "load()", but loads words that are not in the database.
* Returns "false", when there are no static options for the current digitSequence.
*/
private boolean loadStatic() {
// whitespace/special/math characters
if (digitSequence.equals("0")) {
stem = "";
words.clear();
words.addAll(language.getKeyCharacters(0, false));
}
// "00" is a shortcut for the preferred character
else if (digitSequence.equals("00")) {
stem = "";
words.clear();
words.add(settings.getDoubleZeroChar());
}
// emoji
else if (containsOnly1Regex.matcher(digitSequence).matches()) {
stem = "";
words.clear();
if (digitSequence.length() == 1) {
words.addAll(language.getKeyCharacters(1, false));
} else {
digitSequence = digitSequence.length() <= maxEmojiSequence.length() ? digitSequence : maxEmojiSequence;
words.addAll(Characters.getEmoji(digitSequence.length() - 2));
}
} else {
return false;
}
return true;
}
private void loadWithoutLeadingPunctuation() {
@ -242,7 +187,7 @@ public class Predictions {
// append all letters for the last digit in the sequence (the last pressed key)
int lastSequenceDigit = digitSequence.charAt(digitSequence.length() - 1) - '0';
for (String keyLetter : language.getKeyCharacters(lastSequenceDigit, false)) {
for (String keyLetter : language.getKeyCharacters(lastSequenceDigit)) {
generatedWords.add(baseWord + keyLetter);
}

View file

@ -27,8 +27,12 @@ public class Characters {
",", ".", "-", "", "", "(", ")", "[", "]", "&", "~", "`", "'", ";", ":", "!", "?"
));
final public static ArrayList<String> Currency = new ArrayList<>(Arrays.asList(
"$", "", "", "", "", "", "¥", "", "£"
));
final public static ArrayList<String> Special = new ArrayList<>(Arrays.asList(
" ", "\n", "@", "_", "#", "%", "$", "{", "}", "|", "^", "<", ">", "\\", "/", "=", "*", "+"
" ", "\n", "@", "_", "#", "%", "{", "}", "|", "^", "<", ">", "\\", "/", "=", "*", "+"
));
final private static ArrayList<String> TextEmoticons = new ArrayList<>(Arrays.asList(

View file

@ -8,6 +8,9 @@ import java.util.Locale;
public class Language {
public static String SPECIAL_CHARS_KEY = "0";
public static String PUNCTUATION_KEY = "1";
private int id;
protected String name;
protected Locale locale;
@ -120,7 +123,7 @@ public class Language {
final public String getAbcString() {
if (abcString == null) {
ArrayList<String> lettersList = getKeyCharacters(2, false);
ArrayList<String> lettersList = getKeyCharacters(2);
abcString = "";
StringBuilder sb = new StringBuilder();
@ -143,11 +146,11 @@ public class Language {
* Returns "true" when the language is based on the Latin alphabet or "false" otherwise.
*/
public boolean isLatinBased() {
return getKeyCharacters(2, false).contains("a");
return getKeyCharacters(2).contains("a");
}
public boolean isCyrillic() {
return getKeyCharacters(2, false).contains("а");
return getKeyCharacters(2).contains("а");
}
public boolean isRTL() {
@ -155,19 +158,19 @@ public class Language {
}
public boolean isGreek() {
return getKeyCharacters(2, false).contains("α");
return getKeyCharacters(2).contains("α");
}
public boolean isArabic() {
return getKeyCharacters(3, false).contains("ا");
return getKeyCharacters(3).contains("ا");
}
public boolean isUkrainian() {
return getKeyCharacters(3, false).contains("є");
return getKeyCharacters(3).contains("є");
}
public boolean isHebrew() {
return getKeyCharacters(3, false).contains("א");
return getKeyCharacters(3).contains("א");
}
/* ************ utility ************ */
@ -240,21 +243,27 @@ public class Language {
return word != null && word.toUpperCase(locale).equals(word);
}
public ArrayList<String> getKeyCharacters(int key, boolean includeDigit) {
public ArrayList<String> getKeyCharacters(int key, int characterGroup) {
if (key < 0 || key >= layout.size()) {
return new ArrayList<>();
}
ArrayList<String> chars = new ArrayList<>(layout.get(key));
if (includeDigit && chars.size() > 0) {
chars.add(getKeyNumber(key));
if (characterGroup > 0) {
if (key == 0 && characterGroup == 1) {
chars = new ArrayList<>(Characters.Currency);
} else if (key == 1) {
chars = new ArrayList<>(Characters.getEmoji(characterGroup - 1));
} else {
chars = new ArrayList<>();
}
}
return chars;
}
public ArrayList<String> getKeyCharacters(int key) {
return getKeyCharacters(key, true);
return getKeyCharacters(key, 0);
}
public String getKeyNumber(int key) {
@ -285,6 +294,7 @@ public class Language {
return sequence.toString();
}
@NonNull
@Override
public String toString() {

View file

@ -108,7 +108,7 @@ public class SoftNumberKey extends SoftKey {
boolean isGreekBased = language.isGreek();
StringBuilder sb = new StringBuilder();
ArrayList<String> chars = language.getKeyCharacters(number, false);
ArrayList<String> chars = language.getKeyCharacters(number);
for (int i = 0; i < 5 && i < chars.size(); i++) {
String currentLetter = chars.get(i);
if (