diff --git a/app/src/main/java/io/github/sspanak/tt9/db/entities/CustomWordFile.java b/app/src/main/java/io/github/sspanak/tt9/db/entities/CustomWordFile.java index 8f9a2508..af1d513e 100644 --- a/app/src/main/java/io/github/sspanak/tt9/db/entities/CustomWordFile.java +++ b/app/src/main/java/io/github/sspanak/tt9/db/entities/CustomWordFile.java @@ -75,11 +75,7 @@ public class CustomWordFile { return null; } - try { - return LanguageCollection.getLanguage(context, Integer.parseInt(parts[1])); - } catch (NumberFormatException e) { - return null; - } + return LanguageCollection.getLanguage(context, parts[1]); } @NonNull public static String getWord(String line) { diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java index d31dce84..315a46cc 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java @@ -32,11 +32,17 @@ abstract public class InputMode { protected int autoAcceptTimeout = -1; @NonNull protected String digitSequence = ""; protected Language language; + protected final SettingsStore settings; protected final ArrayList suggestions = new ArrayList<>(); @NonNull protected Runnable onSuggestionsUpdated = () -> {}; protected int specialCharSelectedGroup = 0; + protected InputMode(SettingsStore settings) { + this.settings = settings; + } + + public static InputMode getInstance(SettingsStore settings, Language language, InputType inputType, TextField textField, int mode) { switch (mode) { case MODE_PREDICTIVE: @@ -44,11 +50,11 @@ abstract public class InputMode { case MODE_ABC: return new ModeABC(settings, inputType, language); case MODE_PASSTHROUGH: - return new ModePassthrough(); + return new ModePassthrough(settings); default: Logger.w("InputMode", "Defaulting to mode: " + Mode123.class.getName() + " for unknown InputMode: " + mode); case MODE_123: - return new Mode123(inputType, language); + return new Mode123(settings, inputType, language); } } @@ -180,14 +186,14 @@ abstract public class InputMode { } int key = digitSequence.charAt(0) - '0'; - ArrayList chars = altLanguage.getKeyCharacters(key, specialCharSelectedGroup); + ArrayList chars = settings.getOrderedKeyChars(altLanguage, key, specialCharSelectedGroup); if (chars.isEmpty() && specialCharSelectedGroup == 1) { specialCharSelectedGroup = 0; return false; } else if (chars.isEmpty()) { specialCharSelectedGroup = 0; - chars = altLanguage.getKeyCharacters(key, specialCharSelectedGroup); + chars = settings.getOrderedKeyChars(altLanguage, key, specialCharSelectedGroup); } suggestions.clear(); diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/Mode123.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/Mode123.java index 0ab070a5..48ee7668 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/Mode123.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/Mode123.java @@ -8,6 +8,7 @@ import java.util.Collections; import io.github.sspanak.tt9.hacks.InputType; import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.NaturalLanguage; +import io.github.sspanak.tt9.preferences.settings.SettingsStore; import io.github.sspanak.tt9.util.Characters; public class Mode123 extends ModePassthrough { @@ -23,8 +24,10 @@ public class Mode123 extends ModePassthrough { private final boolean isEmailMode; - public Mode123(InputType inputType, Language language) { - this.language = language; + public Mode123(SettingsStore settings, InputType inputType, Language language) { + super(settings); + changeLanguage(language); + isEmailMode = inputType.isEmail(); if (inputType.isPhoneNumber()) { @@ -55,7 +58,7 @@ public class Mode123 extends ModePassthrough { private void setDefaultSpecialCharacters() { // 0-key KEY_CHARACTERS.add(new ArrayList<>(Collections.singletonList("+"))); - for (String character : Characters.Special) { + for (String character : settings.getOrderedKeyChars(language, 0)) { if (!character.equals("+") && !character.equals("\n")) { KEY_CHARACTERS.get(0).add(character); } @@ -63,7 +66,7 @@ public class Mode123 extends ModePassthrough { // 1-key KEY_CHARACTERS.add(new ArrayList<>(Collections.singletonList("."))); - for (String character : Characters.PunctuationEnglish) { + for (String character : settings.getOrderedKeyChars(language, 1)) { if (!character.equals(".")) { KEY_CHARACTERS.get(1).add(character); } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeABC.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeABC.java index 44711213..1a672d47 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeABC.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeABC.java @@ -14,13 +14,12 @@ import io.github.sspanak.tt9.util.Characters; public class ModeABC extends InputMode { private final ArrayList> KEY_CHARACTERS = new ArrayList<>(); - private final SettingsStore settings; private boolean shouldSelectNextLetter = false; @Override public int getId() { return MODE_ABC; } ModeABC(SettingsStore settings, InputType inputType, Language lang) { - this.settings = settings; + super(settings); changeLanguage(lang); if (inputType.isEmail()) { @@ -55,7 +54,7 @@ public class ModeABC extends InputMode { autoAcceptTimeout = settings.getAbcAutoAcceptTimeout(); digitSequence = String.valueOf(number); shouldSelectNextLetter = false; - suggestions.addAll(KEY_CHARACTERS.size() > number ? KEY_CHARACTERS.get(number) : language.getKeyCharacters(number)); + suggestions.addAll(KEY_CHARACTERS.size() > number ? KEY_CHARACTERS.get(number) : settings.getOrderedKeyChars(language, number)); suggestions.add(language.getKeyNumber(number)); } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePassthrough.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePassthrough.java index b97ac74d..d5c961b6 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePassthrough.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePassthrough.java @@ -2,9 +2,12 @@ package io.github.sspanak.tt9.ime.modes; import androidx.annotation.NonNull; +import io.github.sspanak.tt9.preferences.settings.SettingsStore; + // see: InputType.isSpecialNumeric() public class ModePassthrough extends InputMode { - ModePassthrough() { + ModePassthrough(SettingsStore settings) { + super(settings); reset(); allowedTextCases.add(CASE_LOWER); } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePredictive.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePredictive.java index 28740e23..15d488a0 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePredictive.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePredictive.java @@ -25,8 +25,6 @@ public class ModePredictive extends InputMode { private final ArrayList> KEY_CHARACTERS = new ArrayList<>(); - private final SettingsStore settings; - public int getId() { return MODE_PREDICTIVE; } private String lastAcceptedWord = ""; @@ -46,11 +44,12 @@ public class ModePredictive extends InputMode { ModePredictive(SettingsStore settings, InputType inputType, TextField textField, Language lang) { + super(settings); + autoSpace = new AutoSpace(settings).setLanguage(lang); autoTextCase = new AutoTextCase(settings); digitSequence = ""; predictions = new Predictions(settings, textField); - this.settings = settings; if (inputType.isEmail()) { KEY_CHARACTERS.add(new ArrayList<>(Characters.Email.get(0))); diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/Predictions.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/Predictions.java index 1036fe30..cec9eac2 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/Predictions.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/Predictions.java @@ -204,7 +204,7 @@ public class Predictions { // append all letters for the last digit in the sequence (the last pressed key) int lastSequenceDigit = digitSequence.charAt(digitSequence.length() - 1) - '0'; - for (String keyLetter : language.getKeyCharacters(lastSequenceDigit)) { + for (String keyLetter : settings.getOrderedKeyChars(language, lastSequenceDigit)) { if (Character.isAlphabetic(keyLetter.charAt(0)) || Characters.isCombiningPunctuation(language, keyLetter.charAt(0))) { generatedWords.add(baseWord + keyLetter); } diff --git a/app/src/main/java/io/github/sspanak/tt9/languages/LanguageCollection.java b/app/src/main/java/io/github/sspanak/tt9/languages/LanguageCollection.java index 21640a59..7f399841 100644 --- a/app/src/main/java/io/github/sspanak/tt9/languages/LanguageCollection.java +++ b/app/src/main/java/io/github/sspanak/tt9/languages/LanguageCollection.java @@ -38,6 +38,17 @@ public class LanguageCollection { return self; } + + @Nullable + public static NaturalLanguage getLanguage(Context context, String langId) { + try { + return getLanguage(context, Integer.parseInt(langId)); + } catch (NumberFormatException e) { + return null; + } + } + + @Nullable public static NaturalLanguage getLanguage(Context context, int langId) { if (getInstance(context).languages.containsKey(langId)) { diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/PreferencesActivity.java b/app/src/main/java/io/github/sspanak/tt9/preferences/PreferencesActivity.java index 526af702..ed8a3856 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/PreferencesActivity.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/PreferencesActivity.java @@ -30,6 +30,7 @@ import io.github.sspanak.tt9.preferences.screens.languages.LanguagesScreen; import io.github.sspanak.tt9.preferences.screens.main.MainSettingsScreen; import io.github.sspanak.tt9.preferences.screens.modeAbc.ModeAbcScreen; import io.github.sspanak.tt9.preferences.screens.modePredictive.ModePredictiveScreen; +import io.github.sspanak.tt9.preferences.screens.punctuation.PunctuationScreen; import io.github.sspanak.tt9.preferences.screens.setup.SetupScreen; import io.github.sspanak.tt9.ui.ActivityWithNavigation; import io.github.sspanak.tt9.util.Logger; @@ -89,10 +90,17 @@ public class PreferencesActivity extends ActivityWithNavigation implements Prefe @Override public void onBackPressed() { + Fragment previousFragment = getSupportFragmentManager().findFragmentById(R.id.preferences_container); + if (previousFragment instanceof BaseScreenFragment) { + ((BaseScreenFragment) previousFragment).onBackPressed(); + } + super.onBackPressed(); - Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.preferences_container); - if (fragment instanceof BaseScreenFragment) { - getOptionsCount = ((BaseScreenFragment) fragment)::getPreferenceCount; + + Fragment nextFragment = getSupportFragmentManager().findFragmentById(R.id.preferences_container); + if (nextFragment instanceof BaseScreenFragment) { + ((BaseScreenFragment) nextFragment).onBackPressed(); + getOptionsCount = ((BaseScreenFragment) nextFragment)::getPreferenceCount; } } @@ -152,6 +160,8 @@ public class PreferencesActivity extends ActivityWithNavigation implements Prefe return new ModePredictiveScreen(this); case ModeAbcScreen.NAME: return new ModeAbcScreen(this); + case PunctuationScreen.NAME: + return new PunctuationScreen(this); case SetupScreen.NAME: return new SetupScreen(this); case UsageStatsScreen.NAME: diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemDropDown.java b/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemDropDown.java index 9c0cfb75..873fe791 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemDropDown.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemDropDown.java @@ -1,5 +1,6 @@ package io.github.sspanak.tt9.preferences.items; +import androidx.annotation.Nullable; import androidx.preference.DropDownPreference; import androidx.preference.Preference; @@ -80,4 +81,8 @@ abstract public class ItemDropDown { } return this; } + + @Nullable public String getValue() { + return item != null ? item.getValue() : null; + } } diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemTextInput.java b/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemTextInput.java index 82b4a614..96684b6b 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemTextInput.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemTextInput.java @@ -21,6 +21,7 @@ import io.github.sspanak.tt9.util.Logger; abstract public class ItemTextInput extends ScreenPreference implements TextWatcher { @NonNull private final Handler debouncer = new Handler(Looper.getMainLooper()); + private EditText editText; public ItemTextInput(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); @@ -45,6 +46,7 @@ abstract public class ItemTextInput extends ScreenPreference implements TextWatc if (editText == null) { Logger.e(getClass().getSimpleName(), "Cannot attach a text change listener. Unable to find the EditText element."); } else { + this.editText = editText; editText.addTextChangedListener(this); editText.setOnKeyListener(this::ignoreEnter); } @@ -59,7 +61,17 @@ abstract public class ItemTextInput extends ScreenPreference implements TextWatc @Override public void afterTextChanged(Editable s) { debouncer.removeCallbacksAndMessages(null); - debouncer.postDelayed(() -> onChange(s.toString()), SettingsStore.TEXT_INPUT_DEBOUNCE_TIME); + debouncer.postDelayed(() -> onChange(s.toString()), getChangeHandlerDebounceTime()); + } + + protected int getChangeHandlerDebounceTime() { + return SettingsStore.TEXT_INPUT_DEBOUNCE_TIME; + } + + protected void setText(CharSequence text) { + if (editText != null) { + editText.setText(text); + } } /** diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/BaseScreenFragment.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/BaseScreenFragment.java index 900ad20a..2d4a37a7 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/BaseScreenFragment.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/BaseScreenFragment.java @@ -96,4 +96,6 @@ abstract public class BaseScreenFragment extends PreferenceFragmentCompat { abstract protected int getTitle(); abstract protected int getXml(); abstract protected void onCreate(); + + public void onBackPressed() {} } diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/AbstractPreferenceCharList.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/AbstractPreferenceCharList.java new file mode 100644 index 00000000..19d944a7 --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/AbstractPreferenceCharList.java @@ -0,0 +1,93 @@ +package io.github.sspanak.tt9.preferences.screens.punctuation; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.PreferenceViewHolder; + +import io.github.sspanak.tt9.R; +import io.github.sspanak.tt9.languages.Language; +import io.github.sspanak.tt9.preferences.items.ItemTextInput; +import io.github.sspanak.tt9.preferences.settings.SettingsStore; + +abstract class AbstractPreferenceCharList extends ItemTextInput { + @NonNull protected String currentChars = ""; + protected Language language; + private Runnable onRender; + protected static SettingsStore settings; + + + AbstractPreferenceCharList(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } + AbstractPreferenceCharList(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } + AbstractPreferenceCharList(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } + AbstractPreferenceCharList(@NonNull Context context) { super(context); } + + + @Override + public void onBindViewHolder(@NonNull PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + if (holder.itemView.findViewById(R.id.input_text_input_field) != null && onRender != null) { + onRender.run(); + } + } + + + @Override + protected int getChangeHandlerDebounceTime() { + return SettingsStore.TEXT_INPUT_PUNCTUATION_ORDER_DEBOUNCE_TIME; + } + + protected SettingsStore getSettings() { + if (settings == null) { + settings = new SettingsStore(getContext()); + } + return settings; + } + + + @Override + protected void onChange(String word) { + currentChars = word == null ? "" : word; + validateCurrentChars(); + } + + + void onLanguageChange(Language language) { + this.language = language; + + String all = getChars(); + char[] mandatory = getMandatoryChars(); + StringBuilder optional = new StringBuilder(); + + for (int i = 0; i < all.length(); i++) { + char c = all.charAt(i); + + boolean isMandatory = false; + for (char m : mandatory) { + if (m == c) { + isMandatory = true; + break; + } + } + + if (!isMandatory) { + optional.append(c); + } + } + + setText(optional.toString()); + } + + + void setOnRender(Runnable onRender) { + this.onRender = onRender; + } + + + @NonNull abstract protected String getChars(); + @NonNull abstract protected char[] getMandatoryChars(); + abstract public boolean validateCurrentChars(); + abstract public void saveCurrentChars(); +} diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/ItemPunctuationOrderLanguage.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/ItemPunctuationOrderLanguage.java new file mode 100644 index 00000000..622360b3 --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/ItemPunctuationOrderLanguage.java @@ -0,0 +1,68 @@ +package io.github.sspanak.tt9.preferences.screens.punctuation; + +import androidx.preference.DropDownPreference; +import androidx.preference.Preference; + +import java.util.ArrayList; +import java.util.LinkedHashMap; + +import io.github.sspanak.tt9.languages.Language; +import io.github.sspanak.tt9.languages.LanguageCollection; +import io.github.sspanak.tt9.preferences.items.ItemDropDown; +import io.github.sspanak.tt9.preferences.settings.SettingsStore; +import io.github.sspanak.tt9.util.ConsumerCompat; + +class ItemPunctuationOrderLanguage extends ItemDropDown { + public static final String NAME = "punctuation_order_language"; + + private ConsumerCompat onChangeCallback; + private final SettingsStore settings; + + ItemPunctuationOrderLanguage(SettingsStore settings, DropDownPreference item) { + super(item); + this.settings = settings; + } + + @Override + public ItemDropDown populate() { + populateLanguages(); + return this; + } + + + private void populateLanguages() { + if (item == null) { + return; + } + + LinkedHashMap values = new LinkedHashMap<>(); + ArrayList languages = LanguageCollection.getAll(item.getContext(), settings.getEnabledLanguageIds(), true); + if (languages.isEmpty()) { + return; + } + + for (Language lang : languages) { + values.put(String.valueOf(lang.getId()), lang.getName()); + } + + super.populate(values); + super.setValue(String.valueOf(languages.get(0).getId())); + } + + ItemPunctuationOrderLanguage onChange(ConsumerCompat callback) { + onChangeCallback = callback; + return this; + } + + @Override + protected boolean onClick(Preference preference, Object newKey) { + if (super.onClick(preference, newKey)) { + if (onChangeCallback != null) { + onChangeCallback.accept(newKey.toString()); + } + return true; + } + + return false; + } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/ItemPunctuationOrderSave.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/ItemPunctuationOrderSave.java new file mode 100644 index 00000000..749e19bc --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/ItemPunctuationOrderSave.java @@ -0,0 +1,25 @@ +package io.github.sspanak.tt9.preferences.screens.punctuation; + +import androidx.preference.Preference; + +import io.github.sspanak.tt9.preferences.items.ItemClickable; + +public class ItemPunctuationOrderSave extends ItemClickable { + public static final String NAME = "punctuation_order_save"; + private final Runnable clickHandler; + + public ItemPunctuationOrderSave(Preference item, Runnable clickHandler) { + super(item); + this.clickHandler = clickHandler; + } + + @Override + protected boolean onClick(Preference p) { + if (clickHandler == null) { + return false; + } + + clickHandler.run(); + return true; + } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/ItemRestoreDefaultPunctuation.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/ItemRestoreDefaultPunctuation.java new file mode 100644 index 00000000..1885d102 --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/ItemRestoreDefaultPunctuation.java @@ -0,0 +1,51 @@ +package io.github.sspanak.tt9.preferences.screens.punctuation; + +import androidx.annotation.NonNull; +import androidx.preference.Preference; + +import io.github.sspanak.tt9.languages.Language; +import io.github.sspanak.tt9.preferences.items.ItemClickable; +import io.github.sspanak.tt9.preferences.settings.SettingsStore; +import io.github.sspanak.tt9.util.ConsumerCompat; + +class ItemRestoreDefaultPunctuation extends ItemClickable { + public static final String NAME = "punctuation_order_reset_defaults"; + + private Language language; + private final ConsumerCompat onClick; + private final SettingsStore settings; + + ItemRestoreDefaultPunctuation(@NonNull SettingsStore settings, Preference item, ConsumerCompat onClick) { + super(item); + this.onClick = onClick; + this.settings = settings; + } + + ItemRestoreDefaultPunctuation setLanguage(Language language) { + this.language = language; + return this; + } + + @Override + protected boolean onClick(Preference p) { + if (language == null) { + return false; + } + + settings.saveSpecialChars( + language, + String.join("", language.getKeyCharacters(0)) + ); + + settings.savePunctuation( + language, + String.join("", language.getKeyCharacters(1)) + ); + + if (onClick != null) { + onClick.accept(String.valueOf(language.getId())); + } + + return true; + } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/PreferenceSentencePunctuationList.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/PreferenceSentencePunctuationList.java new file mode 100644 index 00000000..b890a3df --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/PreferenceSentencePunctuationList.java @@ -0,0 +1,60 @@ +package io.github.sspanak.tt9.preferences.screens.punctuation; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import io.github.sspanak.tt9.R; + +public class PreferenceSentencePunctuationList extends AbstractPreferenceCharList { + public static final String NAME = "punctuation_order_sentence"; + + public PreferenceSentencePunctuationList(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } + public PreferenceSentencePunctuationList(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } + public PreferenceSentencePunctuationList(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } + public PreferenceSentencePunctuationList(@NonNull Context context) { super(context); } + + @Override + @NonNull + protected String getChars() { + return getSettings().getPunctuation(language); + } + + /** + * We want to all the user to rearrange all characters even the mandatory ones. + * We will verify if they are present upon saving. + */ + @NonNull + @Override + protected char[] getMandatoryChars() { + return new char[0]; + } + + public boolean validateCurrentChars() { + StringBuilder missingCharList = new StringBuilder(); + + for (char c : getSettings().mandatoryPunctuation) { + if (currentChars.indexOf(c) == -1) { + missingCharList.append(" ").append(c).append(","); + } + } + + String error = ""; + if (missingCharList.length() > 0) { + int message = missingCharList.length() == 3 ? R.string.punctuation_order_mandatory_char_missing : R.string.punctuation_order_mandatory_chars_missing; + String missingChars = missingCharList.substring(0, missingCharList.length() - 1); + error = getContext().getString(message, missingChars); + } + + setSummary(error); + + return error.isEmpty(); + } + + @Override + public void saveCurrentChars() { + getSettings().savePunctuation(language, currentChars); + } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/PreferenceSpecialCharList.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/PreferenceSpecialCharList.java new file mode 100644 index 00000000..b844e0a8 --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/PreferenceSpecialCharList.java @@ -0,0 +1,48 @@ +package io.github.sspanak.tt9.preferences.screens.punctuation; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class PreferenceSpecialCharList extends AbstractPreferenceCharList { + public static final String NAME = "punctuation_order_special_chars"; + + public PreferenceSpecialCharList(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } + public PreferenceSpecialCharList(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } + public PreferenceSpecialCharList(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } + public PreferenceSpecialCharList(@NonNull Context context) { super(context); } + + @Override + @NonNull + protected String getChars() { + return getSettings().getSpecialChars(language); + } + + @NonNull + @Override + protected char[] getMandatoryChars() { + return getSettings().mandatorySpecialChars; + } + + @Override + public boolean validateCurrentChars() { + for (char c : getMandatoryChars()) { + currentChars = currentChars.replace(String.valueOf(c), ""); + } + + return true; + } + + @Override + public void saveCurrentChars() { + StringBuilder all = new StringBuilder(); + for (char c : getMandatoryChars()) { + all.append(c); + } + all.append(currentChars); + + getSettings().saveSpecialChars(language, all.toString()); + } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/PunctuationScreen.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/PunctuationScreen.java new file mode 100644 index 00000000..223d8e07 --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/punctuation/PunctuationScreen.java @@ -0,0 +1,130 @@ +package io.github.sspanak.tt9.preferences.screens.punctuation; + +import androidx.annotation.Nullable; +import androidx.preference.Preference; + +import io.github.sspanak.tt9.R; +import io.github.sspanak.tt9.languages.Language; +import io.github.sspanak.tt9.languages.LanguageCollection; +import io.github.sspanak.tt9.preferences.PreferencesActivity; +import io.github.sspanak.tt9.preferences.screens.BaseScreenFragment; + +public class PunctuationScreen extends BaseScreenFragment { + public static final String NAME = "Punctuation"; + private ItemPunctuationOrderLanguage languageList; + private ItemRestoreDefaultPunctuation restoreDefaults; + private PreferenceSpecialCharList specialCharList; + private PreferenceSentencePunctuationList punctuationList; + + public PunctuationScreen() { init(); } + public PunctuationScreen(PreferencesActivity activity) { init(activity); } + + @Override + public String getName() { + return NAME; + } + + @Override + protected int getTitle() { + return R.string.pref_category_punctuation_order; + } + + @Override + protected int getXml() { + return R.xml.prefs_screen_punctuation; + } + + + @Override + public void onBackPressed() { + onSaveOrdering(); + } + + + @Override + protected void onCreate() { + specialCharList = findPreference(PreferenceSpecialCharList.NAME); + punctuationList = findPreference(PreferenceSentencePunctuationList.NAME); + + initLanguageList(); + initResetDefaults(); + initSaveButton(); + loadCharLists(); + resetFontSize(false); + } + + + private void initLanguageList() { + languageList = (new ItemPunctuationOrderLanguage(activity.getSettings(), findPreference(ItemPunctuationOrderLanguage.NAME))); + languageList + .onChange(this::onLanguageChanged) + .enableClickHandler() + .populate() + .preview(); + } + + + private void initSaveButton() { + Preference item = findPreference(ItemPunctuationOrderSave.NAME); + if (item != null) { + new ItemPunctuationOrderSave(item, this::onSaveOrdering).enableClickHandler(); + } + } + + + private void initResetDefaults() { + Preference item = findPreference(ItemRestoreDefaultPunctuation.NAME); + if (item == null) { + return; + } + + restoreDefaults = new ItemRestoreDefaultPunctuation(activity.getSettings(), item, this::onLanguageChanged); + restoreDefaults + .setLanguage(LanguageCollection.getLanguage(activity, languageList.getValue())) + .enableClickHandler(); + } + + + private void onSaveOrdering() { + if (specialCharList != null && specialCharList.validateCurrentChars()) { + specialCharList.saveCurrentChars(); + } + + if (punctuationList != null && punctuationList.validateCurrentChars()) { + punctuationList.saveCurrentChars(); + } + } + + + private void onLanguageChanged(@Nullable String newLanguageId) { + Language language = LanguageCollection.getLanguage(activity, newLanguageId); + + restoreDefaults.setLanguage(language); + + if (specialCharList != null) { + specialCharList.onLanguageChange(language); + } + + if (punctuationList != null) { + punctuationList.onLanguageChange(language); + } + } + + + private void loadCharLists() { + loadCharList(findPreference(PreferenceSpecialCharList.NAME)); + loadCharList(findPreference(PreferenceSentencePunctuationList.NAME)); + } + + + private void loadCharList(AbstractPreferenceCharList list) { + if (list == null) { + return; + } + + list.setOnRender(() -> { + list.setOnRender(null); + onLanguageChanged(languageList.getValue()); + }); + } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsPunctuation.java b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsPunctuation.java new file mode 100644 index 00000000..8612757c --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsPunctuation.java @@ -0,0 +1,112 @@ +package io.github.sspanak.tt9.preferences.settings; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; + +import io.github.sspanak.tt9.languages.Language; + +class SettingsPunctuation extends SettingsInput { + SettingsPunctuation(Context context) { + super(context); + } + + + public final char[] mandatoryPunctuation = new char[] {'\'', '"', '-'}; + public final char[] mandatorySpecialChars = new char[] {' ', '\n'}; + + + public void savePunctuation(@NonNull Language language, @NonNull String punctuation) { + prefsEditor.putString("pref_punctuation_" + language.getId(), punctuation); + prefsEditor.apply(); + } + + + public void saveSpecialChars(@NonNull Language language, @NonNull String specialChars) { + prefsEditor.putString("pref_special_chars_" + language.getId(), specialChars); + prefsEditor.apply(); + } + + + @NonNull public String getPunctuation(Language language) { + return String.join("", getPunctuationAsList(language)); + } + + + @NonNull public String getSpecialChars(Language language) { + return String.join("", getSpecialCharsAsList(language)); + } + + + @NonNull + public ArrayList getPunctuationAsList(Language language) { + if (language == null) { + return new ArrayList<>(); + } + + return getCharsAsList( + prefs.getString("pref_punctuation_" + language.getId(), null), + language.getKeyCharacters(1) + ); + } + + + @NonNull + public ArrayList getSpecialCharsAsList(Language language) { + if (language == null) { + return new ArrayList<>(); + } + + return getCharsAsList( + prefs.getString("pref_special_chars_" + language.getId(), null), + language.getKeyCharacters(0) + ); + } + + + @NonNull + public ArrayList getOrderedKeyChars(Language language, int number) { + ArrayList orderedChars = new ArrayList<>(); + if (language == null) { + return orderedChars; + } + + if (number == 0) { + orderedChars = getSpecialCharsAsList(language); + } else if (number == 1) { + orderedChars = getPunctuationAsList(language); + } + + if (orderedChars.isEmpty()) { + orderedChars = language.getKeyCharacters(number); + } + + return orderedChars; + } + + + @NonNull + public ArrayList getOrderedKeyChars(Language language, int number, int group) { + if (group > 0 && language != null) { + return language.getKeyCharacters(number, group); + } + + return getOrderedKeyChars(language, number); + } + + + private ArrayList getCharsAsList(String chars, ArrayList defaultValue) { + if (chars == null) { + return defaultValue; + } + + ArrayList charsList = new ArrayList<>(); + for (int i = 0; i < chars.length(); i++) { + charsList.add(String.valueOf(chars.charAt(i))); + } + + return charsList; + } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsStore.java b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsStore.java index 984565d3..d04ebbd2 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsStore.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsStore.java @@ -32,6 +32,7 @@ public class SettingsStore extends SettingsUI { public final static int SUGGESTIONS_SELECT_ANIMATION_DURATION = 66; public final static int SUGGESTIONS_TRANSLATE_ANIMATION_DURATION = 0; public final static int TEXT_INPUT_DEBOUNCE_TIME = 500; // ms + public final static int TEXT_INPUT_PUNCTUATION_ORDER_DEBOUNCE_TIME = 100; // ms public final static int WORD_BACKGROUND_TASKS_DELAY = 15000; // ms public final static int WORD_FREQUENCY_MAX = 25500; public final static int WORD_FREQUENCY_NORMALIZATION_DIVIDER = 100; // normalized frequency = WORD_FREQUENCY_MAX / WORD_FREQUENCY_NORMALIZATION_DIVIDER diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsTyping.java b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsTyping.java index f65347f1..31a8d166 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsTyping.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsTyping.java @@ -2,7 +2,7 @@ package io.github.sspanak.tt9.preferences.settings; import android.content.Context; -class SettingsTyping extends SettingsInput { +class SettingsTyping extends SettingsPunctuation { SettingsTyping(Context context) { super(context); } public int getAbcAutoAcceptTimeout() { diff --git a/app/src/main/java/io/github/sspanak/tt9/util/TextTools.java b/app/src/main/java/io/github/sspanak/tt9/util/TextTools.java index 297e8a6b..4cbcf974 100644 --- a/app/src/main/java/io/github/sspanak/tt9/util/TextTools.java +++ b/app/src/main/java/io/github/sspanak/tt9/util/TextTools.java @@ -2,6 +2,7 @@ package io.github.sspanak.tt9.util; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.HashSet; import java.util.Locale; import java.util.TimeZone; import java.util.regex.Pattern; diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 780ed97a..308ec60d 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -177,4 +177,13 @@ думи Бързо изтриване Изтривай цели думи при задържане или плъзване на Backspace. (Не работи в някои приложения.) + Ред на пунктуацията + Интервал и нов ред ще бъдат автоматично добавени в началото на списъка. + Липсва задължителен символ:%1$s + Липсват задължителни символи:%1$s + Ред на символите на клавиш 1 + Ред на символите на клавиш 0 + Възстанови реда по подразбиране + Език + Запази подредбата diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f121917a..82dedddb 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -165,4 +165,13 @@ Wörter Schnelles Löschen Ganze Wörter löschen, indem Sie die Rücktaste gedrückt halten oder wischen. (Nicht in allen Apps unterstützt) + Interpunktionsreihenfolge + Leer- und Zeilenumbruchzeichen werden automatisch am Anfang der Liste hinzugefügt. + Fehlendes erforderliches Zeichen:%1$s + Fehlende erforderliche Zeichen:%1$s + Zeichenreihenfolge der 1-Taste + Zeichenreihenfolge der 0-Taste + Standardreihenfolge wiederherstellen + Sprache + Reihenfolge speichern diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 7d0e9c90..87ef183c 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -175,4 +175,13 @@ palabras Eliminación rápida Borrar palabras completas manteniendo pulsado o deslizando Retroceso. (No compatible con algunas aplicaciones) + Orden de puntuación + Los caracteres Espacio y Nueva línea se añadirán automáticamente al principio de la lista. + Falta carácter obligatorio:%1$s + Faltan caracteres obligatorios:%1$s + Orden de caracteres de la tecla 1 + Orden de caracteres de la tecla 0 + Restaurar el orden predeterminado + Idioma + Guardar orden diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0b0d896e..85e1405d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -173,4 +173,13 @@ mots Suppression rapide Effacer des mots entiers en maintenant ou en glissant sur Retour arrière. (Non pris en charge dans certaines applications) + Ordre de ponctuation + Les caractères Espace et Retour à la ligne seront automatiquement ajoutés au début de la liste. + Caractère obligatoire manquant :%1$s + Caractères obligatoires manquants :%1$s + Ordre des caractères de la touche 1 + Ordre des caractères de la touche 0 + Restaurer l’ordre par défaut + Langue + Enregistrer l’ordre diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index cad4f253..7d63d80e 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -165,5 +165,14 @@ parole Cancellazione rapida Cancellare intere parole tenendo premuto o scorrendo Backspace. (Non supportato in alcune app) + Ordine di punteggiatura + I caratteri Spazio e Nuova linea verranno automaticamente aggiunti all\'inizio dell\'elenco. + Carattere obbligatorio mancante:%1$s + Caratteri obbligatori mancanti:%1$s + Ordine dei caratteri del tasto 1 + Ordine dei caratteri del tasto 0 + Ripristina ordine predefinito + Lingua + Salvare l\'ordine diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index ce56a286..f64d0b61 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -178,4 +178,13 @@ מילים מחיקה מהירה למחוק מילים שלמות על ידי החזקת Backspace או החלקה עליו. (לא נתמך בחלק מהאפליקציות) + סדר סימני פיסוק + תווי רווח ושורה חדשה יתווספו אוטומטית בתחילת הרשימה. + תו חובה חסר: %1$s + תווי חובה חסרים:%1$s + סדר התווים של מקש 1 + סדר התווים של מקש 0 + שחזר את הסדר המוגדר כברירת מחדל + שפה + שמור את הסדר diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 7fb9d955..4739be83 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -184,4 +184,13 @@ žodžių Greitas ištrynimas Ištrinti visus žodžius laikant arba perbraukiant Backspace. (Nepalaikoma kai kuriose programėlėse) + Skyrybos ženklų tvarka + Tarpas ir naujos eilutės simboliai bus automatiškai pridėti sąrašo pradžioje. + Trūksta privalomo simbolio:%1$s + Trūksta privalomų simbolių:%1$s + Simbolių tvarka ant klavišo 1 + Simbolių tvarka ant klavišo 0 + Atkurti numatytąją tvarką + Kalba + Išsaugoti tvarką diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 5d621faf..7700f318 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -164,4 +164,13 @@ woorden Snel verwijderen Hele woorden wissen door Backspace ingedrukt te houden of te vegen. (Niet ondersteund in sommige apps) + Interpunctievolgorde + Spatie- en nieuwe regeltekens worden automatisch aan het begin van de lijst toegevoegd. + Ontbrekend verplicht teken:%1$s + Ontbrekende verplichte tekens:%1$s + Tekenvolgorde van de 1-toets + Tekenvolgorde van de 0-toets + Standaardvolgorde herstellen + Taal + Volgorde opslaan diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index cb312b2f..b4cdc6a6 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -178,4 +178,13 @@ palavras Exclusão rápida Apagar palavras inteiras mantendo pressionado ou deslizando o Backspace. (Não suportado em alguns aplicativos) + Ordem de pontuação + Os caracteres de Espaço e Nova linha serão automaticamente adicionados no início da lista. + Caractere obrigatório ausente:%1$s + Caracteres obrigatórios ausentes:%1$s + Ordem de caracteres da tecla 1 + Ordem de caracteres da tecla 0 + Restaurar ordem padrão + Idioma + Salvar ordem diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 85d040c8..29934656 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -175,4 +175,13 @@ слов Быстрое удаление Стереть целые слова, удерживая или проведя по клавише Backspace. (Не поддерживается в некоторых приложениях) + Порядок пунктуации + Пробел и символ новой строки будут автоматически добавлены в начало списка. + Отсутствует обязательный символ:%1$s + Отсутствуют обязательные символы:%1$s + Порядок символов на клавише 1 + Порядок символов на клавише 0 + Восстановить порядок по умолчанию + Язык + Сохранить порядок diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index e6255171..e096c9a2 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -178,4 +178,13 @@ kelime Hızlı Silme Geri tuşunu basılı tutarak veya kaydırarak tüm kelimeleri sil. (Bazı uygulamalarda desteklenmez) + Noktalama sırası + Boşluk ve Yeni Satır karakterleri listenin başına otomatik olarak eklenecektir. + Zorunlu karakter eksik:%1$s + Zorunlu karakterler eksik:%1$s + 1 tuşunun karakter sırası + 0 tuşunun karakter sırası + Varsayılan sırayı geri yükle + Dil + Sıralamayı kaydet diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 0d43a79c..545a9b53 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -186,4 +186,13 @@ слів Швидке видалення Видалити цілі слова, утримуючи або провівши клавішею Backspace. (Не підтримується в деяких додатках) + Порядок пунктуації + Пробіл і символ нового рядка буде автоматично додано на початок списку. + Відсутній обов’язковий символ:%1$s + Відсутні обов’язкові символи:%1$s + Порядок символів на клавіші 1 + Порядок символів на клавіші 0 + Відновити початковий порядок + Мова + Зберегти порядок diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 62580521..21350e97 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,6 +6,7 @@ TT9 Settings Completed Loading… + Language No Language Search Results No results. @@ -36,6 +37,7 @@ Compatibility Keypad Predictive Mode + Punctuation Order Initial Setup Typing Modes Usage Stats @@ -166,6 +168,13 @@ Search for Languages words + Space and Newline characters will be automatically added at the beginning of the list. + Missing mandatory character:%1$s + Missing mandatory characters:%1$s + Restore Default Order + Save Order + 1-key Character Order + 0-key Character Order Status Select Default Keyboard diff --git a/app/src/main/res/xml/prefs_screen_keypad.xml b/app/src/main/res/xml/prefs_screen_keypad.xml index b966f2fc..fd0ae7c9 100644 --- a/app/src/main/res/xml/prefs_screen_keypad.xml +++ b/app/src/main/res/xml/prefs_screen_keypad.xml @@ -8,6 +8,11 @@ android:key="screen_hotkeys" android:title="@string/pref_category_function_keys" /> + + + + + + + + + + + + + +