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 746939aa..c178845e 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 @@ -170,7 +170,7 @@ abstract public class CommandHandler extends TextEditingHandler { protected void changeLang() { suggestionOps.cancelDelayedAccept(); stopVoiceInput(); - ChangeLanguageDialog.show(this, mInputMode.getSequence(), textField.getComposingText()); + new ChangeLanguageDialog(getFinalContext(), this::setLang).show(); } 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 022be18e..1f3eeb60 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 @@ -11,7 +11,6 @@ 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; @@ -21,7 +20,6 @@ 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.ui.dialogs.RequestPermissionDialog; import io.github.sspanak.tt9.util.Logger; @@ -122,12 +120,6 @@ public class TraditionalT9 extends MainViewHandler { 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; @@ -184,24 +176,6 @@ 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/ui/PopupBuilder.java b/app/src/main/java/io/github/sspanak/tt9/ui/PopupBuilder.java index 214e29a2..9b803686 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/PopupBuilder.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/PopupBuilder.java @@ -5,12 +5,18 @@ import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.view.View; +import android.view.Window; +import android.view.WindowManager; import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import io.github.sspanak.tt9.ui.main.MainView; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.util.sys.DeviceInfo; public class PopupBuilder { + private static final String LOG_TAG = PopupBuilder.class.getSimpleName(); + private final Context context; private MaterialAlertDialogBuilder builder12; private AlertDialog.Builder builderLegacy; @@ -112,4 +118,35 @@ public class PopupBuilder { public Dialog show() { return DeviceInfo.AT_LEAST_ANDROID_12 ? builder12.show() : builderLegacy.show(); } + + + /** + * In IME context, it is not that easy to show a popup dialog. We need to make it "valid" using + * the hacks below. Made possible thanks to: + * Philipp + * Maher Abuthraa + */ + public Dialog showFromIme(MainView main) { + if (main == null || main.getView() == null) { + Logger.e(LOG_TAG, "Cannot show a popup dialog. Main view is null."); + return null; + } + + Dialog dialog = DeviceInfo.AT_LEAST_ANDROID_12 ? builder12.create() : builderLegacy.create(); + + Window window = dialog.getWindow(); + if (window == null) { + Logger.e(LOG_TAG, "Cannot show a popup dialog. AlertDialog generated a Dialog with NULL Window."); + return null; + } + + WindowManager.LayoutParams layout = window.getAttributes(); + layout.token = main.getView().getWindowToken(); + layout.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; + window.setAttributes(layout); + window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); + dialog.show(); + + return dialog; + } } 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 3c2da68a..a4719d36 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 @@ -57,13 +57,13 @@ public class AddWordDialog extends ThemedPopupDialog { @Override - void render() { + void show() { if (message == null || word == null || word.isEmpty()) { close(); return; } - super.render(this::onOK); + super.show(this::onOK); } diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AutoUpdateMonolog.java b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AutoUpdateMonolog.java index 2ce110c9..ede485c1 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AutoUpdateMonolog.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AutoUpdateMonolog.java @@ -36,7 +36,7 @@ public class AutoUpdateMonolog extends PopupDialog { @Override - void render() { + void show() { if (language != null) { DictionaryLoader.load(context, language); } 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 index 439d4ed9..a38de1fd 100644 --- 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 @@ -2,65 +2,55 @@ 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 androidx.annotation.Nullable; import java.util.ArrayList; -import java.util.HashMap; import io.github.sspanak.tt9.R; +import io.github.sspanak.tt9.ime.TraditionalT9; 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.ui.main.MainView; 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 MainView mainView; private final SettingsStore settings; - private final String currentSequence; - private final String currentWord; - private final ArrayList radioButtonsCache = new ArrayList<>(); + private final ConsumerCompat onLanguageChanged; private Dialog popup; + private final ArrayList radioButtonsCache = new ArrayList<>(); - ChangeLanguageDialog(@NonNull AppCompatActivity context, @NonNull Intent intent, ConsumerCompat> activityFinisher) { - super(context, null, R.style.TTheme_AddWord); + public ChangeLanguageDialog(@NonNull TraditionalT9 tt9, @Nullable ConsumerCompat changeHandler) { + super(tt9, null, R.style.TTheme_AddWord); - this.activityFinisher = activityFinisher; - title = context.getResources().getString(R.string.language_popup_title); + title = tt9.getResources().getString(R.string.language_popup_title); OKLabel = null; - inflater = context.getLayoutInflater(); - settings = new SettingsStore(context); + mainView = tt9.getMainView(); + settings = tt9.getSettings(); languages = LanguageCollection.getAll(settings.getEnabledLanguageIds(), true); - - currentSequence = intent.getStringExtra(PARAMETER_SEQUENCE); - currentWord = intent.getStringExtra(PARAMETER_WORD); + onLanguageChanged = changeHandler; } private void onClick(View button) { - changeLanguage(button.getId()); + if (onLanguageChanged != null) { + onLanguageChanged.accept(button.getId()); + } + close(); } @@ -85,7 +75,10 @@ public class ChangeLanguageDialog extends ThemedPopupDialog { return false; } - changeLanguage(languageId); + if (onLanguageChanged != null) { + onLanguageChanged.accept(languageId); + } + close(); return true; } @@ -113,25 +106,6 @@ public class ChangeLanguageDialog extends ThemedPopupDialog { 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); - } } @@ -148,7 +122,7 @@ public class ChangeLanguageDialog extends ThemedPopupDialog { private View generateRadioButtons() { final int currentLanguageId = settings.getInputLanguage(); - final View view = inflater.inflate(R.layout.popup_language_select, null); + final View view = View.inflate(context, R.layout.popup_language_select, null); final LinearLayout radioGroup = view.findViewById(R.id.language_select_list); radioButtonsCache.clear(); @@ -169,31 +143,14 @@ public class ChangeLanguageDialog extends ThemedPopupDialog { } - @Override - void render() { + public void show() { popup = new PopupBuilder(context) - .setCancelable(false) + .setCancelable(true) .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); + .showFromIme(mainView); } } 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 6e04e7e1..84e6adee 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 @@ -36,7 +36,7 @@ abstract public class PopupDialog implements DialogInterface.OnKeyListener { return false; } - protected void render(Runnable OKAction) { + protected void show(Runnable OKAction) { new PopupBuilder(context) .setCancelable(false) .setTitle(title) @@ -47,5 +47,5 @@ abstract public class PopupDialog implements DialogInterface.OnKeyListener { .show(); } - abstract void render(); + abstract void show(); } 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 d93ae32d..59f7aa02 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,8 +6,6 @@ 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; @@ -20,7 +18,7 @@ public class PopupDialogActivity extends AppCompatActivity { super.onCreate(savedData); PopupDialog dialog = getDialog(); if (dialog != null) { - dialog.render(); + dialog.show(); } else { onDialogClose(""); } @@ -36,7 +34,6 @@ public class PopupDialogActivity extends AppCompatActivity { return switch (popupType) { case AddWordDialog.TYPE -> new AddWordDialog(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; @@ -53,23 +50,10 @@ public class PopupDialogActivity extends AppCompatActivity { 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((String) null); + onDialogClose(null); } } diff --git a/app/src/main/res/values-v31/styles.xml b/app/src/main/res/values-v31/styles.xml index e4a3dbfd..5ce08f8e 100644 --- a/app/src/main/res/values-v31/styles.xml +++ b/app/src/main/res/values-v31/styles.xml @@ -37,8 +37,8 @@