1
0
Fork 0

fixed unexpected text reset when starting to type in a new input field, caused by multiple IME restarts in the same field, causing InputMode re-creation; also reduced the InputMode recreations in general

This commit is contained in:
sspanak 2024-04-02 17:34:21 +03:00 committed by Dimo Karaivanov
parent d9b620790b
commit caad8a5446
5 changed files with 55 additions and 31 deletions

View file

@ -19,7 +19,7 @@ abstract public class AbstractHandler extends InputMethodService {
// helpers // helpers
abstract public SettingsStore getSettings(); abstract public SettingsStore getSettings();
abstract protected void onInit(); abstract protected void onInit();
abstract protected void onStart(InputConnection inputConnection, EditorInfo inputField); abstract protected boolean onStart(InputConnection inputConnection, EditorInfo inputField);
abstract protected void onFinishTyping(); abstract protected void onFinishTyping();
abstract protected void onStop(); abstract protected void onStop();
abstract protected void setInputField(InputConnection inputConnection, EditorInfo inputField); abstract protected void setInputField(InputConnection inputConnection, EditorInfo inputField);

View file

@ -29,9 +29,9 @@ public abstract class HotkeyHandler extends TypingHandler {
@Override @Override
protected void onStart(InputConnection connection, EditorInfo field) { protected boolean onStart(InputConnection connection, EditorInfo field) {
super.onStart(connection, field);
isSystemRTL = LanguageKind.isRTL(LanguageCollection.getDefault(this)); isSystemRTL = LanguageKind.isRTL(LanguageCollection.getDefault(this));
return super.onStart(connection, field);
} }

View file

@ -13,6 +13,7 @@ import androidx.annotation.NonNull;
import io.github.sspanak.tt9.db.DictionaryLoader; import io.github.sspanak.tt9.db.DictionaryLoader;
import io.github.sspanak.tt9.db.WordStoreAsync; import io.github.sspanak.tt9.db.WordStoreAsync;
import io.github.sspanak.tt9.ime.modes.InputMode;
import io.github.sspanak.tt9.ime.modes.ModePassthrough; import io.github.sspanak.tt9.ime.modes.ModePassthrough;
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;
@ -85,22 +86,22 @@ public class TraditionalT9 extends HotkeyHandler {
@Override @Override
protected void onStart(InputConnection connection, EditorInfo field) { protected boolean onStart(InputConnection connection, EditorInfo field) {
Logger.setLevel(settings.getLogLevel()); if (!super.onStart(connection, field)) {
return false;
super.onStart(connection, field);
if (mInputMode.isPassthrough()) {
// When the input is invalid or simple, let Android handle it.
onStop();
updateInputViewShown();
return;
} }
normalizationHandler.removeCallbacksAndMessages(null); Logger.setLevel(settings.getLogLevel());
if (mInputMode.isPassthrough()) {
onStop();
} else {
normalizationHandler.removeCallbacksAndMessages(null);
initUi();
}
initUi();
updateInputViewShown(); updateInputViewShown();
return true;
} }
@ -154,11 +155,11 @@ public class TraditionalT9 extends HotkeyHandler {
* Some applications may hide our window and it remains invisible until the screen is touched or OK is pressed. * Some applications may hide our window and it remains invisible until the screen is touched or OK is pressed.
* This is fine for touchscreen keyboards, but the hardware keyboard allows typing even when the window and the suggestions * This is fine for touchscreen keyboards, but the hardware keyboard allows typing even when the window and the suggestions
* are invisible. This function forces the InputMethodManager to show our window. * are invisible. This function forces the InputMethodManager to show our window.
* WARNING! While this is running, it is not possible to load or display suggestions, * WARNING! Calling this may cause a restart, which will cause InputMode to be recreated. Depending
* or change the composing text. Use with care, after all processing is done. * on how much time the restart takes, this may erase the current user input.
*/ */
protected void forceShowWindowIfHidden() { protected void forceShowWindowIfHidden() {
if (getInputMode().isPassthrough() || isInputViewShown() || settings.isMainLayoutStealth()) { if (getInputModeId() == InputMode.MODE_PASSTHROUGH || isInputViewShown() || settings.isMainLayoutStealth()) {
return; return;
} }
@ -188,7 +189,7 @@ public class TraditionalT9 extends HotkeyHandler {
@Override @Override
protected boolean shouldBeVisible() { protected boolean shouldBeVisible() {
return !getInputMode().isPassthrough(); return getInputModeId() != InputMode.MODE_PASSTHROUGH;
} }

View file

@ -20,7 +20,6 @@ import io.github.sspanak.tt9.ime.modes.ModePredictive;
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.ui.UI; import io.github.sspanak.tt9.ui.UI;
import io.github.sspanak.tt9.util.Logger;
import io.github.sspanak.tt9.util.Text; import io.github.sspanak.tt9.util.Text;
public abstract class TypingHandler extends KeyPadHandler { public abstract class TypingHandler extends KeyPadHandler {
@ -46,7 +45,13 @@ public abstract class TypingHandler extends KeyPadHandler {
} }
@Override @Override
protected void onStart(InputConnection connection, EditorInfo field) { protected boolean onStart(InputConnection connection, EditorInfo field) {
// ignore multiple calls for the same field, caused by showWindow()
// or weirdly functioning apps, such as the Qin SMS app
if (textField.equals(connection, field)) {
return false;
}
setInputField(connection, field); setInputField(connection, field);
// in case we are back from Settings screen, update the language list // in case we are back from Settings screen, update the language list
@ -58,10 +63,11 @@ public abstract class TypingHandler extends KeyPadHandler {
mInputMode = getInputMode(); mInputMode = getInputMode();
determineTextCase(); determineTextCase();
suggestionOps.setTextField(textField);
suggestionOps.set(null); suggestionOps.set(null);
appHacks = new AppHacks(settings, connection, field, textField); appHacks = new AppHacks(settings, connection, field, textField);
return true;
} }
@ -69,6 +75,7 @@ public abstract class TypingHandler extends KeyPadHandler {
currentInputConnection = connection; currentInputConnection = connection;
inputType = new InputType(currentInputConnection, field); inputType = new InputType(currentInputConnection, field);
textField = new TextField(currentInputConnection, field); textField = new TextField(currentInputConnection, field);
suggestionOps.setTextField(textField);
} }
@ -84,6 +91,7 @@ public abstract class TypingHandler extends KeyPadHandler {
protected void onFinishTyping() { protected void onFinishTyping() {
suggestionOps.cancelDelayedAccept(); suggestionOps.cancelDelayedAccept();
mInputMode = InputMode.getInstance(null, null, null, InputMode.MODE_PASSTHROUGH); mInputMode = InputMode.getInstance(null, null, null, InputMode.MODE_PASSTHROUGH);
setInputField(null, null);
} }
@ -94,7 +102,6 @@ public abstract class TypingHandler extends KeyPadHandler {
// 3. Some app may need special treatment, so let it be. // 3. Some app may need special treatment, so let it be.
boolean noTextBeforeCursor = textField.getStringBeforeCursor(1).isEmpty(); boolean noTextBeforeCursor = textField.getStringBeforeCursor(1).isEmpty();
if (mInputMode.isPassthrough() || appHacks.onBackspace(mInputMode) || noTextBeforeCursor) { if (mInputMode.isPassthrough() || appHacks.onBackspace(mInputMode) || noTextBeforeCursor) {
Logger.d("onBackspace", "backspace ignored");
mInputMode.reset(); mInputMode.reset();
return false; return false;
} }
@ -109,7 +116,6 @@ public abstract class TypingHandler extends KeyPadHandler {
super.sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); super.sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
} }
Logger.d("onBackspace", "backspace handled");
return true; return true;
} }
@ -207,18 +213,28 @@ public abstract class TypingHandler extends KeyPadHandler {
/** /**
* getInputMode * getInputModeId
* Load the last input mode or choose a more appropriate one. * Return the last input mode ID or choose a more appropriate one.
* Some input fields support only numbers or are not suited for predictions (e.g. password fields) * Some input fields support only numbers or are not suited for predictions (e.g. password fields).
* Others do not support text retrieval or composing text, or the AppHacks detected them as incompatible with us.
* We do not want to handle any of these, hence we pass through all input to the system.
*/ */
protected InputMode getInputMode() { protected int getInputModeId() {
if (!inputType.isValid() || (inputType.isLimited() && !appHacks.isTermux())) { if (!inputType.isValid() || (inputType.isLimited() && !appHacks.isTermux())) {
return InputMode.getInstance(settings, mLanguage, inputType, InputMode.MODE_PASSTHROUGH); return InputMode.MODE_PASSTHROUGH;
} }
allowedInputModes = textField.determineInputModes(inputType); allowedInputModes = textField.determineInputModes(inputType);
int validModeId = InputModeValidator.validateMode(settings.getInputMode(), allowedInputModes); return InputModeValidator.validateMode(settings.getInputMode(), allowedInputModes);
return InputMode.getInstance(settings, mLanguage, inputType, validModeId); }
/**
* getInputMode
* Same as getInputModeId(), but returns an actual InputMode.
*/
protected InputMode getInputMode() {
return InputMode.getInstance(settings, mLanguage, inputType, getInputModeId());
} }

View file

@ -34,6 +34,13 @@ public class TextField {
} }
public boolean equals(InputConnection inputConnection, EditorInfo inputField) {
return
connection != null && connection == inputConnection
&& field != null && field == inputField;
}
public boolean isThereText() { public boolean isThereText() {
if (connection == null) { if (connection == null) {
return false; return false;