emoji can now be added as custom words
This commit is contained in:
parent
e2409f4354
commit
0e9954864e
11 changed files with 143 additions and 54 deletions
|
|
@ -21,6 +21,7 @@ import io.github.sspanak.tt9.db.sqlite.InsertOps;
|
|||
import io.github.sspanak.tt9.db.sqlite.SQLiteOpener;
|
||||
import io.github.sspanak.tt9.db.sqlite.Tables;
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
import io.github.sspanak.tt9.languages.EmojiLanguage;
|
||||
import io.github.sspanak.tt9.languages.InvalidLanguageCharactersException;
|
||||
import io.github.sspanak.tt9.languages.InvalidLanguageException;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
|
|
@ -170,6 +171,7 @@ public class DictionaryLoader {
|
|||
|
||||
start = System.currentTimeMillis();
|
||||
DeleteOps.delete(sqlite, language.getId());
|
||||
DeleteOps.delete(sqlite, new EmojiLanguage().getId());
|
||||
sendProgressMessage(language, ++progress, SettingsStore.DICTIONARY_IMPORT_PROGRESS_UPDATE_TIME);
|
||||
logLoadingStep("Storage cleared", language, start);
|
||||
|
||||
|
|
@ -180,6 +182,7 @@ public class DictionaryLoader {
|
|||
|
||||
start = System.currentTimeMillis();
|
||||
InsertOps.restoreCustomWords(sqlite.getDb(), language);
|
||||
InsertOps.restoreCustomWords(sqlite.getDb(), new EmojiLanguage());
|
||||
sendProgressMessage(language, ++progress, SettingsStore.DICTIONARY_IMPORT_PROGRESS_UPDATE_TIME);
|
||||
logLoadingStep("Custom words restored", language, start);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import io.github.sspanak.tt9.db.sqlite.ReadOps;
|
|||
import io.github.sspanak.tt9.db.sqlite.SQLiteOpener;
|
||||
import io.github.sspanak.tt9.db.sqlite.UpdateOps;
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
import io.github.sspanak.tt9.languages.Characters;
|
||||
import io.github.sspanak.tt9.languages.EmojiLanguage;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.Text;
|
||||
import io.github.sspanak.tt9.preferences.SettingsStore;
|
||||
|
|
@ -136,6 +138,8 @@ public class WordStore {
|
|||
return AddWordDialog.CODE_GENERAL_ERROR;
|
||||
}
|
||||
|
||||
language = Characters.isGraphic(word) ? new EmojiLanguage() : language;
|
||||
|
||||
try {
|
||||
if (readOps.exists(sqlite.getDb(), language, word)) {
|
||||
return AddWordDialog.CODE_WORD_EXISTS;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import java.util.ArrayList;
|
|||
|
||||
import io.github.sspanak.tt9.BuildConfig;
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.languages.EmojiLanguage;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||
|
||||
|
|
@ -23,7 +24,8 @@ public class SQLiteOpener extends SQLiteOpenHelper {
|
|||
|
||||
public SQLiteOpener(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
allLanguages = LanguageCollection.getAll(context);
|
||||
allLanguages = new ArrayList<>(LanguageCollection.getAll(context));
|
||||
allLanguages.add(new EmojiLanguage());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@ import io.github.sspanak.tt9.languages.Text;
|
|||
public class TextField {
|
||||
public static final int IME_ACTION_ENTER = EditorInfo.IME_MASK_ACTION + 1;
|
||||
|
||||
private static final Pattern beforeCursorWordRegex = Pattern.compile("(\\w+)(?!\n)$");
|
||||
private static final Pattern afterCursorWordRegex = Pattern.compile("^(?<!\n)(\\w+)");
|
||||
private static final Pattern beforeCursorUkrainianRegex = Pattern.compile("([\\w']+)(?!\n)$");
|
||||
private static final Pattern afterCursorUkrainianRegex = Pattern.compile("^(?<!\n)([\\w']+)");
|
||||
private static final Pattern beforeCursorWordRegex = Pattern.compile("([^\\s\\d\\p{P}]+)(?!\n)$");
|
||||
private static final Pattern afterCursorWordRegex = Pattern.compile("^(?<!\n)([^\\s\\d\\p{P}]+)");
|
||||
private static final Pattern beforeCursorUkrainianRegex = Pattern.compile("([(?:^\\s\\d\\p{P}|')]+)(?!\n)$");
|
||||
private static final Pattern afterCursorUkrainianRegex = Pattern.compile("^(?<!\n)([(?:^\\s\\d\\p{P}|')]+)");
|
||||
|
||||
|
||||
public final InputConnection connection;
|
||||
|
|
|
|||
|
|
@ -133,21 +133,22 @@ abstract public class InputMode {
|
|||
* 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()) {
|
||||
protected boolean nextSpecialCharacters() { return nextSpecialCharacters(language); }
|
||||
protected boolean nextSpecialCharacters(Language altLanguage) {
|
||||
if (altLanguage == null || digitSequence.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int previousGroup = specialCharSelectedGroup;
|
||||
int key = digitSequence.charAt(0) - '0';
|
||||
ArrayList<String> chars = language.getKeyCharacters(key, ++specialCharSelectedGroup);
|
||||
ArrayList<String> chars = altLanguage.getKeyCharacters(key, ++specialCharSelectedGroup);
|
||||
|
||||
if (chars.isEmpty() && specialCharSelectedGroup == 1) {
|
||||
specialCharSelectedGroup = 0;
|
||||
return false;
|
||||
} else if (chars.isEmpty()) {
|
||||
specialCharSelectedGroup = 0;
|
||||
chars = language.getKeyCharacters(key, specialCharSelectedGroup);
|
||||
chars = altLanguage.getKeyCharacters(key, specialCharSelectedGroup);
|
||||
}
|
||||
|
||||
suggestions.clear();
|
||||
|
|
|
|||
|
|
@ -5,22 +5,21 @@ import androidx.annotation.NonNull;
|
|||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.languages.Text;
|
||||
import io.github.sspanak.tt9.db.WordStoreAsync;
|
||||
import io.github.sspanak.tt9.ime.helpers.InputType;
|
||||
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.EmojiLanguage;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.Text;
|
||||
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;
|
||||
|
||||
|
|
@ -66,6 +65,11 @@ public class ModePredictive extends InputMode {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (digitSequence.startsWith(EmojiLanguage.EMOJI_SEQUENCE) && specialCharSelectedGroup > 0) {
|
||||
specialCharSelectedGroup -= 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
digitSequence = digitSequence.substring(0, digitSequence.length() - 1);
|
||||
if (digitSequence.length() == 0) {
|
||||
clearWordStem();
|
||||
|
|
@ -88,13 +92,17 @@ public class ModePredictive extends InputMode {
|
|||
disablePredictions = true;
|
||||
suggestions.add(language.getKeyNumber(number));
|
||||
} else {
|
||||
// words
|
||||
super.reset();
|
||||
disablePredictions = false;
|
||||
digitSequence += number;
|
||||
if (number == 0 && repeat > 0) {
|
||||
disablePredictions = false;
|
||||
|
||||
if (digitSequence.equals(Language.PREFERRED_CHAR_SEQUENCE)) {
|
||||
autoAcceptTimeout = 0;
|
||||
}
|
||||
|
||||
// custom emoji are longest 1-key sequence, so do not allow typing any longer than that,
|
||||
// to prevent side effects
|
||||
digitSequence = digitSequence.startsWith(EmojiLanguage.CUSTOM_EMOJI_SEQUENCE) ? EmojiLanguage.CUSTOM_EMOJI_SEQUENCE : digitSequence;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -225,12 +233,14 @@ public class ModePredictive extends InputMode {
|
|||
return;
|
||||
}
|
||||
|
||||
Language searchLanguage = digitSequence.startsWith(EmojiLanguage.CUSTOM_EMOJI_SEQUENCE) ? new EmojiLanguage() : language;
|
||||
|
||||
onSuggestionsUpdated = onLoad;
|
||||
predictions
|
||||
.setDigitSequence(digitSequence)
|
||||
.setIsStemFuzzy(isStemFuzzy)
|
||||
.setStem(stem)
|
||||
.setLanguage(language)
|
||||
.setLanguage(searchLanguage)
|
||||
.setInputWord(currentWord)
|
||||
.setWordsChangedHandler(this::getPredictions)
|
||||
.load();
|
||||
|
|
@ -243,13 +253,16 @@ public class ModePredictive extends InputMode {
|
|||
* 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;
|
||||
if (digitSequence.equals(Language.PUNCTUATION_KEY)) {
|
||||
super.nextSpecialCharacters();
|
||||
onLoad.run();
|
||||
return true;
|
||||
} else if (digitSequence.startsWith(PREFERRED_CHAR_SEQUENCE)) {
|
||||
} else if (digitSequence.equals(EmojiLanguage.EMOJI_SEQUENCE)) {
|
||||
specialCharSelectedGroup = -1;
|
||||
nextSpecialCharacters(new EmojiLanguage());
|
||||
onLoad.run();
|
||||
return true;
|
||||
} else if (digitSequence.startsWith(Language.PREFERRED_CHAR_SEQUENCE)) {
|
||||
suggestions.clear();
|
||||
suggestions.add(settings.getDoubleZeroChar());
|
||||
onLoad.run();
|
||||
|
|
@ -265,7 +278,12 @@ public class ModePredictive extends InputMode {
|
|||
* Gets the currently available Predictions and sends them over to the external caller.
|
||||
*/
|
||||
private void getPredictions() {
|
||||
digitSequence = predictions.getDigitSequence();
|
||||
// in case the user hasn't added any custom emoji, do not allow advancing to the empty character group
|
||||
if (predictions.getList().isEmpty() && digitSequence.equals(EmojiLanguage.CUSTOM_EMOJI_SEQUENCE)) {
|
||||
digitSequence = EmojiLanguage.EMOJI_SEQUENCE;
|
||||
return;
|
||||
}
|
||||
|
||||
suggestions.clear();
|
||||
suggestions.addAll(predictions.getList());
|
||||
|
||||
|
|
@ -327,7 +345,9 @@ public class ModePredictive extends InputMode {
|
|||
|
||||
@Override
|
||||
protected boolean nextSpecialCharacters() {
|
||||
return digitSequence.equals(Language.SPECIAL_CHARS_KEY) && super.nextSpecialCharacters();
|
||||
return
|
||||
(digitSequence.equals(EmojiLanguage.EMOJI_SEQUENCE) && super.nextSpecialCharacters(new EmojiLanguage()))
|
||||
|| (digitSequence.equals(Language.SPECIAL_CHARS_KEY) && super.nextSpecialCharacters());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package io.github.sspanak.tt9.ime.modes.helpers;
|
|||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.db.WordStoreAsync;
|
||||
import io.github.sspanak.tt9.languages.EmojiLanguage;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.preferences.SettingsStore;
|
||||
|
||||
|
|
@ -36,10 +37,6 @@ public class Predictions {
|
|||
return this;
|
||||
}
|
||||
|
||||
public String getDigitSequence() {
|
||||
return digitSequence;
|
||||
}
|
||||
|
||||
public Predictions setIsStemFuzzy(boolean yes) {
|
||||
this.isStemFuzzy = yes;
|
||||
return this;
|
||||
|
|
@ -106,8 +103,10 @@ public class Predictions {
|
|||
return;
|
||||
}
|
||||
|
||||
boolean retryAllowed = !digitSequence.equals(EmojiLanguage.CUSTOM_EMOJI_SEQUENCE);
|
||||
|
||||
WordStoreAsync.getWords(
|
||||
(words) -> onDbWords(words, true),
|
||||
(words) -> onDbWords(words, retryAllowed),
|
||||
language,
|
||||
digitSequence,
|
||||
stem,
|
||||
|
|
@ -153,10 +152,14 @@ public class Predictions {
|
|||
}
|
||||
|
||||
words.clear();
|
||||
suggestStem();
|
||||
suggestMissingWords(generatePossibleStemVariations(dbWords));
|
||||
suggestMissingWords(dbWords.isEmpty() ? generateWordVariations(inputWord) : dbWords);
|
||||
words = insertPunctuationCompletions(words);
|
||||
if (digitSequence.equals(EmojiLanguage.CUSTOM_EMOJI_SEQUENCE)) {
|
||||
words.addAll(dbWords);
|
||||
} else {
|
||||
suggestStem();
|
||||
suggestMissingWords(generatePossibleStemVariations(dbWords));
|
||||
suggestMissingWords(dbWords.isEmpty() ? generateWordVariations(inputWord) : dbWords);
|
||||
words = insertPunctuationCompletions(words);
|
||||
}
|
||||
|
||||
onWordsChanged.run();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public class Characters {
|
|||
));
|
||||
|
||||
final public static ArrayList<String> Currency = new ArrayList<>(Arrays.asList(
|
||||
"$", "€", "₹", "₿", "₩", "¢", "₺", "₱", "¥", "₽", "£"
|
||||
"$", "€", "₹", "₿", "₩", "¢", "¤", "₺", "₱", "¥", "₽", "£"
|
||||
));
|
||||
|
||||
final public static ArrayList<String> Special = new ArrayList<>(Arrays.asList(
|
||||
|
|
@ -58,13 +58,24 @@ public class Characters {
|
|||
))
|
||||
));
|
||||
|
||||
public static boolean noEmojiSupported() {
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M;
|
||||
public static boolean isGraphic(String str) {
|
||||
if (str == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0, end = str.length(); i < end; i++) {
|
||||
char ch = str.charAt(i);
|
||||
|
||||
if (ch < 256 || Character.isLetterOrDigit(ch) || Character.isAlphabetic(ch)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static int getEmojiLevels() {
|
||||
return noEmojiSupported() ? 1 : Emoji.size();
|
||||
public static boolean noEmojiSupported() {
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -73,7 +84,9 @@ public class Characters {
|
|||
return new ArrayList<>(TextEmoticons);
|
||||
}
|
||||
|
||||
level = (Emoji.size() > level) ? level : Emoji.size() - 1;
|
||||
if (level < 0 || level >= Emoji.size()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Paint paint = new Paint();
|
||||
ArrayList<String> availableEmoji = new ArrayList<>();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package io.github.sspanak.tt9.languages;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
public class EmojiLanguage extends Language {
|
||||
final public static String EMOJI_SEQUENCE = "11";
|
||||
final public static String CUSTOM_EMOJI_SEQUENCE = EMOJI_SEQUENCE + "1";
|
||||
|
||||
public EmojiLanguage() {
|
||||
id = Integer.parseInt(EMOJI_SEQUENCE);
|
||||
locale = Locale.ROOT;
|
||||
abcString = "emoji";
|
||||
name = "Emoji";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDigitSequenceForWord(String word) {
|
||||
return Characters.isGraphic(word) ? CUSTOM_EMOJI_SEQUENCE : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<String> getKeyCharacters(int key, int characterGroup) {
|
||||
return key == 1 && characterGroup >= 0 ? new ArrayList<>(Characters.getEmoji(characterGroup)) : super.getKeyCharacters(key, characterGroup);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,10 +8,11 @@ import java.util.Locale;
|
|||
|
||||
|
||||
public class Language implements Comparable<Language> {
|
||||
public static String SPECIAL_CHARS_KEY = "0";
|
||||
public static String PUNCTUATION_KEY = "1";
|
||||
final public static String SPECIAL_CHARS_KEY = "0";
|
||||
final public static String PUNCTUATION_KEY = "1";
|
||||
final public static String PREFERRED_CHAR_SEQUENCE = "00";
|
||||
|
||||
private int id;
|
||||
protected int id;
|
||||
protected String name;
|
||||
protected Locale locale;
|
||||
protected String dictionaryFile;
|
||||
|
|
@ -193,7 +194,8 @@ public class Language implements Comparable<Language> {
|
|||
* -> 2 | 224 | 2048 | 229376 (shift each 5-bit number, not overlap with the previous ones)
|
||||
* -> 231650
|
||||
*
|
||||
* Maximum ID is: "zz-ZZ" -> 879450
|
||||
* Minimum ID: "aa" -> 33
|
||||
* Maximum ID: "zz-ZZ" -> 879450
|
||||
*/
|
||||
private int generateId() {
|
||||
String idString = (locale.getLanguage() + locale.getCountry()).toUpperCase();
|
||||
|
|
@ -219,14 +221,12 @@ public class Language implements Comparable<Language> {
|
|||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
ArrayList<String> chars = new ArrayList<>(layout.get(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 {
|
||||
ArrayList<String> chars = layout.get(key);
|
||||
if (key == 0) {
|
||||
if (characterGroup > 1) {
|
||||
chars = new ArrayList<>();
|
||||
} else if (characterGroup == 1) {
|
||||
chars = new ArrayList<>(Characters.Currency);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue