summaryrefslogtreecommitdiff
path: root/app/src/main/java/com/javispedro/vndroid/KeyEventOutput.java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/com/javispedro/vndroid/KeyEventOutput.java')
-rw-r--r--app/src/main/java/com/javispedro/vndroid/KeyEventOutput.java286
1 files changed, 286 insertions, 0 deletions
diff --git a/app/src/main/java/com/javispedro/vndroid/KeyEventOutput.java b/app/src/main/java/com/javispedro/vndroid/KeyEventOutput.java
new file mode 100644
index 0000000..597942a
--- /dev/null
+++ b/app/src/main/java/com/javispedro/vndroid/KeyEventOutput.java
@@ -0,0 +1,286 @@
+package com.javispedro.vndroid;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.util.Log;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.javispedro.vndroid.keymaps.KeyActionHandler;
+import com.javispedro.vndroid.keymaps.KeyHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class KeyEventOutput implements KeyActionHandler {
+ private final String TAG = KeyEventOutput.class.getSimpleName();
+
+ private final List<KeyHandler> handlers = new ArrayList<KeyHandler>();
+
+ @Nullable
+ private AccessibilityNodeInfo getFocusNode() {
+ ControlService service = ControlService.getInstance();
+ if (service != null) {
+ return service.findFocus(AccessibilityNodeInfo.FOCUS_INPUT);
+ }
+ return null;
+ }
+
+ public void addHandler(KeyHandler handler) {
+ handler.setActionHandler(this);
+ handlers.add(handler);
+ }
+
+ public void postKeyEvent(int key, boolean state) {
+ if (state) Log.d(TAG, "keysym pressed: " + Integer.toHexString(key));
+ for (KeyHandler handler : handlers) {
+ boolean handled;
+ if (state) {
+ handled = handler.keyDown(key);
+ } else {
+ handled = handler.keyUp(key);
+ }
+ if (handled) {
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void postText(CharSequence text) {
+ AccessibilityNodeInfo node = getFocusNode();
+ if (node == null || !node.isEditable()) {
+ Log.d(TAG, "no input focus or not editable");
+ return;
+ }
+
+ final CharSequence curText = node.getText();
+ final int curTextLen = curText != null ? curText.length() : 0;
+ final int textLen = text.length();
+ int selStart = node.getTextSelectionStart();
+ int selEnd = node.getTextSelectionEnd();
+ StringBuilder builder = new StringBuilder(curTextLen + textLen);
+
+ Log.d(TAG, " cur text: " + curText + " start=" + selStart + " end=" + selEnd);
+
+ if (selStart == -1 || selEnd == -1 || curTextLen == 0) {
+ // No selection, no cursor
+ if (curTextLen > 0) builder.append(curText);
+ builder.append(text);
+ } else {
+ builder.append(curText.subSequence(0, selStart));
+ builder.append(text);
+ builder.append(curText.subSequence(selEnd, curText.length()));
+
+ selStart = selEnd + textLen;
+ selEnd = selStart;
+ }
+
+ Log.d(TAG, " new text: " + builder.toString() + " start=" + selStart + " end=" + selEnd);
+
+ Bundle args = new Bundle();
+ args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
+ builder.toString());
+ node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args);
+
+ if (selStart != -1 && selEnd != -1 && selStart != builder.length()) {
+ args = new Bundle();
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, selStart);
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, selEnd);
+ node.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, args);
+ }
+ }
+
+ @Override
+ public void postChar(char c) {
+ AccessibilityNodeInfo node = getFocusNode();
+ if (node == null || !node.isEditable()) {
+ Log.d(TAG, "no input focus or not editable");
+ return;
+ }
+
+ final CharSequence curText = node.getText();
+ final int curTextLen = curText != null ? curText.length() : 0;
+ int selStart = node.getTextSelectionStart();
+ int selEnd = node.getTextSelectionEnd();
+ StringBuilder builder = new StringBuilder(curTextLen + 1);
+
+ Log.d(TAG, " cur text: " + curText + " start=" + selStart + " end=" + selEnd);
+
+ if (selStart == -1 || selEnd == -1 || curTextLen == 0) {
+ // No selection, no cursor
+ if (curTextLen > 0) builder.append(curText);
+ builder.append(c);
+ } else {
+ builder.append(curText.subSequence(0, selStart));
+ builder.append(c);
+ builder.append(curText.subSequence(selEnd, curText.length()));
+
+ selStart = selEnd + 1;
+ selEnd = selStart;
+ }
+
+ Log.d(TAG, " new text: " + builder.toString() + " start=" + selStart + " end=" + selEnd);
+
+ Bundle args = new Bundle();
+ args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
+ builder.toString());
+ node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args);
+
+ if (selStart != -1 && selEnd != -1 && selStart != builder.length()) {
+ args = new Bundle();
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, selStart);
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, selEnd);
+ node.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, args);
+ }
+ }
+
+ @Override
+ public void postLocalAction(int action) {
+ AccessibilityNodeInfo node = getFocusNode();
+ if (node == null || !node.isEditable()) {
+ Log.d(TAG, "no input focus or not editable");
+ return;
+ }
+
+ final CharSequence curText = node.getText();
+ final int curTextLen = curText != null ? curText.length() : 0;
+ int selStart = node.getTextSelectionStart();
+ int selEnd = node.getTextSelectionEnd();
+ StringBuilder builder = null;
+
+ Log.d(TAG, " cur text: " + curText + " start=" + selStart + " end=" + selEnd);
+
+ switch (action) {
+ case KeyActionHandler.ACTION_BACKSPACE:
+ if (selStart == -1 || selEnd == -1 || curTextLen == 0) {
+ return;
+ }
+ builder = new StringBuilder(curTextLen - 1);
+ if (selStart == selEnd && selStart > 0) {
+ builder.append(curText.subSequence(0, selStart - 1));
+ builder.append(curText.subSequence(selEnd, curTextLen));
+ selStart = selStart - 1;
+ selEnd = selStart;
+ } else if (selStart != selEnd) {
+ builder.append(curText.subSequence(0, selStart));
+ builder.append(curText.subSequence(selEnd, curTextLen));
+ selEnd = selStart;
+ } else {
+ return;
+ }
+ break;
+ case KeyActionHandler.ACTION_DEL:
+ if (selStart == -1 || selEnd == -1 || curTextLen == 0) {
+ return;
+ }
+ builder = new StringBuilder(curTextLen - 1);
+ if (selStart == selEnd && selStart < curTextLen) {
+ builder.append(curText.subSequence(0, selStart));
+ builder.append(curText.subSequence(selEnd + 1, curTextLen));
+ selStart = selStart;
+ selEnd = selStart;
+ } else if (selStart != selEnd) {
+ builder.append(curText.subSequence(0, selStart));
+ builder.append(curText.subSequence(selEnd, curTextLen));
+ selEnd = selStart;
+ } else {
+ return;
+ }
+ break;
+ case KeyActionHandler.ACTION_MOVE_LEFT:
+ if (selStart == -1 || selEnd == -1 || curTextLen == 0) {
+ return;
+ }
+ if (selStart > 0) {
+ selStart = selStart - 1;
+ selEnd = selStart;
+ } else {
+ return;
+ }
+ break;
+ case KeyActionHandler.ACTION_MOVE_RIGHT:
+ if (selStart == -1 || selEnd == -1 || curTextLen == 0) {
+ return;
+ }
+ if (selEnd < curTextLen) {
+ selStart = selEnd + 1;
+ selEnd = selStart;
+ } else {
+ return;
+ }
+ break;
+ case KeyActionHandler.ACTION_MOVE_LEFTMOST:
+ selStart = 0;
+ selEnd = 0;
+ break;
+ case KeyActionHandler.ACTION_MOVE_RIGHTMOST:
+ if (curTextLen == 0) {
+ return;
+ }
+ selStart = curTextLen;
+ selEnd = curTextLen;
+ break;
+ case KeyActionHandler.ACTION_SELECT_LEFT:
+ if (selStart == -1 || selEnd == -1 || curTextLen == 0) {
+ return;
+ }
+ if (selStart > 0) {
+ selStart = selStart - 1;
+ } else {
+ return;
+ }
+ break;
+ case KeyActionHandler.ACTION_SELECT_RIGHT:
+ if (selStart == -1 || selEnd == -1 || curTextLen == 0) {
+ return;
+ }
+ if (selEnd < curTextLen) {
+ selEnd = selEnd + 1;
+ } else {
+ return;
+ }
+ break;
+ case KeyActionHandler.ACTION_SELECT_LEFTMOST:
+ selStart = 0;
+ break;
+ case KeyActionHandler.ACTION_SELECT_RIGHTMOST:
+ if (curTextLen == 0) {
+ return;
+ }
+ selEnd = curTextLen;
+ break;
+ default:
+ return;
+ }
+
+ if (builder != null) {
+ Log.d(TAG, " new text: " + builder.toString() + " start=" + selStart + " end=" + selEnd);
+ } else {
+ Log.d(TAG, " new start=" + selStart + " end=" + selEnd);
+ }
+
+ if (builder != null && builder.toString() != curText) {
+ Bundle args = new Bundle();
+ args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
+ builder.toString());
+ node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args);
+ }
+
+ if (selStart != -1 && selEnd != -1 && (builder == null || selStart != builder.length())) {
+ Bundle args = new Bundle();
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, selStart);
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, selEnd);
+ node.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, args);
+ }
+ }
+
+ @Override
+ public void postGlobalAction(int action) {
+ ControlService service = ControlService.getInstance();
+ if (service != null) {
+ if (!service.performGlobalAction(action)) {
+ Log.e(TAG, "could not perform global action: " + action);
+ }
+ }
+ }
+}