1
0
Fork 0

significantly simpler language change dialog (no Activity hack)

This commit is contained in:
sspanak 2025-05-10 12:33:34 +03:00 committed by Dimo Karaivanov
parent 32b172827e
commit 18c2ab8440
9 changed files with 71 additions and 119 deletions

View file

@ -170,7 +170,7 @@ abstract public class CommandHandler extends TextEditingHandler {
protected void changeLang() { protected void changeLang() {
suggestionOps.cancelDelayedAccept(); suggestionOps.cancelDelayedAccept();
stopVoiceInput(); stopVoiceInput();
ChangeLanguageDialog.show(this, mInputMode.getSequence(), textField.getComposingText()); new ChangeLanguageDialog(getFinalContext(), this::setLang).show();
} }

View file

@ -11,7 +11,6 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnection;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.db.DataStore; 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.languages.LanguageCollection;
import io.github.sspanak.tt9.preferences.settings.SettingsStore; import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.ui.UI; 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.PopupDialog;
import io.github.sspanak.tt9.ui.dialogs.RequestPermissionDialog; import io.github.sspanak.tt9.ui.dialogs.RequestPermissionDialog;
import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.util.Logger;
@ -122,12 +120,6 @@ public class TraditionalT9 extends MainViewHandler {
if (!message.isEmpty()) { if (!message.isEmpty()) {
UI.toastLong(this, message); 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; 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 @Override
protected void onStop() { protected void onStop() {
stopVoiceInput(); stopVoiceInput();

View file

@ -5,12 +5,18 @@ import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.view.View; import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; 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; import io.github.sspanak.tt9.util.sys.DeviceInfo;
public class PopupBuilder { public class PopupBuilder {
private static final String LOG_TAG = PopupBuilder.class.getSimpleName();
private final Context context; private final Context context;
private MaterialAlertDialogBuilder builder12; private MaterialAlertDialogBuilder builder12;
private AlertDialog.Builder builderLegacy; private AlertDialog.Builder builderLegacy;
@ -112,4 +118,35 @@ public class PopupBuilder {
public Dialog show() { public Dialog show() {
return DeviceInfo.AT_LEAST_ANDROID_12 ? builder12.show() : builderLegacy.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:
* <a href="https://stackoverflow.com/questions/51906586/display-dialog-from-input-method-service-in-android-9-android-pie">Philipp</a>
* <a href="https://stackoverflow.com/questions/3494476/android-ime-how-to-show-a-pop-up-dialog/3508462#3508462">Maher Abuthraa</a>
*/
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;
}
} }

View file

@ -57,13 +57,13 @@ public class AddWordDialog extends ThemedPopupDialog {
@Override @Override
void render() { void show() {
if (message == null || word == null || word.isEmpty()) { if (message == null || word == null || word.isEmpty()) {
close(); close();
return; return;
} }
super.render(this::onOK); super.show(this::onOK);
} }

View file

@ -36,7 +36,7 @@ public class AutoUpdateMonolog extends PopupDialog {
@Override @Override
void render() { void show() {
if (language != null) { if (language != null) {
DictionaryLoader.load(context, language); DictionaryLoader.load(context, language);
} }

View file

@ -2,65 +2,55 @@ package io.github.sspanak.tt9.ui.dialogs;
import android.app.Dialog; import android.app.Dialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent;
import android.inputmethodservice.InputMethodService;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; import androidx.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import io.github.sspanak.tt9.R; 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.ime.helpers.Key;
import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.languages.LanguageCollection;
import io.github.sspanak.tt9.preferences.settings.SettingsStore; import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.ui.LanguageRadioButton; import io.github.sspanak.tt9.ui.LanguageRadioButton;
import io.github.sspanak.tt9.ui.PopupBuilder; 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.ConsumerCompat;
import io.github.sspanak.tt9.util.sys.DeviceInfo; import io.github.sspanak.tt9.util.sys.DeviceInfo;
public class ChangeLanguageDialog extends ThemedPopupDialog { 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<HashMap<String, String>> activityFinisher;
private final LayoutInflater inflater;
private final ArrayList<Language> languages; private final ArrayList<Language> languages;
private final MainView mainView;
private final SettingsStore settings; private final SettingsStore settings;
private final String currentSequence;
private final String currentWord;
private final ArrayList<LanguageRadioButton> radioButtonsCache = new ArrayList<>(); private final ConsumerCompat<Integer> onLanguageChanged;
private Dialog popup; private Dialog popup;
private final ArrayList<LanguageRadioButton> radioButtonsCache = new ArrayList<>();
ChangeLanguageDialog(@NonNull AppCompatActivity context, @NonNull Intent intent, ConsumerCompat<HashMap<String, String>> activityFinisher) { public ChangeLanguageDialog(@NonNull TraditionalT9 tt9, @Nullable ConsumerCompat<Integer> changeHandler) {
super(context, null, R.style.TTheme_AddWord); super(tt9, null, R.style.TTheme_AddWord);
this.activityFinisher = activityFinisher; title = tt9.getResources().getString(R.string.language_popup_title);
title = context.getResources().getString(R.string.language_popup_title);
OKLabel = null; OKLabel = null;
inflater = context.getLayoutInflater(); mainView = tt9.getMainView();
settings = new SettingsStore(context); settings = tt9.getSettings();
languages = LanguageCollection.getAll(settings.getEnabledLanguageIds(), true); languages = LanguageCollection.getAll(settings.getEnabledLanguageIds(), true);
onLanguageChanged = changeHandler;
currentSequence = intent.getStringExtra(PARAMETER_SEQUENCE);
currentWord = intent.getStringExtra(PARAMETER_WORD);
} }
private void onClick(View button) { 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; return false;
} }
changeLanguage(languageId); if (onLanguageChanged != null) {
onLanguageChanged.accept(languageId);
}
close();
return true; return true;
} }
@ -113,25 +106,6 @@ public class ChangeLanguageDialog extends ThemedPopupDialog {
popup.dismiss(); popup.dismiss();
popup = null; popup = null;
} }
activityFinisher.accept(null);
}
private void changeLanguage(int languageId) {
detachRadioButtons();
if (popup != null) {
popup.dismiss();
popup = null;
}
if (activityFinisher != null) {
HashMap<String, String> 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() { private View generateRadioButtons() {
final int currentLanguageId = settings.getInputLanguage(); 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); final LinearLayout radioGroup = view.findViewById(R.id.language_select_list);
radioButtonsCache.clear(); radioButtonsCache.clear();
@ -169,31 +143,14 @@ public class ChangeLanguageDialog extends ThemedPopupDialog {
} }
@Override public void show() {
void render() {
popup = new PopupBuilder(context) popup = new PopupBuilder(context)
.setCancelable(false) .setCancelable(true)
.setTitle(title) .setTitle(title)
.setMessage(message) .setMessage(message)
.setNegativeButton(true, this::close) .setNegativeButton(true, this::close)
.setOnKeyListener(this) .setOnKeyListener(this)
.setView(generateRadioButtons()) .setView(generateRadioButtons())
.show(); .showFromIme(mainView);
}
/**
* 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);
} }
} }

View file

@ -36,7 +36,7 @@ abstract public class PopupDialog implements DialogInterface.OnKeyListener {
return false; return false;
} }
protected void render(Runnable OKAction) { protected void show(Runnable OKAction) {
new PopupBuilder(context) new PopupBuilder(context)
.setCancelable(false) .setCancelable(false)
.setTitle(title) .setTitle(title)
@ -47,5 +47,5 @@ abstract public class PopupDialog implements DialogInterface.OnKeyListener {
.show(); .show();
} }
abstract void render(); abstract void show();
} }

View file

@ -6,8 +6,6 @@ import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import java.util.HashMap;
import io.github.sspanak.tt9.ime.TraditionalT9; import io.github.sspanak.tt9.ime.TraditionalT9;
import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.util.Logger;
@ -20,7 +18,7 @@ public class PopupDialogActivity extends AppCompatActivity {
super.onCreate(savedData); super.onCreate(savedData);
PopupDialog dialog = getDialog(); PopupDialog dialog = getDialog();
if (dialog != null) { if (dialog != null) {
dialog.render(); dialog.show();
} else { } else {
onDialogClose(""); onDialogClose("");
} }
@ -36,7 +34,6 @@ public class PopupDialogActivity extends AppCompatActivity {
return switch (popupType) { return switch (popupType) {
case AddWordDialog.TYPE -> new AddWordDialog(this, i, this::onDialogClose); case AddWordDialog.TYPE -> new AddWordDialog(this, i, this::onDialogClose);
case AutoUpdateMonolog.TYPE -> new AutoUpdateMonolog(this, i, this::onDialogClose); case AutoUpdateMonolog.TYPE -> new AutoUpdateMonolog(this, i, this::onDialogClose);
case ChangeLanguageDialog.TYPE -> new ChangeLanguageDialog(this, i, this::onDialogClose);
default -> { default -> {
Logger.w(LOG_TAG, "Unknown popup type: '" + popupType + "'. Not displaying anything."); Logger.w(LOG_TAG, "Unknown popup type: '" + popupType + "'. Not displaying anything.");
yield null; yield null;
@ -53,23 +50,10 @@ public class PopupDialogActivity extends AppCompatActivity {
startService(intent); startService(intent);
} }
private void onDialogClose(HashMap<String, String> 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 @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults);
onDialogClose((String) null); onDialogClose(null);
} }
} }

View file

@ -37,8 +37,8 @@
</style> </style>
<style name="TTheme.LanguageSelect.RadioButton.Label"> <style name="TTheme.LanguageSelect.RadioButton.Label">
<item name="android:paddingTop">0dp</item> <item name="android:paddingTop">12dp</item>
<item name="android:paddingBottom">0dp</item> <item name="android:paddingBottom">12dp</item>
<item name="android:paddingStart">0dp</item> <item name="android:paddingStart">0dp</item>
<item name="android:paddingEnd">0dp</item> <item name="android:paddingEnd">0dp</item>
<item name="android:textAppearance">@style/TextAppearance.AppCompat.Widget.PopupMenu.Large</item> <item name="android:textAppearance">@style/TextAppearance.AppCompat.Widget.PopupMenu.Large</item>