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

View file

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

View file

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

View file

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

View file

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

View file

@ -7,11 +7,14 @@ import java.util.ArrayList;
import io.github.sspanak.tt9.hacks.InputType; import io.github.sspanak.tt9.hacks.InputType;
import io.github.sspanak.tt9.ime.helpers.TextField; 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.Language;
import io.github.sspanak.tt9.languages.LanguageKind; import io.github.sspanak.tt9.languages.LanguageKind;
import io.github.sspanak.tt9.languages.NullLanguage; import io.github.sspanak.tt9.languages.NullLanguage;
import io.github.sspanak.tt9.preferences.settings.SettingsStore; 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.Logger;
import io.github.sspanak.tt9.util.chars.Characters;
abstract public class InputMode { abstract public class InputMode {
// typing mode // typing mode
@ -40,6 +43,7 @@ abstract public class InputMode {
@NonNull protected final ArrayList<String> suggestions = new ArrayList<>(); @NonNull protected final ArrayList<String> suggestions = new ArrayList<>();
@NonNull protected Runnable onSuggestionsUpdated = () -> {}; @NonNull protected Runnable onSuggestionsUpdated = () -> {};
protected int specialCharSelectedGroup = 0; protected int specialCharSelectedGroup = 0;
@NonNull protected Sequences seq = new Sequences();
protected InputMode(SettingsStore settings, InputType inputType) { 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) { onAcceptSuggestion(word, false); }
public void onAcceptSuggestion(@NonNull String word, boolean preserveWordList) {} public void onAcceptSuggestion(@NonNull String word, boolean preserveWordList) {}
public void onCursorMove(@NonNull String word) { if (!digitSequence.isEmpty()) onAcceptSuggestion(word); } 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 * loadSuggestions
@ -140,7 +152,7 @@ abstract public class InputMode {
// Interaction with the IME. Return "true" if it should perform the respective action. // Interaction with the IME. Return "true" if it should perform the respective action.
public boolean shouldAcceptPreviousSuggestion(String unacceptedText) { return false; } public boolean shouldAcceptPreviousSuggestion(String unacceptedText) { return false; }
public boolean shouldAcceptPreviousSuggestion(int nextKey, boolean hold) { 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 shouldAddTrailingSpace(boolean isWordAcceptedManually, int nextKey) { return false; }
public boolean shouldAddPrecedingSpace() { return false; } public boolean shouldAddPrecedingSpace() { return false; }
public boolean shouldDeletePrecedingSpace() { 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 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() { protected boolean loadSpecialCharacters() {
int key = digitSequence.charAt(0) - '0'; int key = digitSequence.charAt(0) - '0';
ArrayList<String> chars = settings.getOrderedKeyChars(language, key, specialCharSelectedGroup); ArrayList<String> chars = settings.getOrderedKeyChars(language, key, specialCharSelectedGroup);
@ -218,14 +238,22 @@ abstract public class InputMode {
return new ArrayList<>(unordered); 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<>(); ArrayList<String> ordered = new ArrayList<>();
if (unordered.isEmpty() || order == null || order.isEmpty()) {
return ordered;
}
if (isEmailMode) { if (isEmailMode) {
if (unordered.contains("@")) ordered.add("@"); if (unordered.contains("@")) ordered.add("@");
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) == '_')) { if (isEmailMode && (ch.charAt(0) == '@' || ch.charAt(0) == '_')) {
continue; continue;
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -14,7 +14,7 @@ public class ModeKatakana extends ModeHiragana {
@Override @Override
protected void initPredictions() { protected void initPredictions() {
predictions = new KanaPredictions(settings, textField, true); predictions = new KanaPredictions(settings, textField, seq, true);
predictions.setWordsChangedHandler(this::onPredictions); 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.hacks.InputType;
import io.github.sspanak.tt9.ime.helpers.TextField; 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.Language;
import io.github.sspanak.tt9.languages.LanguageKind; 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.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.util.chars.Characters; import io.github.sspanak.tt9.util.chars.Characters;
public class ModePinyin extends ModeIdeograms { public class ModePinyin extends ModeIdeograms {
private final int SPECIAL_CHAR_KEY = NaturalLanguage.SPECIAL_CHAR_KEY.charAt(0) - '0';
private boolean ignoreNextSpace = false; private boolean ignoreNextSpace = false;
@ -44,7 +43,7 @@ public class ModePinyin extends ModeIdeograms {
@Override @Override
protected void onNumberPress(int number) { protected void onNumberPress(int number) {
if (ignoreNextSpace && number == SPECIAL_CHAR_KEY) { if (ignoreNextSpace && number == Sequences.SPECIAL_CHAR_KEY) {
ignoreNextSpace = false; ignoreNextSpace = false;
return; 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. // 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, // 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. // 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; ignoreNextSpace = true;
} }

View file

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

View file

@ -5,6 +5,7 @@ import androidx.annotation.NonNull;
import java.util.ArrayList; import java.util.ArrayList;
import io.github.sspanak.tt9.ime.helpers.TextField; 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.languages.exceptions.InvalidLanguageCharactersException;
import io.github.sspanak.tt9.preferences.settings.SettingsStore; import io.github.sspanak.tt9.preferences.settings.SettingsStore;
@ -13,8 +14,8 @@ public class KanaPredictions extends IdeogramPredictions {
@NonNull private final String STEM_PREFIX; @NonNull private final String STEM_PREFIX;
private final int STEM_PREFIX_LENGTH; private final int STEM_PREFIX_LENGTH;
public KanaPredictions(SettingsStore settings, TextField textField, boolean isKatakana) { public KanaPredictions(SettingsStore settings, TextField textField, Sequences sequences, boolean isKatakana) {
super(settings, textField); super(settings, textField, sequences);
SEQUENCE_PREFIX = isKatakana ? '1' : '0'; SEQUENCE_PREFIX = isKatakana ? '1' : '0';
STEM_PREFIX = isKatakana ? "Qk" : "Qh"; 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.db.DataStore;
import io.github.sspanak.tt9.ime.helpers.TextField; 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.Language;
import io.github.sspanak.tt9.languages.LanguageKind; import io.github.sspanak.tt9.languages.LanguageKind;
import io.github.sspanak.tt9.preferences.settings.SettingsStore; 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 { public class WordPredictions extends Predictions {
protected final TextField textField; protected final TextField textField;
private LocaleWordsSorter localeWordsSorter; private LocaleWordsSorter localeWordsSorter;
private final Sequences seq;
private String inputWord; private String inputWord;
private boolean isStemFuzzy; private boolean isStemFuzzy;
@ -25,11 +26,12 @@ public class WordPredictions extends Predictions {
protected String penultimateWord; protected String penultimateWord;
public WordPredictions(SettingsStore settings, TextField textField) { public WordPredictions(SettingsStore settings, TextField textField, Sequences sequences) {
super(settings); super(settings);
lastEnforcedTopWord = ""; lastEnforcedTopWord = "";
localeWordsSorter = new LocaleWordsSorter(null); localeWordsSorter = new LocaleWordsSorter(null);
penultimateWord = ""; penultimateWord = "";
seq = sequences;
stem = ""; stem = "";
this.textField = textField; this.textField = textField;
} }
@ -92,7 +94,7 @@ public class WordPredictions extends Predictions {
@Override @Override
protected boolean isRetryAllowed() { 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(); words.clear();
if (digitSequence.equals(EmojiLanguage.CUSTOM_EMOJI_SEQUENCE)) { if (digitSequence.equals(seq.CUSTOM_EMOJI_SEQUENCE)) {
words.addAll(dbWords); words.addAll(dbWords);
} else { } else {
suggestStem(); suggestStem();

View file

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

View file

@ -14,11 +14,6 @@ import io.github.sspanak.tt9.util.chars.Characters;
public class NaturalLanguage extends TranscribedLanguage { 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<>(); protected final ArrayList<ArrayList<String>> layout = new ArrayList<>();
private final HashMap<Character, String> characterKeyMap = new HashMap<>(); private final HashMap<Character, String> characterKeyMap = new HashMap<>();
@NonNull private HashMap<Integer, String> numerals = 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 String PUNCTUATION_PLACEHOLDER = "PUNCTUATION";
final Map<String, List<String>> specialChars = new HashMap<>(); 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, Characters.PunctuationEnglish);
specialChars.put(PUNCTUATION_PLACEHOLDER + "_AR", Characters.PunctuationArabic); specialChars.put(PUNCTUATION_PLACEHOLDER + "_AR", Characters.PunctuationArabic);
specialChars.put(PUNCTUATION_PLACEHOLDER + "_BP", Characters.PunctuationChineseBopomofo); specialChars.put(PUNCTUATION_PLACEHOLDER + "_BP", Characters.PunctuationChineseBopomofo);
@ -222,17 +217,7 @@ public class NaturalLanguage extends TranscribedLanguage {
return new ArrayList<>(); return new ArrayList<>();
} }
ArrayList<String> chars = layout.get(key); return 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;
} }

View file

@ -7,6 +7,7 @@ import androidx.annotation.NonNull;
import java.util.ArrayList; import java.util.ArrayList;
import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.util.chars.Characters;
class SettingsPunctuation extends SettingsInput { class SettingsPunctuation extends SettingsInput {
private final static String KEY_PREFIX_PUNCTUATION = "pref_punctuation_"; 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) { 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.putString(KEY_PREFIX_SPECIAL + language.getId(), safeChars);
prefsEditor.apply(); prefsEditor.apply();
} }
@ -65,7 +68,7 @@ class SettingsPunctuation extends SettingsInput {
String safeChars = prefs.getString(KEY_PREFIX_SPECIAL + language.getId(), null); String safeChars = prefs.getString(KEY_PREFIX_SPECIAL + language.getId(), null);
return getCharsAsList( return getCharsAsList(
safeChars == null ? null : safeChars.replace("", "\n"), safeChars == null ? null : safeChars.replace("", "\n").replace(Characters.TAB, "\t"),
language.getKeyCharacters(0) 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) { private String abbreviateCharList(String chars, String abbreviationSign, Locale locale, boolean isUppercase) {
String firstLetter = chars.substring(0, 1); 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); 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; String list = firstLetter + abbreviationSign + lastLetter;
return isUppercase ? list.toUpperCase(locale) : list; 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; import io.github.sspanak.tt9.util.chars.Characters;
public class SuggestionsBar { 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 SHOW_MORE_SUGGESTION = "(...)";
private final String STEM_SUFFIX = "… +"; private final String STEM_SUFFIX = "… +";
private final String STEM_VARIATION_PREFIX = ""; private final String STEM_VARIATION_PREFIX = "";
@ -140,15 +143,16 @@ public class SuggestionsBar {
@NonNull @NonNull
public String get(int id) { public String get(int id) {
if (id < 0 || id >= visibleSuggestions.size()) { String suggestion = getRaw(id);
return "";
// 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 // 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.NEW_LINE)) return "\n";
if (suggestion.equals(Characters.TAB)) return "\t";
suggestion = suggestion.replace(Characters.ZWNJ_GRAPHIC, Characters.ZWNJ); suggestion = suggestion.replace(Characters.ZWNJ_GRAPHIC, Characters.ZWNJ);
suggestion = suggestion.replace(Characters.ZWJ_GRAPHIC, Characters.ZWJ); suggestion = suggestion.replace(Characters.ZWJ_GRAPHIC, Characters.ZWJ);
@ -161,7 +165,7 @@ public class SuggestionsBar {
// "..." prefix // "..." prefix
int startIndex = 0; 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) { for (String prefix : prefixes) {
int prefixIndex = suggestion.indexOf(prefix) + 1; int prefixIndex = suggestion.indexOf(prefix) + 1;
if (prefixIndex < endIndex) { // do not match the prefix chars when they are part of STEM_SUFFIX 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) { public void setRTL(boolean yes) {
if (mView != null) { if (mView != null) {
mView.setLayoutDirection(yes ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); mView.setLayoutDirection(yes ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
@ -310,11 +324,12 @@ public class SuggestionsBar {
private String formatUnreadableSuggestion(String suggestion) { private String formatUnreadableSuggestion(String suggestion) {
if (TextTools.isCombining(suggestion)) { if (TextTools.isCombining(suggestion)) {
return Characters.COMBINING_ZERO_BASE + suggestion; return Characters.PLACEHOLDER + suggestion;
} }
return switch (suggestion) { return switch (suggestion) {
case "\n" -> Characters.NEW_LINE; case "\n" -> Characters.NEW_LINE;
case "\t" -> Characters.TAB;
case Characters.ZWJ -> Characters.ZWJ_GRAPHIC; case Characters.ZWJ -> Characters.ZWJ_GRAPHIC;
case Characters.ZWNJ -> Characters.ZWNJ_GRAPHIC; case Characters.ZWNJ -> Characters.ZWNJ_GRAPHIC;
default -> suggestion; default -> suggestion;

View file

@ -1,5 +1,7 @@
package io.github.sspanak.tt9.util.chars; package io.github.sspanak.tt9.util.chars;
import androidx.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -7,51 +9,79 @@ import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageKind; import io.github.sspanak.tt9.languages.LanguageKind;
public class Characters extends Emoji { 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 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. * 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("@", "_", " ", "#", "%", "{", "}", "|", "^", "/", "=", "*", "+")),
new ArrayList<>(Arrays.asList(".", "-", "&", "~", "`", "'", "!", "?")) new ArrayList<>(Arrays.asList(".", "-", "&", "~", "`", "'", "!", "?"))
)); ));
/** /**
* Special characters for phone number fields, including both characters for conveniently typing a phone number: "()-", * 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. * 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("+", " ")),
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. * 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 : " "; return LanguageKind.isChinese(language) || LanguageKind.isJapanese(language) ? IDEOGRAPHIC_SPACE : " ";
} }
/** /**
* Standard special characters with automatic Space selection based on the language. Useful for * Whitespace characters with language-specific Space. Useful for text fields.
* text fields.
*/ */
public static ArrayList<String> getSpecial(Language language) { public static ArrayList<String> getWhitespaces(@Nullable Language language) {
return new ArrayList<>(Arrays.asList( 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<>(); ArrayList<ArrayList<String>> keyCharacters = new ArrayList<>();
keyCharacters.add(signed ? new ArrayList<>(Arrays.asList("-", "+")) : new ArrayList<>()); keyCharacters.add(signed ? new ArrayList<>(Arrays.asList("-", "+")) : new ArrayList<>());
if (decimal) { if (decimal) {
@ -60,14 +90,18 @@ public class Characters extends Emoji {
return keyCharacters; return keyCharacters;
} }
public static boolean isCurrency(Language language, String c) { public static boolean isCurrency(Language language, String c) {
return Currency.contains(c) || (language != null && language.getCurrency().equals(c)); return Currency.contains(c) || (language != null && language.getCurrency().equals(c));
} }
public static boolean isFathatan(char ch) { public static boolean isFathatan(char ch) {
return ch == 0x064B; return ch == 0x064B;
} }
public static boolean isOm(char ch) { public static boolean isOm(char ch) {
return return
ch == 0x0950 // Devanagari ch == 0x0950 // Devanagari