Improved OK/Enter key handling
* pass through the key press to the system when there is nothing to do * respect the app request for: DONE action + NO_ENTER flag and send the appropriate one, instead of always sending ENTER
This commit is contained in:
parent
25d62bba43
commit
1edda847b7
5 changed files with 59 additions and 74 deletions
|
|
@ -45,11 +45,11 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean onHotkey(int keyCode, boolean repeat, boolean validateOnly) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -193,14 +166,12 @@ 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);
|
||||
|
|
@ -208,9 +179,6 @@ public class AppHacks {
|
|||
sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB, true);
|
||||
sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB, true);
|
||||
sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB, true);
|
||||
} else {
|
||||
inputConnection.commitText("\n", 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Integer, Boolean> 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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue