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 8c22da64..235d8791 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 @@ -45,10 +45,10 @@ public abstract class HotkeyHandler extends TypingHandler { if (suggestionOps.isEmpty()) { int action = textField.getAction(); return action == TextField.IME_ACTION_ENTER ? appHacks.onEnter() : textField.performAction(action); + } else { + onAcceptSuggestionManually(suggestionOps.acceptCurrent(), KeyEvent.KEYCODE_ENTER); + return true; } - - onAcceptSuggestionManually(suggestionOps.acceptCurrent(), KeyEvent.KEYCODE_ENTER); - return true; } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/KeyPadHandler.java b/app/src/main/java/io/github/sspanak/tt9/ime/KeyPadHandler.java index c587c935..b69b047e 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/KeyPadHandler.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/KeyPadHandler.java @@ -18,8 +18,6 @@ abstract class KeyPadHandler extends AbstractHandler { private final static String DEBOUNCE_TIMER = "debounce_"; // temporal key handling - private boolean isBackspaceHandled = false; - private int ignoreNextKeyUp = 0; private int lastKeyCode = 0; @@ -78,7 +76,7 @@ abstract class KeyPadHandler extends AbstractHandler { public void onStartInput(EditorInfo inputField, boolean restarting) { Logger.d( "KeyPadHandler", - "===> Start Up; packageName: " + inputField.packageName + " inputType: " + inputField.inputType + " fieldId: " + inputField.fieldId + " fieldName: " + inputField.fieldName + " privateImeOptions: " + inputField.privateImeOptions + " imeOptions: " + inputField.imeOptions + " extras: " + inputField.extras + "===> Start Up; packageName: " + inputField.packageName + " inputType: " + inputField.inputType + " actionId: " + inputField.actionId + " imeOptions: " + inputField.imeOptions + " privateImeOptions: " + inputField.privateImeOptions + " extras: " + inputField.extras ); onStart(getCurrentInputConnection(), inputField); } @@ -133,9 +131,9 @@ abstract class KeyPadHandler extends AbstractHandler { // "backspace" key must repeat its function when held down, so we handle it in a special way if (Key.isBackspace(settings, keyCode)) { if (onBackspace()) { - return isBackspaceHandled = true; + return Key.setHandled(KeyEvent.KEYCODE_DEL, true); } else { - isBackspaceHandled = false; + Key.setHandled(KeyEvent.KEYCODE_DEL, false); } } @@ -153,7 +151,7 @@ abstract class KeyPadHandler extends AbstractHandler { } return - Key.isOK(keyCode) + Key.setHandled(KeyEvent.KEYCODE_ENTER, Key.isOK(keyCode) && onOK()) || handleHotkey(keyCode, true, false, true) // hold a hotkey, handled in onKeyLongPress()) || handleHotkey(keyCode, false, keyRepeatCounter + 1 > 0, true) // press a hotkey, handled in onKeyUp() || Key.isPoundOrStar(keyCode) && onText(String.valueOf((char) event.getUnicodeChar()), true) @@ -219,7 +217,7 @@ abstract class KeyPadHandler extends AbstractHandler { return false; } - // Logger.d("onKeyUp", "Key: " + keyCode + " repeat?: " + event.getRepeatCount()); +// Logger.d("onKeyUp", "Key: " + keyCode + " repeat?: " + event.getRepeatCount()); if (keyCode == ignoreNextKeyUp) { // Logger.d("onKeyUp", "Ignored: " + keyCode); @@ -227,7 +225,7 @@ abstract class KeyPadHandler extends AbstractHandler { return true; } - if (Key.isBackspace(settings, keyCode) && isBackspaceHandled) { + if (Key.isBackspace(settings, keyCode) && Key.isHandled(KeyEvent.KEYCODE_DEL)) { return true; } @@ -245,7 +243,7 @@ abstract class KeyPadHandler extends AbstractHandler { } return - Key.isOK(keyCode) && onOK() + (Key.isOK(KeyEvent.KEYCODE_ENTER) && Key.isHandled(keyCode)) || handleHotkey(keyCode, false, keyRepeatCounter > 0, false) || Key.isPoundOrStar(keyCode) && onText(String.valueOf((char) event.getUnicodeChar()), false) || super.onKeyUp(keyCode, event); // let the system handle the keys we don't care about (usually, the touch "buttons") diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/AppHacks.java b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/AppHacks.java index 1999eef7..81bc5c78 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/AppHacks.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/AppHacks.java @@ -59,6 +59,13 @@ public class AppHacks { } + private boolean isMultilineTextField() { + return + editorInfo != null + && (editorInfo.inputType & TextField.TYPE_MULTILINE_TEXT) == TextField.TYPE_MULTILINE_TEXT; + } + + private boolean isGoogleChat() { return isAppField( "com.google.android.apps.dynamite", @@ -111,80 +118,46 @@ public class AppHacks { /** * onEnter - * Tries to guess and send the correct confirmation key code or sequence of key codes, depending on the connected application - * and input field. On invalid connection or field, it does nothing. - * This hack applies to all applications, not only selected ones. + * Tries to guess and send the correct confirmation key code or sequence of key codes, + * depending on the connected application and input field. On invalid connection or field, + * it does nothing. */ public boolean onEnter() { if (isTermux()) { sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER); return true; - } else if (isMessenger()) { + } else if (settings.getFbMessengerHack() && isMessenger()) { return onEnterFbMessenger(); - } else if (isGoogleChat()) { + } else if (settings.getGoogleChatHack() && isGoogleChat()) { return onEnterGoogleChat(); + } else if (isMultilineTextField()) { + return onEnterMultilineText(); } - return onEnterDefault(); + return false; } /** - * onEnterDefault - * This is the default "ENTER" routine for most applications that support send-with-enter functionality. It will attempt to - * guess and send the correct confirmation key code, be it "ENTER" or "DPAD_CENTER". - * On invalid textField, it does nothing. + * In generic text fields, or when no hacks are in effect, we just type a new line, + * as one would expect when pressing ENTER. */ - private boolean onEnterDefault() { - if (textField == null) { - return false; - } - - String oldText = textField.getStringBeforeCursor() + textField.getStringAfterCursor(); - - sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_CENTER); - - // If there is no text, there is nothing to send, so there is no need to attempt any hacks. - // We just pass through DPAD_CENTER and finish as if the key press was handled by the system. - if (oldText.isEmpty()) { - return true; - } - - try { - // In Android there is no strictly defined confirmation key, hence DPAD_CENTER may have done nothing. - // If so, send an alternative key code as a final resort. - Thread.sleep(80); - String newText = textField.getStringBeforeCursor() + textField.getStringAfterCursor(); - if (newText.equals(oldText)) { - sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER); - } - } catch (InterruptedException e) { - // This thread got interrupted. Assume it's because the connected application has taken an action - // after receiving DPAD_CENTER, so we don't need to do anything else. - return true; - } - - return true; + private boolean onEnterMultilineText() { + return inputConnection != null && inputConnection.commitText("\n", 1); } /** * onEnterFbMessenger * Messenger responds only to ENTER, but not DPAD_CENTER, so we make sure to send the correct code, - * no matter how the hardware key is implemented. In case the hack is disabled, we just type a new line, - * as one would expect. + * no matter how the hardware key is implemented. */ private boolean onEnterFbMessenger() { if (inputConnection == null || textField == null || !textField.isThereText()) { return false; } - if (settings.getFbMessengerHack()) { - sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER); - } else { - // in case the setting is disabled, just type a new line as one would expect - inputConnection.commitText("\n", 1); - } + sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER); return true; } @@ -193,24 +166,19 @@ public class AppHacks { * onEnterGoogleChat * Google Chat does not seem to respond consistently to ENTER. So we trick it by selecting * the send button it, then going back to the text field, so that one can continue typing. - * If the hack is disabled, we just type a new line. */ private boolean onEnterGoogleChat() { if (inputConnection == null || textField == null || !textField.isThereText()) { return false; } - if (settings.getGoogleChatHack()) { - sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB); - sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB); - sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER); - sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB, true); - sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB, true); - sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB, true); - sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB, true); - } else { - inputConnection.commitText("\n", 1); - } + sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB); + sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB); + sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER); + sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB, true); + sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB, true); + sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB, true); + sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB, true); return true; } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/Key.java b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/Key.java index 7a433fd9..db3b24fb 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/Key.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/Key.java @@ -2,9 +2,25 @@ package io.github.sspanak.tt9.ime.helpers; import android.view.KeyEvent; +import java.util.HashMap; + import io.github.sspanak.tt9.preferences.SettingsStore; public class Key { + private static final HashMap handledKeys = new HashMap<>(); + + + public static boolean setHandled(int keyCode, boolean handled) { + handledKeys.put(keyCode, handled); + return handled; + } + + + public static boolean isHandled(int keyCode) { + return Boolean.TRUE.equals(handledKeys.get(keyCode)); + } + + public static boolean isBackspace(SettingsStore settings, int keyCode) { return keyCode == KeyEvent.KEYCODE_DEL diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/TextField.java b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/TextField.java index 2e72fb4c..27bd1eac 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/TextField.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/TextField.java @@ -21,10 +21,12 @@ import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.util.Text; public class TextField { + public static final int TYPE_MULTILINE_TEXT = EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; public static final int IME_ACTION_ENTER = EditorInfo.IME_MASK_ACTION + 1; - public final InputConnection connection; - public final EditorInfo field; + private final InputConnection connection; + private final EditorInfo field; + public TextField(InputConnection inputConnection, EditorInfo inputField) { connection = inputConnection; @@ -342,6 +344,7 @@ public class TextField { int standardAction = field.imeOptions & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION); switch (standardAction) { case EditorInfo.IME_ACTION_GO: + case EditorInfo.IME_ACTION_DONE: case EditorInfo.IME_ACTION_NEXT: case EditorInfo.IME_ACTION_PREVIOUS: case EditorInfo.IME_ACTION_SEARCH: