1
0
Fork 0

new way of displaying the special chars

This commit is contained in:
sspanak 2025-05-07 19:08:56 +03:00 committed by Dimo Karaivanov
parent c484050678
commit ce3446c3dd
26 changed files with 313 additions and 178 deletions

View file

@ -162,7 +162,7 @@ abstract public class CommandHandler extends TextEditingHandler {
determineTextCase();
}
// save the settings for the next time
suggestionOps.setInputMode(mInputMode);
settings.saveInputMode(mInputMode.getId());
}
@ -206,7 +206,7 @@ abstract public class CommandHandler extends TextEditingHandler {
getSuggestions(null);
setStatusIcon(mInputMode, mLanguage);
statusBar.setText(mInputMode);
suggestionOps.setRTL(isLanguageRTL);
suggestionOps.setLanguage(mLanguage);
mainView.render();
if (settings.isMainLayoutStealth() && !settings.isStatusIconEnabled()) {
UI.toastShortSingle(this, mInputMode.getClass().getSimpleName(), mInputMode.toString());

View file

@ -36,8 +36,8 @@ public abstract class HotkeyHandler extends CommandHandler {
suggestionOps.cancelDelayedAccept();
if (!suggestionOps.isEmpty()) {
if (mInputMode.shouldReplacePreviousSuggestion()) {
mInputMode.onReplaceSuggestion(suggestionOps.getCurrent());
if (mInputMode.shouldReplacePreviousSuggestion(suggestionOps.getCurrent())) {
mInputMode.onReplaceSuggestion(suggestionOps.getCurrentRaw());
} else {
onAcceptSuggestionManually(suggestionOps.acceptCurrent(), KeyEvent.KEYCODE_ENTER);
}

View file

@ -14,7 +14,7 @@ abstract public class TextEditingHandler extends VoiceHandler {
protected void initTray() {
super.initTray();
detectRTL();
suggestionOps.setRTL(isLanguageRTL);
suggestionOps.setLanguage(LanguageCollection.getLanguage(settings.getInputLanguage()));
}

View file

@ -72,6 +72,7 @@ public abstract class TypingHandler extends KeyPadHandler {
mInputMode = determineInputMode();
determineTextCase();
suggestionOps.set(null);
suggestionOps.setInputMode(mInputMode);
return true;
}
@ -89,6 +90,7 @@ public abstract class TypingHandler extends KeyPadHandler {
// changing the TextField and notifying all interested classes is an atomic operation
appHacks = new AppHacks(inputType, textField, textSelection);
suggestionOps.setTextField(textField);
suggestionOps.setInputMode(mInputMode);
}

View file

@ -8,7 +8,10 @@ import androidx.annotation.Nullable;
import java.util.ArrayList;
import io.github.sspanak.tt9.ime.modes.InputMode;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageKind;
import io.github.sspanak.tt9.languages.NullLanguage;
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.ui.main.ResizableMainView;
import io.github.sspanak.tt9.ui.tray.SuggestionsBar;
@ -18,6 +21,10 @@ import io.github.sspanak.tt9.util.Text;
public class SuggestionOps {
@NonNull private final Handler delayedAcceptHandler;
@NonNull private final ConsumerCompat<String> onDelayedAccept;
@Nullable private InputMode inputMode;
@NonNull private Language language;
@Nullable private final SettingsStore settings;
@Nullable protected SuggestionsBar suggestionBar;
@NonNull private TextField textField;
@ -26,16 +33,25 @@ public class SuggestionOps {
delayedAcceptHandler = new Handler(Looper.getMainLooper());
this.onDelayedAccept = onDelayedAccept != null ? onDelayedAccept : s -> {};
language = new NullLanguage();
this.settings = settings;
this.textField = textField != null ? textField : new TextField(null, null, null);
if (settings != null && mainView != null && onSuggestionClick != null) {
suggestionBar = new SuggestionsBar(settings, mainView, onSuggestionClick);
}
}
public void setRTL(boolean yes) {
public void setInputMode(@Nullable InputMode inputMode) {
this.inputMode = inputMode;
}
public void setLanguage(@Nullable Language language) {
this.language = language == null ? new NullLanguage() : language;
if (suggestionBar != null) {
suggestionBar.setRTL(yes);
suggestionBar.setRTL(LanguageKind.isRTL(language));
}
}
@ -154,6 +170,11 @@ public class SuggestionOps {
}
public String getCurrentRaw() {
return suggestionBar != null ? suggestionBar.getRaw(getCurrentIndex()) : "";
}
public String getCurrent(Language language, int maxLength) {
if (maxLength == 0 || isEmpty()) {
return "";

View file

@ -7,11 +7,14 @@ import java.util.ArrayList;
import io.github.sspanak.tt9.hacks.InputType;
import io.github.sspanak.tt9.ime.helpers.TextField;
import io.github.sspanak.tt9.ime.modes.helpers.Sequences;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageKind;
import io.github.sspanak.tt9.languages.NullLanguage;
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.ui.tray.SuggestionsBar;
import io.github.sspanak.tt9.util.Logger;
import io.github.sspanak.tt9.util.chars.Characters;
abstract public class InputMode {
// typing mode
@ -40,6 +43,7 @@ abstract public class InputMode {
@NonNull protected final ArrayList<String> suggestions = new ArrayList<>();
@NonNull protected Runnable onSuggestionsUpdated = () -> {};
protected int specialCharSelectedGroup = 0;
@NonNull protected Sequences seq = new Sequences();
protected InputMode(SettingsStore settings, InputType inputType) {
@ -83,7 +87,15 @@ abstract public class InputMode {
public void onAcceptSuggestion(@NonNull String word) { onAcceptSuggestion(word, false); }
public void onAcceptSuggestion(@NonNull String word, boolean preserveWordList) {}
public void onCursorMove(@NonNull String word) { if (!digitSequence.isEmpty()) onAcceptSuggestion(word); }
public void onReplaceSuggestion(@NonNull String word) {}
public void onReplaceSuggestion(@NonNull String rawWord) {
if (SuggestionsBar.SHOW_SPECIAL_CHARS_SUGGESTION.equals(rawWord)) {
Logger.d("InputMode", "Loading special characters for: " + seq.SPECIAL_CHAR_SEQUENCE);
}
if (SuggestionsBar.SHOW_CURRENCIES_SUGGESTION.equals(rawWord)) {
Logger.d("InputMode", "Loading special characters for: " + seq.CURRENCY_SEQUENCE);
}
}
/**
* loadSuggestions
@ -140,7 +152,7 @@ abstract public class InputMode {
// Interaction with the IME. Return "true" if it should perform the respective action.
public boolean shouldAcceptPreviousSuggestion(String unacceptedText) { return false; }
public boolean shouldAcceptPreviousSuggestion(int nextKey, boolean hold) { return false; }
public boolean shouldReplacePreviousSuggestion() { return false; }
public boolean shouldReplacePreviousSuggestion(@Nullable String currentWord) { return Characters.PLACEHOLDER.equals(currentWord); }
public boolean shouldAddTrailingSpace(boolean isWordAcceptedManually, int nextKey) { return false; }
public boolean shouldAddPrecedingSpace() { return false; }
public boolean shouldDeletePrecedingSpace() { return false; }
@ -190,6 +202,14 @@ abstract public class InputMode {
protected String adjustSuggestionTextCase(String word, int newTextCase) { return word; }
protected ArrayList<String> getAbbreviatedSpecialChars() {
ArrayList<String> special = Characters.getWhitespaces(language);
special.add(SuggestionsBar.SHOW_CURRENCIES_SUGGESTION);
special.add(SuggestionsBar.SHOW_SPECIAL_CHARS_SUGGESTION);
return special;
}
protected boolean loadSpecialCharacters() {
int key = digitSequence.charAt(0) - '0';
ArrayList<String> chars = settings.getOrderedKeyChars(language, key, specialCharSelectedGroup);
@ -218,14 +238,22 @@ abstract public class InputMode {
return new ArrayList<>(unordered);
}
return orderSpecialChars(unordered, settings.getOrderedKeyChars(language, key));
}
public ArrayList<String> orderSpecialChars(@NonNull ArrayList<String> unordered, @Nullable ArrayList<String> order) {
ArrayList<String> ordered = new ArrayList<>();
if (unordered.isEmpty() || order == null || order.isEmpty()) {
return ordered;
}
if (isEmailMode) {
if (unordered.contains("@")) ordered.add("@");
if (unordered.contains("_")) ordered.add("_");
}
for (String ch : settings.getOrderedKeyChars(language, key)) {
for (String ch : order) {
if (isEmailMode && (ch.charAt(0) == '@' || ch.charAt(0) == '_')) {
continue;
}

View file

@ -1,6 +1,7 @@
package io.github.sspanak.tt9.ime.modes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
@ -28,7 +29,7 @@ class Mode123 extends ModePassthrough {
if (inputType.isPhoneNumber()) {
setSpecificSpecialCharacters(Characters.Phone, false);
} else if (inputType.isNumeric()) {
setSpecificSpecialCharacters(Characters.getSpecialForNumbers(inputType.isDecimal(), inputType.isSignedNumber()), false);
setSpecificSpecialCharacters(Characters.getAllForDecimal(inputType.isDecimal(), inputType.isSignedNumber()), false);
} else if (isEmailMode) {
setSpecificSpecialCharacters(Characters.Email, true);
} else {
@ -52,16 +53,15 @@ class Mode123 extends ModePassthrough {
*/
private void setDefaultSpecialCharacters() {
Language english = LanguageCollection.getByLocale("en");
KEY_CHARACTERS.add(getAbbreviatedSpecialChars());
KEY_CHARACTERS.add(
TextTools.removeLettersFromList(applyNumericFieldCharacterOrder(settings.getOrderedKeyChars(english, 0)))
);
KEY_CHARACTERS.add(
TextTools.removeLettersFromList(applyNumericFieldCharacterOrder(settings.getOrderedKeyChars(english, 1)))
TextTools.removeLettersFromList(orderSpecialChars(settings.getOrderedKeyChars(english, 1), null))
);
}
protected ArrayList<String> applyNumericFieldCharacterOrder(ArrayList<String> unordered) {
@Override
public ArrayList<String> orderSpecialChars(@NonNull ArrayList<String> unordered, @Nullable ArrayList<String> o) {
ArrayList<String> ordered = new ArrayList<>();
if (unordered.contains(".")) {

View file

@ -25,6 +25,9 @@ class ModeABC extends InputMode {
if (isEmailMode) {
KEY_CHARACTERS.add(applyPunctuationOrder(Characters.Email.get(0), 0));
KEY_CHARACTERS.add(applyPunctuationOrder(Characters.Email.get(1), 1));
} else {
KEY_CHARACTERS.add(getAbbreviatedSpecialChars());
KEY_CHARACTERS.add(settings.getOrderedKeyChars(language, 1));
}
}
@ -54,7 +57,7 @@ class ModeABC extends InputMode {
autoAcceptTimeout = settings.getAbcAutoAcceptTimeout();
digitSequence = String.valueOf(number);
shouldSelectNextLetter = false;
suggestions.addAll(KEY_CHARACTERS.size() > number ? KEY_CHARACTERS.get(number) : settings.getOrderedKeyChars(language, number));
suggestions.addAll(KEY_CHARACTERS.size() > number ? KEY_CHARACTERS.get(number) : new ArrayList<>());
suggestions.add(language.getKeyNumeral(number));
}

View file

@ -4,20 +4,19 @@ import androidx.annotation.Nullable;
import io.github.sspanak.tt9.hacks.InputType;
import io.github.sspanak.tt9.ime.helpers.TextField;
import io.github.sspanak.tt9.ime.modes.helpers.Sequences;
import io.github.sspanak.tt9.languages.EmojiLanguage;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageKind;
import io.github.sspanak.tt9.languages.NaturalLanguage;
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.util.TextTools;
import io.github.sspanak.tt9.util.chars.Characters;
public class ModeBopomofo extends ModePinyin {
private static final String SPECIAL_CHAR_SEQUENCE_PREFIX = "S0";
private static final String PUNCTUATION_SEQUENCE_PREFIX = "S1";
protected ModeBopomofo(SettingsStore settings, Language lang, InputType inputType, TextField textField) {
super(settings, lang, inputType, textField);
seq = new Sequences("S1", "S0");
}
@ -47,7 +46,7 @@ public class ModeBopomofo extends ModePinyin {
*/
protected void setCustomSpecialCharacters() {
// special
KEY_CHARACTERS.add(TextTools.removeLettersFromList(applyPunctuationOrder(Characters.getSpecial(language), 0)));
KEY_CHARACTERS.add(getAbbreviatedSpecialChars());
KEY_CHARACTERS.get(0).add(0, "0");
// punctuation
@ -56,20 +55,11 @@ public class ModeBopomofo extends ModePinyin {
);
}
protected void setSpecialCharacterConstants() {
CUSTOM_EMOJI_SEQUENCE = PUNCTUATION_SEQUENCE_PREFIX + EmojiLanguage.CUSTOM_EMOJI_SEQUENCE;
EMOJI_SEQUENCE = PUNCTUATION_SEQUENCE_PREFIX + EmojiLanguage.EMOJI_SEQUENCE;
PUNCTUATION_SEQUENCE = PUNCTUATION_SEQUENCE_PREFIX + NaturalLanguage.PUNCTUATION_KEY;
SPECIAL_CHAR_SEQUENCE = SPECIAL_CHAR_SEQUENCE_PREFIX + NaturalLanguage.SPECIAL_CHAR_KEY;
}
/***************************** TYPING *********************************/
@Override
public boolean onBackspace() {
if (digitSequence.equals(PUNCTUATION_SEQUENCE) || digitSequence.equals(SPECIAL_CHAR_SEQUENCE)) {
if (digitSequence.equals(seq.PUNCTUATION_SEQUENCE) || digitSequence.equals(seq.WHITESPACE_SEQUENCE)) {
digitSequence = "";
return false;
} else {
@ -80,8 +70,8 @@ public class ModeBopomofo extends ModePinyin {
@Override
protected void onNumberPress(int nextNumber) {
if (digitSequence.startsWith(PUNCTUATION_SEQUENCE)) {
digitSequence = PUNCTUATION_SEQUENCE_PREFIX + EmojiLanguage.validateEmojiSequence(digitSequence.substring(PUNCTUATION_SEQUENCE_PREFIX.length()), nextNumber);
if (digitSequence.startsWith(seq.PUNCTUATION_SEQUENCE)) {
digitSequence = EmojiLanguage.validateEmojiSequence(seq, digitSequence, nextNumber);
} else {
digitSequence += String.valueOf(nextNumber);
}
@ -92,10 +82,10 @@ public class ModeBopomofo extends ModePinyin {
protected void onNumberHold(int number) {
if (number == 0) {
disablePredictions = false;
digitSequence = SPECIAL_CHAR_SEQUENCE;
digitSequence = seq.WHITESPACE_SEQUENCE;
} else if (number == 1) {
disablePredictions = false;
digitSequence = PUNCTUATION_SEQUENCE;
digitSequence = seq.PUNCTUATION_SEQUENCE;
} else {
autoAcceptTimeout = 0;
suggestions.add(language.getKeyNumeral(number));
@ -112,7 +102,7 @@ public class ModeBopomofo extends ModePinyin {
public boolean shouldAcceptPreviousSuggestion(int nextKey, boolean hold) {
String newSequence = digitSequence + (char)(nextKey + '0');
return hold
|| newSequence.startsWith(SPECIAL_CHAR_SEQUENCE)
|| (newSequence.startsWith(PUNCTUATION_SEQUENCE) && nextKey != NaturalLanguage.PUNCTUATION_KEY.charAt(0) - '0');
|| newSequence.startsWith(seq.WHITESPACE_SEQUENCE)
|| (newSequence.startsWith(seq.PUNCTUATION_SEQUENCE) && nextKey != Sequences.PUNCTUATION_KEY);
}
}

View file

@ -9,13 +9,13 @@ import io.github.sspanak.tt9.hacks.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.Cheonjiin;
import io.github.sspanak.tt9.ime.modes.helpers.Sequences;
import io.github.sspanak.tt9.ime.modes.predictions.Predictions;
import io.github.sspanak.tt9.ime.modes.predictions.SyllablePredictions;
import io.github.sspanak.tt9.languages.EmojiLanguage;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection;
import io.github.sspanak.tt9.languages.LanguageKind;
import io.github.sspanak.tt9.languages.NaturalLanguage;
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.util.TextTools;
import io.github.sspanak.tt9.util.chars.Characters;
@ -26,11 +26,8 @@ class ModeCheonjiin extends InputMode {
protected final ArrayList<ArrayList<String>> KEY_CHARACTERS = new ArrayList<>();
// special chars and emojis
private static String SPECIAL_CHAR_SEQUENCE_PREFIX;
protected String CUSTOM_EMOJI_SEQUENCE;
protected String EMOJI_SEQUENCE;
protected String PUNCTUATION_SEQUENCE;
protected String SPECIAL_CHAR_SEQUENCE;
private final String PUNCTUATION_SEQUENCE_PREFIX = "11";
private final String SPECIAL_CHAR_SEQUENCE_PREFIX = "00";
// predictions
protected boolean disablePredictions = false;
@ -46,18 +43,15 @@ class ModeCheonjiin extends InputMode {
protected ModeCheonjiin(SettingsStore settings, InputType inputType, TextField textField) {
super(settings, inputType);
SPECIAL_CHAR_SEQUENCE_PREFIX = "11";
autoSpace = new AutoSpace(settings);
digitSequence = "";
allowedTextCases.add(CASE_LOWER);
digitSequence = "";
seq = new Sequences(PUNCTUATION_SEQUENCE_PREFIX, SPECIAL_CHAR_SEQUENCE_PREFIX);
this.inputType = inputType;
this.textField = textField;
setLanguage(LanguageCollection.getLanguage(LanguageKind.KOREAN));
initPredictions();
setSpecialCharacterConstants();
}
@ -68,7 +62,7 @@ class ModeCheonjiin extends InputMode {
*/
protected void setCustomSpecialCharacters() {
// special
KEY_CHARACTERS.add(TextTools.removeLettersFromList(applyPunctuationOrder(Characters.getSpecial(language), 0)));
KEY_CHARACTERS.add(getAbbreviatedSpecialChars());
KEY_CHARACTERS.get(0).add(0, "0");
// punctuation
@ -94,14 +88,6 @@ class ModeCheonjiin extends InputMode {
}
protected void setSpecialCharacterConstants() {
CUSTOM_EMOJI_SEQUENCE = SPECIAL_CHAR_SEQUENCE_PREFIX + EmojiLanguage.CUSTOM_EMOJI_SEQUENCE;
EMOJI_SEQUENCE = SPECIAL_CHAR_SEQUENCE_PREFIX + EmojiLanguage.EMOJI_SEQUENCE;
PUNCTUATION_SEQUENCE = SPECIAL_CHAR_SEQUENCE_PREFIX + NaturalLanguage.PUNCTUATION_KEY;
SPECIAL_CHAR_SEQUENCE = "000";
}
protected void initPredictions() {
predictions = new SyllablePredictions(settings);
predictions
@ -113,9 +99,9 @@ class ModeCheonjiin extends InputMode {
@Override
public boolean onBackspace() {
if (digitSequence.equals(PUNCTUATION_SEQUENCE)) {
if (digitSequence.equals(seq.PUNCTUATION_SEQUENCE)) {
digitSequence = "";
} else if (digitSequence.equals(SPECIAL_CHAR_SEQUENCE) || (!digitSequence.startsWith(PUNCTUATION_SEQUENCE) && Cheonjiin.isSingleJamo(digitSequence))) {
} else if (digitSequence.equals(seq.WHITESPACE_SEQUENCE) || (!digitSequence.startsWith(seq.PUNCTUATION_SEQUENCE) && Cheonjiin.isSingleJamo(digitSequence))) {
digitSequence = "";
} else if (!digitSequence.isEmpty()) {
digitSequence = digitSequence.substring(0, digitSequence.length() - 1);
@ -145,10 +131,10 @@ class ModeCheonjiin extends InputMode {
protected void onNumberHold(int number) {
if (number == 0) {
disablePredictions = false;
digitSequence = SPECIAL_CHAR_SEQUENCE;
digitSequence = seq.WHITESPACE_SEQUENCE;
} else if (number == 1) {
disablePredictions = false;
digitSequence = PUNCTUATION_SEQUENCE;
digitSequence = seq.PUNCTUATION_SEQUENCE;
} else {
autoAcceptTimeout = 0;
suggestions.add(language.getKeyNumeral(number));
@ -162,8 +148,8 @@ class ModeCheonjiin extends InputMode {
digitSequence = digitSequence.substring(0, digitSequence.length() - rewindAmount);
}
if (digitSequence.startsWith(PUNCTUATION_SEQUENCE)) {
digitSequence = SPECIAL_CHAR_SEQUENCE_PREFIX + EmojiLanguage.validateEmojiSequence(digitSequence.substring(SPECIAL_CHAR_SEQUENCE_PREFIX.length()), nextNumber);
if (digitSequence.startsWith(seq.PUNCTUATION_SEQUENCE)) {
digitSequence = EmojiLanguage.validateEmojiSequence(seq, digitSequence, nextNumber);
} else {
digitSequence += String.valueOf(nextNumber);
}
@ -175,8 +161,8 @@ class ModeCheonjiin extends InputMode {
final int repeatingDigits = digitSequence.length() > 1 && digitSequence.charAt(digitSequence.length() - 1) == nextChar ? Cheonjiin.getRepeatingEndingDigits(digitSequence) : 0;
final int keyCharsCount = nextNumber == 0 ? 2 : language.getKeyCharacters(nextNumber).size();
if (SPECIAL_CHAR_SEQUENCE.equals(digitSequence)) {
return SPECIAL_CHAR_SEQUENCE.length();
if (seq.WHITESPACE_SEQUENCE.equals(digitSequence)) {
return seq.WHITESPACE_SEQUENCE.length();
}
if (repeatingDigits == 0 || keyCharsCount < 2) {
@ -214,16 +200,16 @@ class ModeCheonjiin extends InputMode {
return;
}
String seq = digitSequence;
String currentSeq = digitSequence;
if (shouldDisplayCustomEmojis()) {
seq = digitSequence.substring(SPECIAL_CHAR_SEQUENCE_PREFIX.length());
currentSeq = digitSequence.substring(PUNCTUATION_SEQUENCE_PREFIX.length());
} else if (!previousJamoSequence.isEmpty()) {
seq = previousJamoSequence;
currentSeq = previousJamoSequence;
}
predictions
.setLanguage(shouldDisplayCustomEmojis() ? new EmojiLanguage() : language)
.setDigitSequence(seq)
.setLanguage(shouldDisplayCustomEmojis() ? new EmojiLanguage(seq) : language)
.setDigitSequence(currentSeq)
.load();
}
@ -231,7 +217,7 @@ class ModeCheonjiin extends InputMode {
protected boolean loadEmojis() {
if (shouldDisplayEmojis()) {
suggestions.clear();
suggestions.addAll(new EmojiLanguage().getKeyCharacters(digitSequence.charAt(digitSequence.length() - 1) - '0', getEmojiGroup()));
suggestions.addAll(new EmojiLanguage(seq).getKeyCharacters(digitSequence.charAt(digitSequence.length() - 1) - '0', getEmojiGroup()));
return true;
}
@ -240,17 +226,17 @@ class ModeCheonjiin extends InputMode {
protected int getEmojiGroup() {
return digitSequence.length() - EMOJI_SEQUENCE.length();
return digitSequence.length() - seq.EMOJI_SEQUENCE.length();
}
protected boolean shouldDisplayEmojis() {
return !isEmailMode && digitSequence.startsWith(EMOJI_SEQUENCE) && !digitSequence.equals(CUSTOM_EMOJI_SEQUENCE);
return !isEmailMode && digitSequence.startsWith(seq.EMOJI_SEQUENCE) && !digitSequence.equals(seq.CUSTOM_EMOJI_SEQUENCE);
}
protected boolean shouldDisplayCustomEmojis() {
return !isEmailMode && digitSequence.equals(CUSTOM_EMOJI_SEQUENCE);
return !isEmailMode && digitSequence.equals(seq.CUSTOM_EMOJI_SEQUENCE);
}
@ -285,7 +271,7 @@ class ModeCheonjiin extends InputMode {
protected boolean shouldDisplaySpecialCharacters() {
return digitSequence.equals(PUNCTUATION_SEQUENCE) || digitSequence.equals(SPECIAL_CHAR_SEQUENCE);
return digitSequence.equals(seq.PUNCTUATION_SEQUENCE) || digitSequence.equals(seq.WHITESPACE_SEQUENCE);
}
@ -295,8 +281,8 @@ class ModeCheonjiin extends InputMode {
*/
protected void onPredictions() {
// in case the user hasn't added any custom emoji, do not allow advancing to the empty character group
if (predictions.getList().isEmpty() && digitSequence.startsWith(EMOJI_SEQUENCE)) {
digitSequence = EMOJI_SEQUENCE;
if (predictions.getList().isEmpty() && digitSequence.startsWith(seq.EMOJI_SEQUENCE)) {
digitSequence = seq.EMOJI_SEQUENCE;
return;
}
@ -351,8 +337,8 @@ class ModeCheonjiin extends InputMode {
public boolean shouldAcceptPreviousSuggestion(int nextKey, boolean hold) {
return
(hold && !digitSequence.isEmpty())
|| (digitSequence.equals(SPECIAL_CHAR_SEQUENCE) && nextKey != 0)
|| (digitSequence.startsWith(PUNCTUATION_SEQUENCE) && nextKey != 1);
|| (digitSequence.equals(seq.WHITESPACE_SEQUENCE) && nextKey != 0)
|| (digitSequence.startsWith(seq.PUNCTUATION_SEQUENCE) && nextKey != 1);
}

View file

@ -14,7 +14,7 @@ public class ModeHiragana extends ModeKanji {
@Override
protected void initPredictions() {
predictions = new KanaPredictions(settings, textField, false);
predictions = new KanaPredictions(settings, textField, seq, false);
predictions.setWordsChangedHandler(this::onPredictions);
}

View file

@ -53,7 +53,7 @@ public class ModeIdeograms extends ModeWords {
@Override
protected void initPredictions() {
predictions = new IdeogramPredictions(settings, textField);
predictions = new IdeogramPredictions(settings, textField, seq);
predictions.setWordsChangedHandler(this::onPredictions);
}
@ -117,7 +117,7 @@ public class ModeIdeograms extends ModeWords {
boolean lastDigitBelongsToNewWord = preserveWords && initialLength >= 2;
try {
if (!digitSequence.equals(SPECIAL_CHAR_SEQUENCE) && !digitSequence.equals(PUNCTUATION_SEQUENCE)) {
if (!digitSequence.equals(seq.WHITESPACE_SEQUENCE) && !digitSequence.equals(seq.PUNCTUATION_SEQUENCE)) {
lastAcceptedWord = currentWord;
lastAcceptedSequence = lastDigitBelongsToNewWord ? digitSequence.substring(0, initialLength - 1) : digitSequence;
@ -143,9 +143,9 @@ public class ModeIdeograms extends ModeWords {
return
digitSequence.length() > 1
&& predictions.noDbWords()
&& !digitSequence.equals(EMOJI_SEQUENCE)
&& !digitSequence.equals(PUNCTUATION_SEQUENCE)
&& !digitSequence.equals(SPECIAL_CHAR_SEQUENCE);
&& !digitSequence.equals(seq.EMOJI_SEQUENCE)
&& !digitSequence.equals(seq.PUNCTUATION_SEQUENCE)
&& !digitSequence.equals(seq.WHITESPACE_SEQUENCE);
}
@ -164,8 +164,8 @@ public class ModeIdeograms extends ModeWords {
return
TextTools.containsOtherThan1(nextSequence)
&& (
nextSequence.endsWith(EMOJI_SEQUENCE) || nextSequence.startsWith(EMOJI_SEQUENCE) ||
nextSequence.endsWith(PUNCTUATION_SEQUENCE) || nextSequence.startsWith(PUNCTUATION_SEQUENCE)
nextSequence.endsWith(seq.EMOJI_SEQUENCE) || nextSequence.startsWith(seq.EMOJI_SEQUENCE) ||
nextSequence.endsWith(seq.PUNCTUATION_SEQUENCE) || nextSequence.startsWith(seq.PUNCTUATION_SEQUENCE)
);
}
@ -196,8 +196,8 @@ public class ModeIdeograms extends ModeWords {
* accept it.
*/
@Override
public boolean shouldReplacePreviousSuggestion() {
return isFiltering;
public boolean shouldReplacePreviousSuggestion(@Nullable String word) {
return isFiltering || super.shouldReplacePreviousSuggestion(word);
}
/********************************* FILTERING *********************************/

View file

@ -31,7 +31,7 @@ public class ModeKanji extends ModePinyin {
}
String nextSequence = digitSequence + (char)(nextKey + '0');
if (nextSequence.endsWith(PUNCTUATION_SEQUENCE) && !predictions.noDbWords()) {
if (nextSequence.endsWith(seq.PUNCTUATION_SEQUENCE) && !predictions.noDbWords()) {
return false;
}

View file

@ -14,7 +14,7 @@ public class ModeKatakana extends ModeHiragana {
@Override
protected void initPredictions() {
predictions = new KanaPredictions(settings, textField, true);
predictions = new KanaPredictions(settings, textField, seq, true);
predictions.setWordsChangedHandler(this::onPredictions);
}

View file

@ -4,14 +4,13 @@ import androidx.annotation.Nullable;
import io.github.sspanak.tt9.hacks.InputType;
import io.github.sspanak.tt9.ime.helpers.TextField;
import io.github.sspanak.tt9.ime.modes.helpers.Sequences;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageKind;
import io.github.sspanak.tt9.languages.NaturalLanguage;
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.util.chars.Characters;
public class ModePinyin extends ModeIdeograms {
private final int SPECIAL_CHAR_KEY = NaturalLanguage.SPECIAL_CHAR_KEY.charAt(0) - '0';
private boolean ignoreNextSpace = false;
@ -44,7 +43,7 @@ public class ModePinyin extends ModeIdeograms {
@Override
protected void onNumberPress(int number) {
if (ignoreNextSpace && number == SPECIAL_CHAR_KEY) {
if (ignoreNextSpace && number == Sequences.SPECIAL_CHAR_KEY) {
ignoreNextSpace = false;
return;
}
@ -66,7 +65,7 @@ public class ModePinyin extends ModeIdeograms {
// In East Asian languages, Space must accept the current word, or type a space when there is no word.
// Here, we handle the case when 0-key is Space, unlike the Space hotkey in HotkeyHandler,
// which could be a different key, assigned by the user.
if (!digitSequence.isEmpty() && !digitSequence.endsWith(SPECIAL_CHAR_SEQUENCE) && nextKey == SPECIAL_CHAR_KEY) {
if (!digitSequence.isEmpty() && !digitSequence.endsWith(seq.WHITESPACE_SEQUENCE) && nextKey == Sequences.SPECIAL_CHAR_KEY) {
ignoreNextSpace = true;
}

View file

@ -9,11 +9,11 @@ import java.util.List;
import io.github.sspanak.tt9.hacks.InputType;
import io.github.sspanak.tt9.ime.helpers.TextField;
import io.github.sspanak.tt9.ime.modes.helpers.AutoTextCase;
import io.github.sspanak.tt9.ime.modes.helpers.Sequences;
import io.github.sspanak.tt9.ime.modes.predictions.WordPredictions;
import io.github.sspanak.tt9.languages.EmojiLanguage;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageKind;
import io.github.sspanak.tt9.languages.NaturalLanguage;
import io.github.sspanak.tt9.languages.exceptions.InvalidLanguageCharactersException;
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.util.Logger;
@ -39,6 +39,7 @@ class ModeWords extends ModeCheonjiin {
super(settings, inputType, textField);
autoTextCase = new AutoTextCase(settings, inputType);
seq = new Sequences();
changeLanguage(lang);
defaultTextCase();
@ -46,20 +47,14 @@ class ModeWords extends ModeCheonjiin {
}
@Override protected void setCustomSpecialCharacters() {} // we use the default ones
protected void setSpecialCharacterConstants() {
PUNCTUATION_SEQUENCE = NaturalLanguage.PUNCTUATION_KEY;
EMOJI_SEQUENCE = EmojiLanguage.EMOJI_SEQUENCE;
CUSTOM_EMOJI_SEQUENCE = EmojiLanguage.CUSTOM_EMOJI_SEQUENCE;
SPECIAL_CHAR_SEQUENCE = NaturalLanguage.SPECIAL_CHAR_KEY;
@Override protected void setCustomSpecialCharacters() {
KEY_CHARACTERS.add(getAbbreviatedSpecialChars()); // special
}
@Override
protected void initPredictions() {
predictions = new WordPredictions(settings, textField);
predictions = new WordPredictions(settings, textField, seq);
predictions.setWordsChangedHandler(this::onPredictions);
}
@ -100,9 +95,9 @@ class ModeWords extends ModeCheonjiin {
@Override
protected void onNumberPress(int number) {
digitSequence = EmojiLanguage.validateEmojiSequence(digitSequence, number);
digitSequence = EmojiLanguage.validateEmojiSequence(seq, digitSequence, number);
if (digitSequence.equals(NaturalLanguage.PREFERRED_CHAR_SEQUENCE)) {
if (digitSequence.equals(seq.PREFERRED_CHAR_SEQUENCE)) {
autoAcceptTimeout = 0;
}
}
@ -286,13 +281,13 @@ class ModeWords extends ModeCheonjiin {
.setIsStemFuzzy(isStemFuzzy)
.setStem(stem)
.setDigitSequence(digitSequence)
.setLanguage(shouldDisplayCustomEmojis() ? new EmojiLanguage() : language)
.setLanguage(shouldDisplayCustomEmojis() ? new EmojiLanguage(seq) : language)
.load();
}
protected boolean loadPreferredChar() {
if (digitSequence.startsWith(NaturalLanguage.PREFERRED_CHAR_SEQUENCE)) {
if (digitSequence.startsWith(seq.PREFERRED_CHAR_SEQUENCE)) {
suggestions.clear();
suggestions.add(getPreferredChar());
return true;
@ -334,13 +329,13 @@ class ModeWords extends ModeCheonjiin {
try {
// special chars are not in the database, no need to run queries on them
String digitSequence = language.getDigitSequenceForWord(currentWord);
if (digitSequence.equals(SPECIAL_CHAR_SEQUENCE) || digitSequence.equals(PUNCTUATION_SEQUENCE)) {
String currentWordSeq = language.getDigitSequenceForWord(currentWord);
if (seq.isAnySpecialCharSequence(currentWordSeq)) {
return;
}
// increment the frequency of the given word
predictions.onAccept(currentWord, digitSequence);
predictions.onAccept(currentWord, currentWordSeq);
} catch (Exception e) {
Logger.e(LOG_TAG, "Failed incrementing priority of word: '" + currentWord + "'. " + e.getMessage());
}
@ -382,8 +377,7 @@ class ModeWords extends ModeCheonjiin {
changed = super.nextTextCase();
}
boolean onlySpecialChars = digitSequence.startsWith(PUNCTUATION_SEQUENCE) || digitSequence.startsWith(SPECIAL_CHAR_SEQUENCE) || digitSequence.startsWith(EMOJI_SEQUENCE);
if (onlySpecialChars && textCase == CASE_CAPITALIZE) {
if (seq.startsWithAnySpecialCharSequence(digitSequence) && textCase == CASE_CAPITALIZE) {
super.nextTextCase();
}
@ -411,20 +405,17 @@ class ModeWords extends ModeCheonjiin {
return true;
}
final char SPECIAL_CHAR_KEY_CODE = SPECIAL_CHAR_SEQUENCE.charAt(SPECIAL_CHAR_SEQUENCE.length() - 1);
final int SPECIAL_CHAR_KEY = SPECIAL_CHAR_KEY_CODE - '0';
// Prevent typing the preferred character when the user has scrolled the special char suggestions.
// For example, it makes more sense to allow typing "+ " with 0 + scroll + 0, instead of clearing
// the "+" and replacing it with the preferred character.
if (!stem.isEmpty() && nextKey == SPECIAL_CHAR_KEY && digitSequence.charAt(0) == SPECIAL_CHAR_KEY_CODE) {
if (!stem.isEmpty() && nextKey == Sequences.SPECIAL_CHAR_KEY && digitSequence.charAt(0) == Sequences.SPECIAL_CHAR_CODE) {
return true;
}
return
!digitSequence.isEmpty() && (
(nextKey == SPECIAL_CHAR_KEY && digitSequence.charAt(digitSequence.length() - 1) != SPECIAL_CHAR_KEY_CODE)
|| (nextKey != SPECIAL_CHAR_KEY && digitSequence.charAt(digitSequence.length() - 1) == SPECIAL_CHAR_KEY_CODE)
(nextKey == Sequences.SPECIAL_CHAR_KEY && digitSequence.charAt(digitSequence.length() - 1) != Sequences.SPECIAL_CHAR_CODE)
|| (nextKey != Sequences.SPECIAL_CHAR_KEY && digitSequence.charAt(digitSequence.length() - 1) == Sequences.SPECIAL_CHAR_CODE)
);
}
@ -448,8 +439,8 @@ class ModeWords extends ModeCheonjiin {
return
!digitSequence.isEmpty()
&& predictions.noDbWords()
&& digitSequence.contains(PUNCTUATION_SEQUENCE)
&& !digitSequence.startsWith(EMOJI_SEQUENCE)
&& digitSequence.contains(seq.PUNCTUATION_SEQUENCE)
&& !digitSequence.startsWith(seq.EMOJI_SEQUENCE)
&& Text.containsOtherThan1(digitSequence);
}

View file

@ -0,0 +1,70 @@
package io.github.sspanak.tt9.ime.modes.helpers;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class Sequences {
public static final int SPECIAL_CHAR_KEY = 0;
public static final int SPECIAL_CHAR_CODE = SPECIAL_CHAR_KEY + '0';
public static final int PUNCTUATION_KEY = 1;
public static final int CUSTOM_EMOJI_KEY = 3;
public final int PUNCTUATION_PREFIX_LENGTH;
@NonNull public final String PUNCTUATION_SEQUENCE;
@NonNull public final String EMOJI_SEQUENCE;
@NonNull public final String CUSTOM_EMOJI_SEQUENCE;
@NonNull public final String CURRENCY_SEQUENCE;
@NonNull public final String PREFERRED_CHAR_SEQUENCE;
@NonNull public final String SPECIAL_CHAR_SEQUENCE;
@NonNull public final String WHITESPACE_SEQUENCE;
public Sequences() {
this(null, null);
}
public Sequences(@Nullable String punctuationPrefix, @Nullable String specialCharPrefix) {
final String PUNCTUATION_PREFIX = punctuationPrefix != null ? punctuationPrefix : "";
final String SPECIAL_CHAR_PREFIX = specialCharPrefix != null ? specialCharPrefix : "";
PUNCTUATION_SEQUENCE = PUNCTUATION_PREFIX + PUNCTUATION_KEY;
PUNCTUATION_PREFIX_LENGTH = PUNCTUATION_PREFIX.length();
EMOJI_SEQUENCE = PUNCTUATION_SEQUENCE + PUNCTUATION_KEY;
CUSTOM_EMOJI_SEQUENCE = EMOJI_SEQUENCE + CUSTOM_EMOJI_KEY;
WHITESPACE_SEQUENCE = SPECIAL_CHAR_PREFIX + SPECIAL_CHAR_KEY;
PREFERRED_CHAR_SEQUENCE = WHITESPACE_SEQUENCE + SPECIAL_CHAR_KEY;
SPECIAL_CHAR_SEQUENCE = SPECIAL_CHAR_PREFIX + SPECIAL_CHAR_KEY + SPECIAL_CHAR_KEY + SPECIAL_CHAR_KEY;
CURRENCY_SEQUENCE = SPECIAL_CHAR_SEQUENCE + SPECIAL_CHAR_KEY;
}
public boolean isAnySpecialCharSequence(String sequence) {
if (sequence == null) {
return false;
}
return
sequence.equals(PUNCTUATION_SEQUENCE)
|| sequence.equals(WHITESPACE_SEQUENCE)
|| sequence.equals(EMOJI_SEQUENCE)
|| sequence.equals(PREFERRED_CHAR_SEQUENCE)
|| sequence.equals(SPECIAL_CHAR_SEQUENCE)
|| sequence.equals(CURRENCY_SEQUENCE);
}
public boolean startsWithAnySpecialCharSequence(String sequence) {
if (sequence == null) {
return false;
}
return
sequence.startsWith(PUNCTUATION_SEQUENCE)
|| sequence.startsWith(WHITESPACE_SEQUENCE)
|| sequence.startsWith(EMOJI_SEQUENCE)
|| sequence.startsWith(PREFERRED_CHAR_SEQUENCE)
|| sequence.startsWith(SPECIAL_CHAR_SEQUENCE)
|| sequence.startsWith(CURRENCY_SEQUENCE);
}
}

View file

@ -8,6 +8,7 @@ import java.util.HashSet;
import io.github.sspanak.tt9.db.DataStore;
import io.github.sspanak.tt9.ime.helpers.TextField;
import io.github.sspanak.tt9.ime.modes.helpers.Sequences;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.exceptions.InvalidLanguageCharactersException;
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
@ -20,8 +21,8 @@ public class IdeogramPredictions extends WordPredictions {
@NonNull protected ArrayList<String> lastTranscriptions = new ArrayList<>();
public IdeogramPredictions(SettingsStore settings, TextField textField) {
super(settings, textField);
public IdeogramPredictions(SettingsStore settings, TextField textField, Sequences sequences) {
super(settings, textField, sequences);
minWords = 1;
onlyExactMatches = true;

View file

@ -5,6 +5,7 @@ import androidx.annotation.NonNull;
import java.util.ArrayList;
import io.github.sspanak.tt9.ime.helpers.TextField;
import io.github.sspanak.tt9.ime.modes.helpers.Sequences;
import io.github.sspanak.tt9.languages.exceptions.InvalidLanguageCharactersException;
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
@ -13,8 +14,8 @@ public class KanaPredictions extends IdeogramPredictions {
@NonNull private final String STEM_PREFIX;
private final int STEM_PREFIX_LENGTH;
public KanaPredictions(SettingsStore settings, TextField textField, boolean isKatakana) {
super(settings, textField);
public KanaPredictions(SettingsStore settings, TextField textField, Sequences sequences, boolean isKatakana) {
super(settings, textField, sequences);
SEQUENCE_PREFIX = isKatakana ? '1' : '0';
STEM_PREFIX = isKatakana ? "Qk" : "Qh";

View file

@ -6,7 +6,7 @@ import java.util.ArrayList;
import io.github.sspanak.tt9.db.DataStore;
import io.github.sspanak.tt9.ime.helpers.TextField;
import io.github.sspanak.tt9.languages.EmojiLanguage;
import io.github.sspanak.tt9.ime.modes.helpers.Sequences;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageKind;
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
@ -17,6 +17,7 @@ import io.github.sspanak.tt9.util.chars.Characters;
public class WordPredictions extends Predictions {
protected final TextField textField;
private LocaleWordsSorter localeWordsSorter;
private final Sequences seq;
private String inputWord;
private boolean isStemFuzzy;
@ -25,11 +26,12 @@ public class WordPredictions extends Predictions {
protected String penultimateWord;
public WordPredictions(SettingsStore settings, TextField textField) {
public WordPredictions(SettingsStore settings, TextField textField, Sequences sequences) {
super(settings);
lastEnforcedTopWord = "";
localeWordsSorter = new LocaleWordsSorter(null);
penultimateWord = "";
seq = sequences;
stem = "";
this.textField = textField;
}
@ -92,7 +94,7 @@ public class WordPredictions extends Predictions {
@Override
protected boolean isRetryAllowed() {
return !EmojiLanguage.CUSTOM_EMOJI_SEQUENCE.equals(digitSequence);
return !seq.CUSTOM_EMOJI_SEQUENCE.equals(digitSequence);
}
/**
@ -113,7 +115,7 @@ public class WordPredictions extends Predictions {
}
words.clear();
if (digitSequence.equals(EmojiLanguage.CUSTOM_EMOJI_SEQUENCE)) {
if (digitSequence.equals(seq.CUSTOM_EMOJI_SEQUENCE)) {
words.addAll(dbWords);
} else {
suggestStem();

View file

@ -5,27 +5,31 @@ import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.Locale;
import io.github.sspanak.tt9.ime.modes.helpers.Sequences;
import io.github.sspanak.tt9.util.TextTools;
import io.github.sspanak.tt9.util.chars.Characters;
public class EmojiLanguage extends Language {
final public static String EMOJI_SEQUENCE = "11";
final private static int CUSTOM_EMOJI_KEY = 3;
final public static String CUSTOM_EMOJI_SEQUENCE = EMOJI_SEQUENCE + CUSTOM_EMOJI_KEY;
private final Sequences seq;
public EmojiLanguage() {
id = Integer.parseInt(EMOJI_SEQUENCE);
this(null);
}
public EmojiLanguage(Sequences sequences) {
id = Integer.parseInt(new Sequences().EMOJI_SEQUENCE); // always use the unprefixed sequence for ID
locale = Locale.ROOT;
abcString = "emoji";
code = "emj";
currency = "";
name = "Emoji";
seq = sequences == null ? new Sequences() : sequences;
}
@NonNull
@Override
public String getDigitSequenceForWord(String word) {
return TextTools.isGraphic(word) ? CUSTOM_EMOJI_SEQUENCE : "";
return isValidWord(word) ? seq.EMOJI_SEQUENCE : "";
}
@NonNull
@ -39,10 +43,10 @@ public class EmojiLanguage extends Language {
return TextTools.isGraphic(word);
}
public static String validateEmojiSequence(@NonNull String sequence, int next) {
if (sequence.startsWith(CUSTOM_EMOJI_SEQUENCE) || (sequence.equals(EMOJI_SEQUENCE) && next == CUSTOM_EMOJI_KEY)) {
return CUSTOM_EMOJI_SEQUENCE;
} else if (sequence.startsWith(EMOJI_SEQUENCE) && (next > 1 || sequence.length() == Characters.getMaxEmojiLevel() + 1)) {
public static String validateEmojiSequence(@NonNull Sequences seq, @NonNull String sequence, int next) {
if (sequence.startsWith(seq.CUSTOM_EMOJI_SEQUENCE) || (sequence.equals(seq.EMOJI_SEQUENCE) && next == Sequences.CUSTOM_EMOJI_KEY)) {
return seq.CUSTOM_EMOJI_SEQUENCE;
} else if (sequence.startsWith(seq.EMOJI_SEQUENCE) && (next > 1 || sequence.length() - seq.PUNCTUATION_PREFIX_LENGTH == Characters.getMaxEmojiLevel() + 1)) {
return sequence;
} else {
return sequence + next;

View file

@ -14,11 +14,6 @@ import io.github.sspanak.tt9.util.chars.Characters;
public class NaturalLanguage extends TranscribedLanguage {
final public static String SPECIAL_CHAR_KEY = "0";
final public static String PUNCTUATION_KEY = "1";
final public static String PREFERRED_CHAR_SEQUENCE = "00";
protected final ArrayList<ArrayList<String>> layout = new ArrayList<>();
private final HashMap<Character, String> characterKeyMap = new HashMap<>();
@NonNull private HashMap<Integer, String> numerals = new HashMap<>();
@ -86,7 +81,7 @@ public class NaturalLanguage extends TranscribedLanguage {
final String PUNCTUATION_PLACEHOLDER = "PUNCTUATION";
final Map<String, List<String>> specialChars = new HashMap<>();
specialChars.put(SPECIAL_CHARS_PLACEHOLDER, Characters.getSpecial(this));
specialChars.put(SPECIAL_CHARS_PLACEHOLDER, new ArrayList<>(Characters.Special));
specialChars.put(PUNCTUATION_PLACEHOLDER, Characters.PunctuationEnglish);
specialChars.put(PUNCTUATION_PLACEHOLDER + "_AR", Characters.PunctuationArabic);
specialChars.put(PUNCTUATION_PLACEHOLDER + "_BP", Characters.PunctuationChineseBopomofo);
@ -222,17 +217,7 @@ public class NaturalLanguage extends TranscribedLanguage {
return new ArrayList<>();
}
ArrayList<String> chars = layout.get(key);
if (key == 0) {
if (characterGroup > 1) {
chars = new ArrayList<>();
} else if (characterGroup == 1) {
chars = new ArrayList<>(Characters.Currency);
if (!currency.isEmpty()) chars.add(2, currency);
}
}
return chars;
return layout.get(key);
}

View file

@ -7,6 +7,7 @@ import androidx.annotation.NonNull;
import java.util.ArrayList;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.util.chars.Characters;
class SettingsPunctuation extends SettingsInput {
private final static String KEY_PREFIX_PUNCTUATION = "pref_punctuation_";
@ -27,7 +28,9 @@ class SettingsPunctuation extends SettingsInput {
public void saveSpecialChars(@NonNull Language language, @NonNull String specialChars) {
String safeChars = specialChars.replace("\n", "");
String safeChars = specialChars
.replace("\n", "")
.replace("\t", Characters.TAB);
prefsEditor.putString(KEY_PREFIX_SPECIAL + language.getId(), safeChars);
prefsEditor.apply();
}
@ -65,7 +68,7 @@ class SettingsPunctuation extends SettingsInput {
String safeChars = prefs.getString(KEY_PREFIX_SPECIAL + language.getId(), null);
return getCharsAsList(
safeChars == null ? null : safeChars.replace("", "\n"),
safeChars == null ? null : safeChars.replace("", "\n").replace(Characters.TAB, "\t"),
language.getKeyCharacters(0)
);
}

View file

@ -109,10 +109,10 @@ public class SoftKeyNumber2to9 extends SoftKeyNumber {
*/
private String abbreviateCharList(String chars, String abbreviationSign, Locale locale, boolean isUppercase) {
String firstLetter = chars.substring(0, 1);
firstLetter = TextTools.isCombining(firstLetter) ? Characters.COMBINING_ZERO_BASE + firstLetter : firstLetter;
firstLetter = TextTools.isCombining(firstLetter) ? Characters.PLACEHOLDER + firstLetter : firstLetter;
String lastLetter = chars.substring(chars.length() - 1);
lastLetter = TextTools.isCombining(lastLetter) ? Characters.COMBINING_ZERO_BASE + lastLetter : lastLetter;
lastLetter = TextTools.isCombining(lastLetter) ? Characters.PLACEHOLDER + lastLetter : lastLetter;
String list = firstLetter + abbreviationSign + lastLetter;
return isUppercase ? list.toUpperCase(locale) : list;

View file

@ -27,6 +27,9 @@ import io.github.sspanak.tt9.util.TextTools;
import io.github.sspanak.tt9.util.chars.Characters;
public class SuggestionsBar {
public static final String SHOW_SPECIAL_CHARS_SUGGESTION = "(@#*…)";
public static final String SHOW_CURRENCIES_SUGGESTION = "($€£…)";
private final String SHOW_MORE_SUGGESTION = "(...)";
private final String STEM_SUFFIX = "… +";
private final String STEM_VARIATION_PREFIX = "";
@ -140,15 +143,16 @@ public class SuggestionsBar {
@NonNull
public String get(int id) {
if (id < 0 || id >= visibleSuggestions.size()) {
return "";
String suggestion = getRaw(id);
// show more...
if (suggestion.equals(SHOW_MORE_SUGGESTION) || suggestion.equals(SHOW_CURRENCIES_SUGGESTION) || suggestion.equals(SHOW_SPECIAL_CHARS_SUGGESTION)) {
return Characters.PLACEHOLDER;
}
String suggestion = visibleSuggestions.get(id);
// single char
if (suggestion.equals(SHOW_MORE_SUGGESTION)) return Characters.COMBINING_ZERO_BASE;
if (suggestion.equals(Characters.NEW_LINE)) return "\n";
if (suggestion.equals(Characters.TAB)) return "\t";
suggestion = suggestion.replace(Characters.ZWNJ_GRAPHIC, Characters.ZWNJ);
suggestion = suggestion.replace(Characters.ZWJ_GRAPHIC, Characters.ZWJ);
@ -161,7 +165,7 @@ public class SuggestionsBar {
// "..." prefix
int startIndex = 0;
String[] prefixes = {STEM_VARIATION_PREFIX, STEM_PUNCTUATION_VARIATION_PREFIX, Characters.COMBINING_ZERO_BASE};
String[] prefixes = {STEM_VARIATION_PREFIX, STEM_PUNCTUATION_VARIATION_PREFIX, Characters.PLACEHOLDER};
for (String prefix : prefixes) {
int prefixIndex = suggestion.indexOf(prefix) + 1;
if (prefixIndex < endIndex) { // do not match the prefix chars when they are part of STEM_SUFFIX
@ -177,6 +181,16 @@ public class SuggestionsBar {
}
@NonNull
public String getRaw(int id) {
if (id < 0 || id >= visibleSuggestions.size()) {
return "";
}
return visibleSuggestions.get(id);
}
public void setRTL(boolean yes) {
if (mView != null) {
mView.setLayoutDirection(yes ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
@ -310,11 +324,12 @@ public class SuggestionsBar {
private String formatUnreadableSuggestion(String suggestion) {
if (TextTools.isCombining(suggestion)) {
return Characters.COMBINING_ZERO_BASE + suggestion;
return Characters.PLACEHOLDER + suggestion;
}
return switch (suggestion) {
case "\n" -> Characters.NEW_LINE;
case "\t" -> Characters.TAB;
case Characters.ZWJ -> Characters.ZWJ_GRAPHIC;
case Characters.ZWNJ -> Characters.ZWNJ_GRAPHIC;
default -> suggestion;

View file

@ -1,5 +1,7 @@
package io.github.sspanak.tt9.util.chars;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
@ -7,51 +9,79 @@ import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageKind;
public class Characters extends Emoji {
public static final String COMBINING_ZERO_BASE = "";
public static final String PLACEHOLDER = "";
public static final String IDEOGRAPHIC_SPACE = " ";
public static final String TAB = "";
final public static ArrayList<String> Currency = new ArrayList<>(Arrays.asList(
public static final ArrayList<String> Currency = new ArrayList<>(Arrays.asList(
"$", "", "", "¢", "¤", "", "¥", "£"
));
/**
* The English punctuation filtered to contain only valid email characters.
*/
final public static ArrayList<ArrayList<String>> Email = new ArrayList<>(Arrays.asList(
public static final ArrayList<ArrayList<String>> Email = new ArrayList<>(Arrays.asList(
new ArrayList<>(Arrays.asList("@", "_", " ", "#", "%", "{", "}", "|", "^", "/", "=", "*", "+")),
new ArrayList<>(Arrays.asList(".", "-", "&", "~", "`", "'", "!", "?"))
));
/**
* Special characters for phone number fields, including both characters for conveniently typing a phone number: "()-",
* as well as command characters such as "," = "slight pause" and ";" = "wait" used in Japan and some other countries.
*/
final public static ArrayList<ArrayList<String>> Phone = new ArrayList<>(Arrays.asList(
public static final ArrayList<ArrayList<String>> Phone = new ArrayList<>(Arrays.asList(
new ArrayList<>(Arrays.asList("+", " ")),
new ArrayList<>(Arrays.asList("-", "(", ")", ".", ";", ","))
));
/**
* Commonly used special and math characters.
*/
public static final ArrayList<String> Special = new ArrayList<>(Arrays.asList(
"@", "_", "#", "%", "[", "]", "{", "}", "§", "|", "^", "<", ">", "\\", "/", "=", "*", "+"
));
/**
* Returns a language-specific currency list.
*/
public static ArrayList<String> getCurrencies(@Nullable Language language) {
ArrayList<String> chars = new ArrayList<>(Characters.Currency);
if (language != null && !language.getCurrency().isEmpty()) {
chars.add(2, language.getCurrency());
}
return chars;
}
/**
* Returns the language-specific space character.
*/
public static String getSpace(Language language) {
public static String getSpace(@Nullable Language language) {
return LanguageKind.isChinese(language) || LanguageKind.isJapanese(language) ? IDEOGRAPHIC_SPACE : " ";
}
/**
* Standard special characters with automatic Space selection based on the language. Useful for
* text fields.
* Whitespace characters with language-specific Space. Useful for text fields.
*/
public static ArrayList<String> getSpecial(Language language) {
public static ArrayList<String> getWhitespaces(@Nullable Language language) {
return new ArrayList<>(Arrays.asList(
getSpace(language), "\n", "@", "_", "#", "%", "[", "]", "{", "}", "§", "|", "^", "<", ">", "\\", "/", "=", "*", "+"
getSpace(language), "\n", "\t"
));
}
/**
* Special characters for all kinds of numeric fields: integer, decimal with +/- included as necessary.
* Special and punctuation characters for all kinds of numeric fields: integer, decimal with +/-,
* included as necessary.
*/
public static ArrayList<ArrayList<String>> getSpecialForNumbers(boolean decimal, boolean signed) {
public static ArrayList<ArrayList<String>> getAllForDecimal(boolean decimal, boolean signed) {
ArrayList<ArrayList<String>> keyCharacters = new ArrayList<>();
keyCharacters.add(signed ? new ArrayList<>(Arrays.asList("-", "+")) : new ArrayList<>());
if (decimal) {
@ -60,14 +90,18 @@ public class Characters extends Emoji {
return keyCharacters;
}
public static boolean isCurrency(Language language, String c) {
return Currency.contains(c) || (language != null && language.getCurrency().equals(c));
}
public static boolean isFathatan(char ch) {
return ch == 0x064B;
}
public static boolean isOm(char ch) {
return
ch == 0x0950 // Devanagari