diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/CommandHandler.java b/app/src/main/java/io/github/sspanak/tt9/ime/CommandHandler.java index ff96d206..ef45c66d 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/CommandHandler.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/CommandHandler.java @@ -10,6 +10,7 @@ import io.github.sspanak.tt9.ime.modes.InputModeKind; import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.ui.UI; import io.github.sspanak.tt9.ui.dialogs.AddWordDialog; +import io.github.sspanak.tt9.ui.dialogs.ChangeLanguageDialog; import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.util.Ternary; import io.github.sspanak.tt9.util.sys.Clipboard; @@ -166,16 +167,56 @@ abstract public class CommandHandler extends TextEditingHandler { } - protected void nextLang() { + protected void changeLang() { + suggestionOps.cancelDelayedAccept(); stopVoiceInput(); + ChangeLanguageDialog.show(this, mInputMode.getSequence(), textField.getComposingText()); + } - // select the next language + + protected void nextLang() { int previous = mEnabledLanguages.indexOf(mLanguage.getId()); int next = (previous + 1) % mEnabledLanguages.size(); - mLanguage = LanguageCollection.getLanguage(mEnabledLanguages.get(next)); + setLang(mEnabledLanguages.get(next)); + } - // validate and save it for the next time + + public void setLang(int langId) { + if (!mEnabledLanguages.contains(langId)) { + return; + } + + suggestionOps.cancelDelayedAccept(); + stopVoiceInput(); + + mLanguage = LanguageCollection.getLanguage(langId); validateLanguages(); + + detectRTL(); + + // for languages that do not have ABC or Predictive, make sure we remain in valid state + if (mInputMode.changeLanguage(mLanguage)) { + mInputMode.clearWordStem(); + } else { + final String digits = mInputMode.getSequence(); + mInputMode = InputMode.getInstance(settings, mLanguage, inputType, textField, determineInputModeId()); + mInputMode.setSequence(digits); + } + + getSuggestions(null); + setStatusIcon(mInputMode, mLanguage); + statusBar.setText(mInputMode); + suggestionOps.setRTL(isLanguageRTL); + mainView.render(); + if (settings.isMainLayoutStealth() && !settings.isStatusIconEnabled()) { + UI.toastShortSingle(this, mInputMode.getClass().getSimpleName(), mInputMode.toString()); + } + + if (InputModeKind.isPredictive(mInputMode)) { + DictionaryLoader.autoLoad(this, mLanguage); + } + + forceShowWindow(); } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/HotkeyHandler.java b/app/src/main/java/io/github/sspanak/tt9/ime/HotkeyHandler.java index 7fb0ec7d..7881ec48 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/HotkeyHandler.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/HotkeyHandler.java @@ -3,9 +3,7 @@ package io.github.sspanak.tt9.ime; import android.view.KeyEvent; import io.github.sspanak.tt9.R; -import io.github.sspanak.tt9.db.words.DictionaryLoader; import io.github.sspanak.tt9.ime.helpers.TextField; -import io.github.sspanak.tt9.ime.modes.InputMode; import io.github.sspanak.tt9.ime.modes.InputModeKind; import io.github.sspanak.tt9.languages.LanguageKind; import io.github.sspanak.tt9.ui.UI; @@ -300,33 +298,12 @@ public abstract class HotkeyHandler extends CommandHandler { return true; } - suggestionOps.cancelDelayedAccept(); - nextLang(); - detectRTL(); - - // for languages that do not have ABC or Predictive, make sure we remain in valid state - if (mInputMode.changeLanguage(mLanguage)) { - mInputMode.clearWordStem(); + if (settings.getQuickSwitchLanguage()) { + nextLang(); } else { - final String digits = mInputMode.getSequence(); - mInputMode = InputMode.getInstance(settings, mLanguage, inputType, textField, determineInputModeId()); - mInputMode.setSequence(digits); + changeLang(); } - getSuggestions(null); - setStatusIcon(mInputMode, mLanguage); - statusBar.setText(mInputMode); - suggestionOps.setRTL(isLanguageRTL); - mainView.render(); - if (settings.isMainLayoutStealth() && !settings.isStatusIconEnabled()) { - UI.toastShortSingle(this, mInputMode.getClass().getSimpleName(), mInputMode.toString()); - } - - if (InputModeKind.isPredictive(mInputMode)) { - DictionaryLoader.autoLoad(this, mLanguage); - } - - forceShowWindow(); return true; } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/TraditionalT9.java b/app/src/main/java/io/github/sspanak/tt9/ime/TraditionalT9.java index f577cf4b..bd41c63d 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/TraditionalT9.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/TraditionalT9.java @@ -10,6 +10,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.db.DataStore; @@ -19,6 +20,7 @@ import io.github.sspanak.tt9.ime.modes.InputModeKind; import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.preferences.settings.SettingsStore; import io.github.sspanak.tt9.ui.UI; +import io.github.sspanak.tt9.ui.dialogs.ChangeLanguageDialog; import io.github.sspanak.tt9.ui.dialogs.PopupDialog; import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.util.sys.SystemSettings; @@ -109,11 +111,20 @@ public class TraditionalT9 extends MainViewHandler { int result = super.onStartCommand(intent, flags, startId); String message = intent != null ? intent.getStringExtra(PopupDialog.INTENT_CLOSE) : null; - if (message != null) { - forceShowWindow(); - if (!message.isEmpty()) { - UI.toastLong(this, message); - } + if (message == null) { + return result; + } + + forceShowWindow(); + + if (!message.isEmpty()) { + UI.toastLong(this, message); + } else if (ChangeLanguageDialog.INTENT_SET_LANGUAGE.equals(intent.getStringExtra(ChangeLanguageDialog.INTENT_SET_LANGUAGE))) { + onResume( + intent.getStringExtra(ChangeLanguageDialog.PARAMETER_LANGUAGE), + intent.getStringExtra(ChangeLanguageDialog.PARAMETER_SEQUENCE), + intent.getStringExtra(ChangeLanguageDialog.PARAMETER_WORD) + ); } return result; @@ -168,6 +179,24 @@ public class TraditionalT9 extends MainViewHandler { } + private void onResume(@Nullable String langStringId, @Nullable String sequence, @Nullable String word) { + int languageId; + try { + langStringId = langStringId == null ? "(null)" : langStringId; + languageId = Integer.parseInt(langStringId); + } catch (NumberFormatException e) { + Logger.e(LOG_TAG, "Can not resume typing. Failed to parse language ID '" + langStringId + "'. " + e); + return; + } + + if (word != null && !word.isEmpty() && sequence != null && !sequence.isEmpty()) { + mInputMode.setSequence(sequence); + } + + setLang(languageId); + } + + @Override protected void onStop() { stopVoiceInput(); diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/deleteWords/PreferenceDeletableWord.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/deleteWords/PreferenceDeletableWord.java index 163a71d2..8b7927fa 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/deleteWords/PreferenceDeletableWord.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/deleteWords/PreferenceDeletableWord.java @@ -13,6 +13,7 @@ import io.github.sspanak.tt9.db.DataStore; import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.preferences.custom.ScreenPreference; import io.github.sspanak.tt9.preferences.settings.SettingsStore; +import io.github.sspanak.tt9.ui.PopupBuilder; import io.github.sspanak.tt9.ui.UI; public class PreferenceDeletableWord extends ScreenPreference { @@ -48,17 +49,13 @@ public class PreferenceDeletableWord extends ScreenPreference { super.onClick(); Context context = getContext(); - - UI.confirm( - context, - context.getString(R.string.delete_words_deleted_confirm_deletion_title), - context.getString(R.string.delete_words_deleted_confirm_deletion_question, word), - context.getString(R.string.delete_words_delete), - this::onDeletionConfirmed, - true, - null, - null - ); + new PopupBuilder(context) + .setCancelable(true) + .setTitle(context.getString(R.string.delete_words_deleted_confirm_deletion_title)) + .setMessage(context.getString(R.string.delete_words_deleted_confirm_deletion_question, word)) + .setNegativeButton(true, null) + .setPositiveButton(context.getString(R.string.delete_words_delete), this::onDeletionConfirmed) + .show(); } diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/hotkeys/PreferenceHotkey.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/hotkeys/PreferenceHotkey.java index 05852024..e0387e7c 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/hotkeys/PreferenceHotkey.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/hotkeys/PreferenceHotkey.java @@ -12,7 +12,7 @@ import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.ime.helpers.Key; import io.github.sspanak.tt9.preferences.custom.ScreenPreference; import io.github.sspanak.tt9.preferences.settings.SettingsStore; -import io.github.sspanak.tt9.ui.UI; +import io.github.sspanak.tt9.ui.PopupBuilder; import io.github.sspanak.tt9.util.sys.DeviceInfo; public class PreferenceHotkey extends ScreenPreference implements DialogInterface.OnKeyListener{ @@ -72,16 +72,12 @@ public class PreferenceHotkey extends ScreenPreference implements DialogInterfac boolean enableCancelButton = !DeviceInfo.noTouchScreen(getContext()); - UI.confirm( - getContext(), - getKey(), - getContext().getString(R.string.function_assign_instructions, getTitle()), - null, - null, - enableCancelButton, - enableCancelButton ? ()->{} : null, - this - ); + new PopupBuilder(getContext()) + .setCancelable(false) + .setMessage(getContext().getString(R.string.function_assign_instructions, getTitle())) + .setNegativeButton(enableCancelButton, () -> {}) + .setOnKeyListener(this) + .show(); } @@ -164,19 +160,17 @@ public class PreferenceHotkey extends ScreenPreference implements DialogInterfac getTitle() ); - UI.confirm( - getContext(), - getKey(), - question, - getContext().getString(R.string.function_reassign), - () -> { - settings.setFunctionKey(otherFunction, KeyEvent.KEYCODE_UNKNOWN); - onAssign(dialog, keyCode); - }, - true, - null, - null - ); + new PopupBuilder(getContext()) + .setCancelable(false) + .setMessage(question) + .setNegativeButton(true, null) + .setPositiveButton( + getContext().getString(R.string.function_reassign), + () -> { + settings.setFunctionKey(otherFunction, KeyEvent.KEYCODE_UNKNOWN); + onAssign(dialog, keyCode); + } + ).show(); return true; } diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsUI.java b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsUI.java index 43f6f0c8..6bc5274a 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsUI.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsUI.java @@ -75,6 +75,10 @@ public class SettingsUI extends SettingsTyping { prefsEditor.apply(); } + public boolean getQuickSwitchLanguage() { + return prefs.getBoolean("pref_quick_switch_language", true); + } + public int getSettingsFontSize() { int defaultSize = DeviceInfo.IS_QIN_F21 || DeviceInfo.IS_LG_X100S ? FONT_SIZE_LARGE : FONT_SIZE_DEFAULT; return getStringifiedInt("pref_font_size", defaultSize); diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/LanguageRadioButton.java b/app/src/main/java/io/github/sspanak/tt9/ui/LanguageRadioButton.java new file mode 100644 index 00000000..93e2887b --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/ui/LanguageRadioButton.java @@ -0,0 +1,72 @@ +package io.github.sspanak.tt9.ui; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.LinearLayout; +import android.widget.RadioButton; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.content.res.AppCompatResources; + +import io.github.sspanak.tt9.R; +import io.github.sspanak.tt9.languages.Language; + +public class LanguageRadioButton extends LinearLayout { + private static Drawable highlightDrawable; + + private TextView label; + private RadioButton radio; + + public LanguageRadioButton(Context context) { super(context); init(context); } + public LanguageRadioButton(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context); } + public LanguageRadioButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } + + private void init(Context context) { + LayoutInflater.from(context).inflate(R.layout.radio_button_language, this, true); + setOrientation(HORIZONTAL); + label = findViewById(R.id.radio_label); + radio = findViewById(R.id.radio_button); + if (highlightDrawable == null) { + highlightDrawable = AppCompatResources.getDrawable(context, R.color.key_num_ripple); + } + } + + /** + * autoHighlightCompat + * Used to highlight the button when scrolling with the DPAD. Normally, this is done by setting + * android:background="?android:attr/selectableItemBackground" in the styles. However, this does + * not work on Sonim XP3800, which means it may not work on other devices as well. So... here is + * one more device hack. + */ + public void autoHighlightCompat() { + setBackground(hasFocus() ? highlightDrawable : null); + } + + public LanguageRadioButton setLanguage(@NonNull Language language, String labelPrefix) { + setId(language.getId()); + radio.setId(language.getId()); + + final String text = labelPrefix != null ? labelPrefix + language.getName() : language.getName(); + label.setText(text); + + return this; + } + + public LanguageRadioButton setChecked(boolean checked) { + radio.setChecked(checked); + return this; + } + + /** + * On Android 5, ensure there is no clickable="true" in the XML, otherwise the + * listener will not be called. + */ + public LanguageRadioButton setOnClick(@Nullable OnClickListener l) { + super.setOnClickListener(l); + return this; + } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/PopupBuilder.java b/app/src/main/java/io/github/sspanak/tt9/ui/PopupBuilder.java new file mode 100644 index 00000000..214e29a2 --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/ui/PopupBuilder.java @@ -0,0 +1,115 @@ +package io.github.sspanak.tt9.ui; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.view.View; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +import io.github.sspanak.tt9.util.sys.DeviceInfo; + +public class PopupBuilder { + private final Context context; + private MaterialAlertDialogBuilder builder12; + private AlertDialog.Builder builderLegacy; + + + public PopupBuilder(Context context) { + this.context = context; + + if (DeviceInfo.AT_LEAST_ANDROID_12) { + builder12 = new MaterialAlertDialogBuilder(context); + } else { + builderLegacy = new AlertDialog.Builder(context); + } + } + + + public PopupBuilder setCancelable(boolean cancelable) { + if (DeviceInfo.AT_LEAST_ANDROID_12) { + builder12.setCancelable(cancelable); + } else { + builderLegacy.setCancelable(cancelable); + } + return this; + } + + + public PopupBuilder setMessage(String message) { + if (DeviceInfo.AT_LEAST_ANDROID_12) { + builder12.setMessage(message); + } else { + builderLegacy.setMessage(message); + } + return this; + } + + + public PopupBuilder setNegativeButton(boolean yes, Runnable action) { + if (DeviceInfo.AT_LEAST_ANDROID_12) { + builder12.setNegativeButton( + yes ? context.getString(android.R.string.cancel) : null, + (dialog, whichButton) -> { if (action != null) action.run(); } + ); + } else { + builderLegacy.setNegativeButton( + yes ? context.getString(android.R.string.cancel) : null, + (dialog, whichButton) -> { if (action != null) action.run(); } + ); + } + return this; + } + + + public PopupBuilder setOnKeyListener(DialogInterface.OnKeyListener listener) { + if (DeviceInfo.AT_LEAST_ANDROID_12) { + builder12.setOnKeyListener(listener); + } else { + builderLegacy.setOnKeyListener(listener); + } + return this; + } + + + public PopupBuilder setPositiveButton(String text, Runnable action) { + if (DeviceInfo.AT_LEAST_ANDROID_12) { + builder12.setPositiveButton( + text, + (dialog, which) -> action.run() + ); + } else { + builderLegacy.setPositiveButton( + text, + (dialog, which) -> action.run() + ); + } + return this; + } + + + public PopupBuilder setTitle(String title) { + if (DeviceInfo.AT_LEAST_ANDROID_12) { + builder12.setTitle(title); + } else { + builderLegacy.setTitle(title); + } + return this; + } + + + public PopupBuilder setView(View view) { + if (DeviceInfo.AT_LEAST_ANDROID_12) { + builder12.setView(view); + } else { + builderLegacy.setView(view); + } + return this; + } + + + public Dialog show() { + return DeviceInfo.AT_LEAST_ANDROID_12 ? builder12.show() : builderLegacy.show(); + } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/UI.java b/app/src/main/java/io/github/sspanak/tt9/ui/UI.java index cac796ca..109a18cd 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/UI.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/UI.java @@ -1,10 +1,7 @@ package io.github.sspanak.tt9.ui; -import android.app.AlertDialog; -import android.app.Dialog; import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.inputmethodservice.InputMethodService; import android.os.Looper; @@ -13,12 +10,9 @@ import android.widget.Toast; import androidx.annotation.NonNull; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - import java.util.HashMap; import io.github.sspanak.tt9.preferences.PreferencesActivity; -import io.github.sspanak.tt9.util.sys.DeviceInfo; public class UI { private static final HashMap singleToasts = new HashMap<>(); @@ -57,31 +51,6 @@ public class UI { } - public static void confirm(@NonNull Context context, String title, String message, String OKLabel, Runnable onOk, boolean cancelLabel, Runnable onCancel, DialogInterface.OnKeyListener onKey) { - Dialog dialogue; - - if (DeviceInfo.AT_LEAST_ANDROID_12) { - dialogue = new MaterialAlertDialogBuilder(context) - .setMessage(message) - .setPositiveButton(OKLabel, (dialog, whichButton) -> { if (onOk != null) onOk.run(); }) - .setNegativeButton(cancelLabel ? context.getString(android.R.string.cancel) : null, (dialog, whichButton) -> { if (onCancel != null) onCancel.run(); }) - .show(); - } else { - dialogue = new AlertDialog.Builder(context) - .setMessage(message) - .setPositiveButton(OKLabel, (dialog, whichButton) -> { if (onOk != null) onOk.run(); }) - .setNegativeButton(cancelLabel ? context.getString(android.R.string.cancel) : null, (dialog, whichButton) -> { if (onCancel != null) onCancel.run(); }) - .show(); - } - - dialogue.setTitle(title); - dialogue.setCancelable(false); - if (onKey != null) { - dialogue.setOnKeyListener(onKey); - } - } - - public static void toast(Context context, CharSequence msg) { Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); } diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AddWordDialog.java b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AddWordDialog.java index 7fca3287..3c2da68a 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AddWordDialog.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AddWordDialog.java @@ -11,12 +11,9 @@ import io.github.sspanak.tt9.db.DataStore; import io.github.sspanak.tt9.db.entities.AddWordResult; import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.LanguageCollection; -import io.github.sspanak.tt9.preferences.settings.SettingsStore; import io.github.sspanak.tt9.util.ConsumerCompat; -import io.github.sspanak.tt9.util.ThemedContextBuilder; -import io.github.sspanak.tt9.util.sys.DeviceInfo; -public class AddWordDialog extends PopupDialog { +public class AddWordDialog extends ThemedPopupDialog { public static final String TYPE = "tt9.popup_dialog.add_word"; public static final String PARAMETER_LANGUAGE = "lang"; public static final String PARAMETER_WORD = "word"; @@ -26,18 +23,7 @@ public class AddWordDialog extends PopupDialog { AddWordDialog(@NonNull Context context, @NonNull Intent intent, ConsumerCompat activityFinisher) { - super( - new ThemedContextBuilder() - .setConfiguration(context.getApplicationContext().getResources().getConfiguration()) - .setContext(context) - .setSettings(new SettingsStore(context)) - // The main theme does not work on Android <= 11 and the _AddWord theme does not work on 12+. - // Not sure why since they inherit from the same parent, but it is what it is. - .setTheme(DeviceInfo.AT_LEAST_ANDROID_12 ? R.style.TTheme : R.style.TTheme_AddWord) - .build(), - activityFinisher - ); - + super(context, activityFinisher, R.style.TTheme_AddWord); title = context.getResources().getString(R.string.add_word_title); OKLabel = context.getResources().getString(R.string.add_word_add); parseIntent(context, intent); diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AutoUpdateMonologue.java b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AutoUpdateMonolog.java similarity index 89% rename from app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AutoUpdateMonologue.java rename to app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AutoUpdateMonolog.java index 4b5e0b9e..2ce110c9 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AutoUpdateMonologue.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AutoUpdateMonolog.java @@ -11,14 +11,14 @@ import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.util.ConsumerCompat; import io.github.sspanak.tt9.util.Logger; -public class AutoUpdateMonologue extends PopupDialog { +public class AutoUpdateMonolog extends PopupDialog { public static final String TYPE = "tt9.popup_dialog.confirm_words_update"; public static final String PARAMETER_LANGUAGE = "lang"; private Language language; - AutoUpdateMonologue(@NonNull Context context, @NonNull Intent intent, ConsumerCompat activityFinisher) { + AutoUpdateMonolog(@NonNull Context context, @NonNull Intent intent, ConsumerCompat activityFinisher) { super(context, activityFinisher); LanguageCollection.init(context); parseIntent(intent); diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/ChangeLanguageDialog.java b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/ChangeLanguageDialog.java new file mode 100644 index 00000000..439d4ed9 --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/ChangeLanguageDialog.java @@ -0,0 +1,199 @@ +package io.github.sspanak.tt9.ui.dialogs; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.inputmethodservice.InputMethodService; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; + +import java.util.ArrayList; +import java.util.HashMap; + +import io.github.sspanak.tt9.R; +import io.github.sspanak.tt9.ime.helpers.Key; +import io.github.sspanak.tt9.languages.Language; +import io.github.sspanak.tt9.languages.LanguageCollection; +import io.github.sspanak.tt9.preferences.settings.SettingsStore; +import io.github.sspanak.tt9.ui.LanguageRadioButton; +import io.github.sspanak.tt9.ui.PopupBuilder; +import io.github.sspanak.tt9.util.ConsumerCompat; +import io.github.sspanak.tt9.util.sys.DeviceInfo; + +public class ChangeLanguageDialog extends ThemedPopupDialog { + public static final String TYPE = "tt9.popup_dialog.change_language"; + public static final String INTENT_SET_LANGUAGE = "tt9.popup_dialog.command.set_language"; + public static final String PARAMETER_LANGUAGE = "tt9.popup_dialog.parameter.language"; + public static final String PARAMETER_SEQUENCE = "tt9.popup_dialog.parameter.sequence"; + public static final String PARAMETER_WORD = "tt9.popup_dialog.parameter.word"; + + private final ConsumerCompat> activityFinisher; + private final LayoutInflater inflater; + private final ArrayList languages; + private final SettingsStore settings; + private final String currentSequence; + private final String currentWord; + + private final ArrayList radioButtonsCache = new ArrayList<>(); + private Dialog popup; + + + ChangeLanguageDialog(@NonNull AppCompatActivity context, @NonNull Intent intent, ConsumerCompat> activityFinisher) { + super(context, null, R.style.TTheme_AddWord); + + this.activityFinisher = activityFinisher; + title = context.getResources().getString(R.string.language_popup_title); + OKLabel = null; + + inflater = context.getLayoutInflater(); + settings = new SettingsStore(context); + languages = LanguageCollection.getAll(settings.getEnabledLanguageIds(), true); + + currentSequence = intent.getStringExtra(PARAMETER_SEQUENCE); + currentWord = intent.getStringExtra(PARAMETER_WORD); + } + + + private void onClick(View button) { + changeLanguage(button.getId()); + } + + + @Override + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + if (Key.isBack(keyCode)) { + close(); + return true; + } + + int languageId = -1; + + if (Key.isOK(keyCode)) { + languageId = getSelected(); + } else if (Key.isNumber(keyCode)) { + languageId = getByIndex(Key.codeToNumber(settings, keyCode) - 1); + } else if (event.getAction() == KeyEvent.ACTION_UP && (keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_DPAD_UP)) { + for (LanguageRadioButton radio : radioButtonsCache) radio.autoHighlightCompat(); // yet another device hack + } + + if (languageId == -1) { + return false; + } + + changeLanguage(languageId); + return true; + } + + + private int getSelected() { + for (LanguageRadioButton radio : radioButtonsCache) { + if (radio.hasFocus()) { + return radio.getId(); + } + } + + return -1; + } + + + private int getByIndex(int index) { + return (index < 0 || index >= languages.size()) ? -1 : languages.get(index).getId(); + } + + + @Override + protected void close() { + detachRadioButtons(); + if (popup != null) { + popup.dismiss(); + popup = null; + } + activityFinisher.accept(null); + } + + + private void changeLanguage(int languageId) { + detachRadioButtons(); + if (popup != null) { + popup.dismiss(); + popup = null; + } + + if (activityFinisher != null) { + HashMap messages = new HashMap<>(); + messages.put(INTENT_SET_LANGUAGE, INTENT_SET_LANGUAGE); + messages.put(PARAMETER_LANGUAGE, String.valueOf(languageId)); + messages.put(PARAMETER_SEQUENCE, currentSequence); + messages.put(PARAMETER_WORD, currentWord); + activityFinisher.accept(messages); + } + } + + + private void detachRadioButtons() { + for (LanguageRadioButton radio : radioButtonsCache) { + radio.setOnClick(null); + LinearLayout parent = (LinearLayout) radio.getParent(); + if (parent != null) { + parent.removeView(radio); + } + } + } + + + private View generateRadioButtons() { + final int currentLanguageId = settings.getInputLanguage(); + final View view = inflater.inflate(R.layout.popup_language_select, null); + final LinearLayout radioGroup = view.findViewById(R.id.language_select_list); + + radioButtonsCache.clear(); + + for (int i = 0, end = languages.size(); i < end; i++) { + final String labelPrefix = DeviceInfo.noKeyboard(context) ? null : (i + 1) + ". "; + + LanguageRadioButton radioButton = new LanguageRadioButton(context) + .setOnClick(this::onClick) + .setLanguage(languages.get(i), labelPrefix) + .setChecked(languages.get(i).getId() == currentLanguageId); + + radioButtonsCache.add(radioButton); + radioGroup.addView(radioButton); + } + + return view; + } + + + @Override + void render() { + popup = new PopupBuilder(context) + .setCancelable(false) + .setTitle(title) + .setMessage(message) + .setNegativeButton(true, this::close) + .setOnKeyListener(this) + .setView(generateRadioButtons()) + .show(); + } + + + /** + * Open a popup dialog containing a list of the enabled languages. After a language is selected, + * "currentSequence" and "currentWord" are passed back to the IME, in case it wants to recompose them. + */ + public static void show(InputMethodService ims, String currentSequence, String currentWord) { + Intent intent = new Intent(ims, PopupDialogActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + intent.putExtra(PARAMETER_DIALOG_TYPE, TYPE); + intent.putExtra(PARAMETER_SEQUENCE, currentSequence); + intent.putExtra(PARAMETER_WORD, currentWord); + ims.startActivity(intent); + } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/PopupDialog.java b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/PopupDialog.java index c8eb144e..6e04e7e1 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/PopupDialog.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/PopupDialog.java @@ -1,13 +1,15 @@ package io.github.sspanak.tt9.ui.dialogs; import android.content.Context; +import android.content.DialogInterface; +import android.view.KeyEvent; import androidx.annotation.NonNull; -import io.github.sspanak.tt9.ui.UI; +import io.github.sspanak.tt9.ui.PopupBuilder; import io.github.sspanak.tt9.util.ConsumerCompat; -abstract public class PopupDialog { +abstract public class PopupDialog implements DialogInterface.OnKeyListener { public static final String INTENT_CLOSE = "tt9.popup_dialog.close"; public static final String PARAMETER_DIALOG_TYPE = "popup_type"; @@ -29,8 +31,20 @@ abstract public class PopupDialog { } } + @Override + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + return false; + } + protected void render(Runnable OKAction) { - UI.confirm(context, title, message, OKLabel, OKAction, true, () -> activityFinisher.accept(""), null); + new PopupBuilder(context) + .setCancelable(false) + .setTitle(title) + .setMessage(message) + .setPositiveButton(OKLabel, OKAction) + .setNegativeButton(true, () -> activityFinisher.accept("")) + .setOnKeyListener(this) + .show(); } abstract void render(); diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/PopupDialogActivity.java b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/PopupDialogActivity.java index eea326ed..d93ae32d 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/PopupDialogActivity.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/PopupDialogActivity.java @@ -6,6 +6,8 @@ import android.os.Bundle; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import java.util.HashMap; + import io.github.sspanak.tt9.ime.TraditionalT9; import io.github.sspanak.tt9.util.Logger; @@ -33,7 +35,8 @@ public class PopupDialogActivity extends AppCompatActivity { return switch (popupType) { case AddWordDialog.TYPE -> new AddWordDialog(this, i, this::onDialogClose); - case AutoUpdateMonologue.TYPE -> new AutoUpdateMonologue(this, i, this::onDialogClose); + case AutoUpdateMonolog.TYPE -> new AutoUpdateMonolog(this, i, this::onDialogClose); + case ChangeLanguageDialog.TYPE -> new ChangeLanguageDialog(this, i, this::onDialogClose); default -> { Logger.w(LOG_TAG, "Unknown popup type: '" + popupType + "'. Not displaying anything."); yield null; @@ -43,18 +46,30 @@ public class PopupDialogActivity extends AppCompatActivity { private void onDialogClose(String message) { finish(); - sendMessageToMain(message); - } - - private void sendMessageToMain(String message) { Intent intent = new Intent(this, TraditionalT9.class); - intent.putExtra(PopupDialog.INTENT_CLOSE, message); + if (message != null) { + intent.putExtra(PopupDialog.INTENT_CLOSE, message); + } startService(intent); } + private void onDialogClose(HashMap messages) { + finish(); + Intent intent = new Intent(this, TraditionalT9.class); + if (messages != null) { + intent.putExtra(PopupDialog.INTENT_CLOSE, ""); + for (String key : messages.keySet()) { + intent.putExtra(key, messages.get(key)); + } + } + + startService(intent); + } + + @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); - onDialogClose(null); + onDialogClose((String) null); } } diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/ThemedPopupDialog.java b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/ThemedPopupDialog.java new file mode 100644 index 00000000..5bad2f64 --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/ThemedPopupDialog.java @@ -0,0 +1,27 @@ +package io.github.sspanak.tt9.ui.dialogs; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import io.github.sspanak.tt9.R; +import io.github.sspanak.tt9.preferences.settings.SettingsStore; +import io.github.sspanak.tt9.util.ConsumerCompat; +import io.github.sspanak.tt9.util.ThemedContextBuilder; +import io.github.sspanak.tt9.util.sys.DeviceInfo; + +abstract class ThemedPopupDialog extends PopupDialog { + ThemedPopupDialog(@NonNull Context context, ConsumerCompat activityFinisher, int theme) { + super( + new ThemedContextBuilder() + .setConfiguration(context.getApplicationContext().getResources().getConfiguration()) + .setContext(context) + .setSettings(new SettingsStore(context)) + // The main theme does not work on Android <= 11 and the _AddWord theme does not work on 12+. + // Not sure why since they inherit from the same parent, but it is what it is. + .setTheme(DeviceInfo.AT_LEAST_ANDROID_12 ? R.style.TTheme : theme) + .build(), + activityFinisher + ); + } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/notifications/DictionaryUpdateNotification.java b/app/src/main/java/io/github/sspanak/tt9/ui/notifications/DictionaryUpdateNotification.java index 9e6677b7..3823e7f9 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/notifications/DictionaryUpdateNotification.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/notifications/DictionaryUpdateNotification.java @@ -10,7 +10,7 @@ import androidx.core.app.NotificationCompat; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.languages.Language; -import io.github.sspanak.tt9.ui.dialogs.AutoUpdateMonologue; +import io.github.sspanak.tt9.ui.dialogs.AutoUpdateMonolog; public class DictionaryUpdateNotification extends DictionaryNotification { private final Language language; @@ -25,7 +25,7 @@ public class DictionaryUpdateNotification extends DictionaryNotification { @Override protected PendingIntent createNavigationIntent(@NonNull Context context, @Nullable Language language) { - Intent intent = AutoUpdateMonologue.generateShowIntent(context, language != null ? language.getId() : -1); + Intent intent = AutoUpdateMonolog.generateShowIntent(context, language != null ? language.getId() : -1); return PendingIntent.getActivity(context, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } diff --git a/app/src/main/res/layout/popup_language_select.xml b/app/src/main/res/layout/popup_language_select.xml new file mode 100644 index 00000000..b7afefc1 --- /dev/null +++ b/app/src/main/res/layout/popup_language_select.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/app/src/main/res/layout/radio_button_language.xml b/app/src/main/res/layout/radio_button_language.xml new file mode 100644 index 00000000..340d1453 --- /dev/null +++ b/app/src/main/res/layout/radio_button_language.xml @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index a74f5e74..cfc1128d 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -31,6 +31,8 @@ Запомняне на двойки думи Запомняй често ползвани фрази, за по-точно подсказване на думи. Политика за поверителност + Бързо превключване на езиците + Натискането на клавиша за език автоматично превключва към следващия език, вместо да показва изскачащ списък с всички активирани езици. Икона за състояние Показвай икона, когато въвеждането с клавиатура е активно. Отмени зареждането @@ -194,6 +196,7 @@ Бързо изтриване Изтривай цели думи при задържане или плъзване на Backspace. (Не работи в някои приложения.) Ред на пунктуацията + Изберете език Интервал и нов ред ще бъдат автоматично добавени в началото на списъка. Липсва задължителен символ:%1$s Липсват задължителни символи:%1$s diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d77392fd..179c7a9a 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -47,6 +47,7 @@ Wortpaare lernen Häufig verwendete Phrasen merken, um die Genauigkeit der Vorschläge zu verbessern. Datenschutzerklärung + Durch Drücken der Sprachentaste wird automatisch zur nächsten Sprache gewechselt, anstatt ein Popup mit allen aktivierten Sprachen anzuzeigen. Statusicon Ein Icon anzeigen, wenn die Tastatureingabe aktiv ist. Schriftgröße der Vorschläge @@ -183,6 +184,7 @@ 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 + Sprache auswählen Leer- und Zeilenumbruchzeichen werden automatisch am Anfang der Liste hinzugefügt. Fehlendes erforderliches Zeichen:%1$s Fehlende erforderliche Zeichen:%1$s @@ -227,4 +229,5 @@ Nur Tastencodes 1–8 sind erlaubt. Eine Taste kann nicht in beiden Spalten platziert werden. Spalte 1 + Schneller Sprachwechsel diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index f57c45e9..9b7d6176 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -56,6 +56,7 @@ Aprender pares de palabras Recordar frases de uso común para mejorar las sugerencias de palabras. Política de privacidad + Al presionar la tecla de idioma, se cambia automáticamente al siguiente idioma en lugar de mostrar una ventana emergente con todos los idiomas habilitados. Icono de estado Mostrar un icono cuando la escritura esté activa. Cancelar la carga @@ -194,6 +195,7 @@ Eliminación rápida Borrar palabras completas manteniendo pulsado o deslizando Retroceso. (No compatible con algunas aplicaciones) Orden de puntuación + Seleccionar idioma 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 @@ -225,4 +227,5 @@ Solo se permiten los códigos de tecla del 1 al 8. Una tecla no puede colocarse en ambas columnas. Columna 1 + Cambio rápido de idioma diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6918df4c..1c1178d0 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -32,6 +32,7 @@ Mémoriser les paires de mots Apprendre des phrases couramment utilisées pour améliorer les suggestions. Politique de confidentialité + Appuyer sur la touche de langue passe automatiquement à la langue suivante au lieu d\'afficher une fenêtre contextuelle avec toutes les langues activées. Icône d\'état Afficher une icône lorsque la saisie au clavier est active Annuler le chargement @@ -192,6 +193,7 @@ 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 + Sélectionner la langue 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 @@ -225,4 +227,5 @@ Seuls les codes de touche 1 à 8 sont autorisés. Une touche ne peut pas être placée dans les deux colonnes. Colonne 1 + Changement rapide de langue diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index c95151a4..ab4fdbf9 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -50,6 +50,7 @@ Memorizzare coppie di parole Ricordare frasi comunemente usate per migliorare i suggerimenti di parole. Informativa sulla privacy + Premendo il tasto della lingua si passa automaticamente alla lingua successiva invece di mostrare un popup con tutte le lingue abilitate. Icona di stato Mostrare un\'icona quando la digitazione è attiva. Annullare il caricamento @@ -181,6 +182,7 @@ Cancellazione rapida Cancellare intere parole tenendo premuto o scorrendo Backspace. (Non supportato in alcune app) Ordine di punteggiatura + Seleziona la lingua I caratteri Spazio e Nuova linea verranno automaticamente aggiunti all\'inizio dell\'elenco. Carattere obbligatorio mancante:%1$s Caratteri obbligatori mancanti:%1$s @@ -228,5 +230,6 @@ Sono consentiti solo i codici dei tasti da 1 a 8. Un tasto non può essere inserito in entrambe le colonne. Colonna 1 + Cambio rapido lingua diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index e91c76f1..a399897c 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -62,6 +62,7 @@ ללמוד צמדי מילים לזכור ביטויים נפוצים כדי לשפר את דיוק ההצעות. מדיניות פרטיות + לחיצה על מקש השפה תעביר אוטומטית לשפה הבאה במקום להציג חלון קופץ עם כל השפות הפעילות. סמל מצב הצגת סמל כאשר קלט המקלדת פעיל. גודל גופן להצעות @@ -195,6 +196,7 @@ מחיקה מהירה למחוק מילים שלמות על ידי החזקת Backspace או החלקה עליו. (לא נתמך בחלק מהאפליקציות) סדר סימני פיסוק + בחר שפה תווי רווח ושורה חדשה יתווספו אוטומטית בתחילת הרשימה. תו חובה חסר: %1$s תווי חובה חסרים:%1$s @@ -232,4 +234,5 @@ רק קודי מקשים 1–8 מותרים. לא ניתן למקם מקש אחד בשתי העמודות. עמודה 1 + החלפה מהירה של שפות diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index e6bf7d71..67ebe7be 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -69,6 +69,7 @@ Išmokti žodžių poras Įsiminti dažnai naudojamas frazes, kad pagerintumėte žodžių pasiūlymus. Privatumo politika + Paspaudus kalbos klavišą, automatiškai perjungiama į kitą kalbą, užuot rodžius iškylantį langą su visomis įjungtomis kalbomis. Būsenos piktograma Rodyti piktogramą, kai aktyvus klaviatūros įvedimas Atšaukti įkėlimą @@ -203,6 +204,7 @@ Greitas ištrynimas Ištrinti visus žodžius laikant arba perbraukiant Backspace. (Nepalaikoma kai kuriose programėlėse) Skyrybos ženklų tvarka + Pasirinkite kalbą 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 @@ -234,4 +236,5 @@ Leidžiami tik klavišų kodai nuo 1 iki 8. Vieno klavišo negalima dėti į abu stulpelius. Stulpelis 1 + Greitas kalbos keitimas diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index e9f449db..2090d921 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -47,6 +47,7 @@ Woordenparen leren Veelgebruikte zinnen onthouden om de nauwkeurigheid van suggesties te verbeteren. Privacybeleid + Door op de taaltoets te drukken, schakelt u automatisch naar de volgende taal in plaats van een pop-up met alle ingeschakelde talen weer te geven. Statusicoon Een icoon tonen wanneer toetsenbordinvoer actief is. Lettergrootte van suggesties @@ -183,6 +184,7 @@ Snel verwijderen Hele woorden wissen door Backspace ingedrukt te houden of te vegen. (Niet ondersteund in sommige apps) Interpunctievolgorde + Selecteer taal Spatie- en nieuwe regeltekens worden automatisch aan het begin van de lijst toegevoegd. Ontbrekend verplicht teken:%1$s Ontbrekende verplichte tekens:%1$s @@ -226,4 +228,5 @@ Alleen toetscodes 1–8 zijn toegestaan. Eén toets kan niet in beide kolommen staan. Kolom 1 + Snel schakelen tussen talen diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 6bb07d9e..021ec51c 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -63,6 +63,7 @@ Aprender pares de palavras Lembrar de frases comumente usadas para melhorar as sugestões de palavras. Política de privacidade + Pressionar a tecla de idioma alterna automaticamente para o próximo idioma, em vez de mostrar um pop-up com todos os idiomas habilitados. Ícone de status Mostrar um ícone quando a digitação estiver ativa. Cancelar Carregamento @@ -196,6 +197,7 @@ Exclusão rápida Apagar palavras inteiras mantendo pressionado ou deslizando o Backspace. (Não suportado em alguns aplicativos) Ordem de pontuação + Selecione o idioma 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 @@ -232,4 +234,5 @@ Apenas os códigos de tecla de 1 a 8 são permitidos. Uma tecla não pode estar nas duas colunas. Coluna 1 + Alternância rápida de idioma diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index fd1c18c8..eec6cf35 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -32,6 +32,7 @@ Запоминать пары слов Запоминать часто используемые фразы для улучшения предложений слов. Политика конфиденциальности + Нажатие клавиши языка автоматически переключает на следующий язык вместо отображения всплывающего окна со всеми включенными языками. Иконка состояния Показывать иконку, когда активен режим ввода с клавиатуры. Отменить загрузку @@ -194,6 +195,7 @@ Быстрое удаление Стереть целые слова, удерживая или проведя по клавише Backspace. (Не поддерживается в некоторых приложениях) Порядок пунктуации + Выберите язык Пробел и символ новой строки будут автоматически добавлены в начало списка. Отсутствует обязательный символ:%1$s Отсутствуют обязательные символы:%1$s @@ -225,4 +227,5 @@ Разрешены только коды клавиш от 1 до 8. Одна клавиша не может находиться в обеих колонках. Столбец 1 + Быстрое переключение языков diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index e5fd8ee0..dfbb7a6d 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -48,6 +48,7 @@ Kelime çiftlerini öğren Öneri doğruluğunu artırmak için sık kullanılan ifadeleri hatırla. Gizlilik politikası + Dil tuşuna basıldığında, etkin diller listesini göstermek yerine otomatik olarak bir sonraki dile geçilir. Durum Klavye girişi etkin olduğunda bir simge göster. Yüklemeyi İptal Et @@ -196,6 +197,7 @@ Hızlı Silme Geri tuşunu basılı tutarak veya kaydırarak tüm kelimeleri sil. (Bazı uygulamalarda desteklenmez) Noktalama sırası + Dil Seçin Boşluk ve Yeni Satır karakterleri listenin başına otomatik olarak eklenecektir. Zorunlu karakter eksik:%1$s Zorunlu karakterler eksik:%1$s @@ -230,4 +232,5 @@ Yalnızca 1–8 arası tuş kodlarına izin verilir. Bir tuş her iki sütuna da yerleştirilemez. Sütun 1 + Hızlı dil geçişi diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index ee4c93c0..abd2b3b1 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -75,6 +75,7 @@ Запам’ятовувати пари слів Запам\'ятовувати часто вживані фрази для покращення пропозицій слів. Політика конфіденційності + Натискання клавіші мови автоматично перемикає на наступну мову замість відображення спливаючого вікна з усіма активними мовами. Іконка статусу Показати іконку, коли активне введення з клавіатури. Скасувати завантаження @@ -205,6 +206,7 @@ Швидке видалення Видалити цілі слова, утримуючи або провівши клавішею Backspace. (Не підтримується в деяких додатках) Порядок пунктуації + Виберіть мову Пробіл і символ нового рядка буде автоматично додано на початок списку. Відсутній обов’язковий символ:%1$s Відсутні обов’язкові символи:%1$s @@ -236,4 +238,5 @@ Дозволено лише коди клавіш від 1 до 8. Одна клавіша не може бути в обох стовпцях. Стовпець 1 + Швидке перемикання мов diff --git a/app/src/main/res/values-v31/styles.xml b/app/src/main/res/values-v31/styles.xml index 59483d84..e4a3dbfd 100644 --- a/app/src/main/res/values-v31/styles.xml +++ b/app/src/main/res/values-v31/styles.xml @@ -25,6 +25,25 @@ 8dp + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 55202acb..f4fbc165 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -102,6 +102,8 @@ Learn Word Pairs Remember commonly used phrases to improve the suggestions accuracy. Privacy Policy + Quick Switch Languages + Pressing the Language key switches to the next language automatically, instead of showing a popup with all enabled languages. Status Icon Show an icon when keyboard input is active. Suggestion Font Size @@ -201,6 +203,8 @@ Enable Languages words + Select Language + Space and Newline characters will be automatically added at the beginning of the list. Missing mandatory character:%1$s Missing mandatory characters:%1$s diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 7eda7cf7..a7ee848c 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -45,6 +45,45 @@ @color/keyboard_wrapper_top_separator + + + + + + + + + + diff --git a/app/src/main/res/xml/prefs_screen_languages.xml b/app/src/main/res/xml/prefs_screen_languages.xml index 9a037979..93a2cf59 100644 --- a/app/src/main/res/xml/prefs_screen_languages.xml +++ b/app/src/main/res/xml/prefs_screen_languages.xml @@ -29,6 +29,12 @@ app:key="dictionary_truncate" app:title="@string/dictionary_truncate_title" /> + +