summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/input/Android.bp5
-rwxr-xr-xcmds/input/input3
-rw-r--r--cmds/input/src/com/android/commands/input/Input.java435
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java10
-rw-r--r--services/core/java/com/android/server/input/InputShellCommand.java391
5 files changed, 404 insertions, 440 deletions
diff --git a/cmds/input/Android.bp b/cmds/input/Android.bp
index a0ebde63eb6e..1ee9dd355290 100644
--- a/cmds/input/Android.bp
+++ b/cmds/input/Android.bp
@@ -1,8 +1,7 @@
// Copyright 2008 The Android Open Source Project
//
-java_binary {
+sh_binary {
name: "input",
- wrapper: "input",
- srcs: ["**/*.java"],
+ src: "input",
}
diff --git a/cmds/input/input b/cmds/input/input
index 2625eba17153..d7d041431b49 100755
--- a/cmds/input/input
+++ b/cmds/input/input
@@ -1,3 +1,2 @@
#!/system/bin/sh
-export CLASSPATH=/system/framework/input.jar
-exec app_process /system/bin com.android.commands.input.Input "$@"
+cmd input "$@"
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
deleted file mode 100644
index 08216d9b3f1d..000000000000
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.commands.input;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
-
-import android.hardware.input.InputManager;
-import android.os.SystemClock;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-import com.android.internal.os.BaseCommand;
-
-import java.io.PrintStream;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Command that sends key events to the device, either by their keycode, or by
- * desired character output.
- */
-
-public class Input extends BaseCommand {
- private static final String TAG = "Input";
- private static final String INVALID_ARGUMENTS = "Error: Invalid arguments for command: ";
- private static final String INVALID_DISPLAY_ARGUMENTS =
- "Error: Invalid arguments for display ID.";
-
- private static final float DEFAULT_PRESSURE = 1.0f;
- private static final float NO_PRESSURE = 0.0f;
-
- private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{
- put("keyboard", InputDevice.SOURCE_KEYBOARD);
- put("dpad", InputDevice.SOURCE_DPAD);
- put("gamepad", InputDevice.SOURCE_GAMEPAD);
- put("touchscreen", InputDevice.SOURCE_TOUCHSCREEN);
- put("mouse", InputDevice.SOURCE_MOUSE);
- put("stylus", InputDevice.SOURCE_STYLUS);
- put("trackball", InputDevice.SOURCE_TRACKBALL);
- put("touchpad", InputDevice.SOURCE_TOUCHPAD);
- put("touchnavigation", InputDevice.SOURCE_TOUCH_NAVIGATION);
- put("joystick", InputDevice.SOURCE_JOYSTICK);
- }};
-
- private static final Map<String, InputCmd> COMMANDS = new HashMap<String, InputCmd>();
-
- /**
- * Command-line entry point.
- *
- * @param args The command-line arguments
- */
- public static void main(String[] args) {
- (new Input()).run(args);
- }
-
- Input() {
- COMMANDS.put("text", new InputText());
- COMMANDS.put("keyevent", new InputKeyEvent());
- COMMANDS.put("tap", new InputTap());
- COMMANDS.put("swipe", new InputSwipe());
- COMMANDS.put("draganddrop", new InputDragAndDrop());
- COMMANDS.put("press", new InputPress());
- COMMANDS.put("roll", new InputRoll());
- COMMANDS.put("motionevent", new InputMotionEvent());
- }
-
- @Override
- public void onRun() throws Exception {
- String arg = nextArgRequired();
- int inputSource = InputDevice.SOURCE_UNKNOWN;
-
- // Get source (optional).
- if (SOURCES.containsKey(arg)) {
- inputSource = SOURCES.get(arg);
- arg = nextArgRequired();
- }
-
- // Get displayId (optional).
- int displayId = INVALID_DISPLAY;
- if ("-d".equals(arg)) {
- displayId = getDisplayId();
- arg = nextArgRequired();
- }
-
- // Get command and run.
- InputCmd cmd = COMMANDS.get(arg);
- if (cmd != null) {
- try {
- cmd.run(inputSource, displayId);
- return;
- } catch (NumberFormatException ex) {
- throw new IllegalArgumentException(INVALID_ARGUMENTS + arg);
- }
- }
-
- throw new IllegalArgumentException("Error: Unknown command: " + arg);
- }
-
- private int getDisplayId() {
- String displayArg = nextArgRequired();
- if ("INVALID_DISPLAY".equalsIgnoreCase(displayArg)) {
- return INVALID_DISPLAY;
- } else if ("DEFAULT_DISPLAY".equalsIgnoreCase(displayArg)) {
- return DEFAULT_DISPLAY;
- } else {
- try {
- final int displayId = Integer.parseInt(displayArg);
- if (displayId == INVALID_DISPLAY) {
- return INVALID_DISPLAY;
- }
- return Math.max(displayId, 0);
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException(INVALID_DISPLAY_ARGUMENTS);
- }
- }
- }
-
- class InputText implements InputCmd {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
- sendText(inputSource, nextArgRequired(), displayId);
- }
-
- /**
- * Convert the characters of string text into key event's and send to
- * device.
- *
- * @param text is a string of characters you want to input to the device.
- */
- private void sendText(int source, final String text, int displayId) {
- final StringBuffer buff = new StringBuffer(text);
- boolean escapeFlag = false;
- for (int i = 0; i < buff.length(); i++) {
- if (escapeFlag) {
- escapeFlag = false;
- if (buff.charAt(i) == 's') {
- buff.setCharAt(i, ' ');
- buff.deleteCharAt(--i);
- }
- }
- if (buff.charAt(i) == '%') {
- escapeFlag = true;
- }
- }
-
- final char[] chars = buff.toString().toCharArray();
- final KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
- final KeyEvent[] events = kcm.getEvents(chars);
- for (int i = 0; i < events.length; i++) {
- KeyEvent e = events[i];
- if (source != e.getSource()) {
- e.setSource(source);
- }
- e.setDisplayId(displayId);
- injectKeyEvent(e);
- }
- }
- }
-
- class InputKeyEvent implements InputCmd {
- @Override
- public void run(int inputSource, int displayId) {
- String arg = nextArgRequired();
- final boolean longpress = "--longpress".equals(arg);
- if (longpress) {
- arg = nextArgRequired();
- }
-
- do {
- final int keycode = KeyEvent.keyCodeFromString(arg);
- sendKeyEvent(inputSource, keycode, longpress, displayId);
- } while ((arg = nextArg()) != null);
- }
-
- private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) {
- final long now = SystemClock.uptimeMillis();
- int repeatCount = 0;
-
- KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount,
- 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
- inputSource);
- event.setDisplayId(displayId);
-
- injectKeyEvent(event);
- if (longpress) {
- repeatCount++;
- injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount,
- KeyEvent.FLAG_LONG_PRESS));
- }
- injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
- }
- }
-
- class InputTap implements InputCmd {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
- sendTap(inputSource, Float.parseFloat(nextArgRequired()),
- Float.parseFloat(nextArgRequired()), displayId);
- }
-
- void sendTap(int inputSource, float x, float y, int displayId) {
- final long now = SystemClock.uptimeMillis();
- injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, now, x, y, 1.0f,
- displayId);
- injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, now, x, y, 0.0f, displayId);
- }
- }
-
- class InputPress extends InputTap {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
- sendTap(inputSource, 0.0f, 0.0f, displayId);
- }
- }
-
- class InputSwipe implements InputCmd {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
- sendSwipe(inputSource, displayId, false);
- }
-
- void sendSwipe(int inputSource, int displayId, boolean isDragDrop) {
- // Parse two points and duration.
- final float x1 = Float.parseFloat(nextArgRequired());
- final float y1 = Float.parseFloat(nextArgRequired());
- final float x2 = Float.parseFloat(nextArgRequired());
- final float y2 = Float.parseFloat(nextArgRequired());
- String durationArg = nextArg();
- int duration = durationArg != null ? Integer.parseInt(durationArg) : -1;
- if (duration < 0) {
- duration = 300;
- }
-
- final long down = SystemClock.uptimeMillis();
- injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, down, down, x1, y1, 1.0f,
- displayId);
- if (isDragDrop) {
- // long press until drag start.
- try {
- Thread.sleep(ViewConfiguration.getLongPressTimeout());
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- long now = SystemClock.uptimeMillis();
- final long endTime = down + duration;
- while (now < endTime) {
- final long elapsedTime = now - down;
- final float alpha = (float) elapsedTime / duration;
- injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now,
- lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId);
- now = SystemClock.uptimeMillis();
- }
- injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f,
- displayId);
- }
- }
-
- class InputDragAndDrop extends InputSwipe {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
- sendSwipe(inputSource, displayId, true);
- }
- }
-
- class InputRoll implements InputCmd {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
- sendMove(inputSource, Float.parseFloat(nextArgRequired()),
- Float.parseFloat(nextArgRequired()), displayId);
- }
-
- /**
- * Sends a simple zero-pressure move event.
- *
- * @param inputSource the InputDevice.SOURCE_* sending the input event
- * @param dx change in x coordinate due to move
- * @param dy change in y coordinate due to move
- */
- private void sendMove(int inputSource, float dx, float dy, int displayId) {
- final long now = SystemClock.uptimeMillis();
- injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, now, dx, dy, 0.0f,
- displayId);
- }
- }
-
- class InputMotionEvent implements InputCmd {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
- sendMotionEvent(inputSource, nextArgRequired(), Float.parseFloat(nextArgRequired()),
- Float.parseFloat(nextArgRequired()), displayId);
- }
-
- private void sendMotionEvent(int inputSource, String motionEventType, float x, float y,
- int displayId) {
- final int action;
- final float pressure;
-
- switch (motionEventType.toUpperCase()) {
- case "DOWN":
- action = MotionEvent.ACTION_DOWN;
- pressure = DEFAULT_PRESSURE;
- break;
- case "UP":
- action = MotionEvent.ACTION_UP;
- pressure = NO_PRESSURE;
- break;
- case "MOVE":
- action = MotionEvent.ACTION_MOVE;
- pressure = DEFAULT_PRESSURE;
- break;
- default:
- throw new IllegalArgumentException("Unknown motionevent " + motionEventType);
- }
-
- final long now = SystemClock.uptimeMillis();
- injectMotionEvent(inputSource, action, now, now, x, y, pressure, displayId);
- }
- }
-
- /**
- * Abstract class for command
- * use nextArgRequired or nextArg to check next argument if necessary.
- */
- private interface InputCmd {
- void run(int inputSource, int displayId);
- }
-
- private static void injectKeyEvent(KeyEvent event) {
- InputManager.getInstance().injectInputEvent(event,
- InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
- }
-
- private static int getInputDeviceId(int inputSource) {
- final int DEFAULT_DEVICE_ID = 0;
- int[] devIds = InputDevice.getDeviceIds();
- for (int devId : devIds) {
- InputDevice inputDev = InputDevice.getDevice(devId);
- if (inputDev.supportsSource(inputSource)) {
- return devId;
- }
- }
- return DEFAULT_DEVICE_ID;
- }
-
- /**
- * Builds a MotionEvent and injects it into the event stream.
- *
- * @param inputSource the InputDevice.SOURCE_* sending the input event
- * @param action the MotionEvent.ACTION_* for the event
- * @param downTime the value of the ACTION_DOWN event happened
- * @param when the value of SystemClock.uptimeMillis() at which the event happened
- * @param x x coordinate of event
- * @param y y coordinate of event
- * @param pressure pressure of event
- */
- private static void injectMotionEvent(int inputSource, int action, long downTime, long when,
- float x, float y, float pressure, int displayId) {
- final float DEFAULT_SIZE = 1.0f;
- final int DEFAULT_META_STATE = 0;
- final float DEFAULT_PRECISION_X = 1.0f;
- final float DEFAULT_PRECISION_Y = 1.0f;
- final int DEFAULT_EDGE_FLAGS = 0;
- MotionEvent event = MotionEvent.obtain(downTime, when, action, x, y, pressure, DEFAULT_SIZE,
- DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y,
- getInputDeviceId(inputSource), DEFAULT_EDGE_FLAGS);
- event.setSource(inputSource);
- if (displayId == INVALID_DISPLAY && (inputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- displayId = DEFAULT_DISPLAY;
- }
- event.setDisplayId(displayId);
- InputManager.getInstance().injectInputEvent(event,
- InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
- }
-
- private static final float lerp(float a, float b, float alpha) {
- return (b - a) * alpha + a;
- }
-
- private static final int getSource(int inputSource, int defaultSource) {
- return inputSource == InputDevice.SOURCE_UNKNOWN ? defaultSource : inputSource;
- }
-
- @Override
- public void onShowUsage(PrintStream out) {
- out.println("Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]");
- out.println();
- out.println("The sources are: ");
- for (String src : SOURCES.keySet()) {
- out.println(" " + src);
- }
- out.println();
- out.printf("-d: specify the display ID.\n"
- + " (Default: %d for key event, %d for motion event if not specified.)",
- INVALID_DISPLAY, DEFAULT_DISPLAY);
- out.println();
- out.println("The commands and default sources are:");
- out.println(" text <string> (Default: touchscreen)");
- out.println(" keyevent [--longpress] <key code number or name> ..."
- + " (Default: keyboard)");
- out.println(" tap <x> <y> (Default: touchscreen)");
- out.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]"
- + " (Default: touchscreen)");
- out.println(" draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
- + " (Default: touchscreen)");
- out.println(" press (Default: trackball)");
- out.println(" roll <dx> <dy> (Default: trackball)");
- out.println(" motionevent <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)");
- }
-}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 115899a2a518..d9b25baf2482 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -59,6 +59,8 @@ import android.os.Message;
import android.os.MessageQueue;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -115,6 +117,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+
/*
* Wraps the C++ InputManager and provides its callbacks.
*/
@@ -2506,4 +2509,11 @@ public class InputManagerService extends IInputManager.Stub
return InputManagerService.this.transferTouchFocus(fromChannelToken, toChannelToken);
}
}
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ new InputShellCommand().exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
}
diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java
new file mode 100644
index 000000000000..533392a86712
--- /dev/null
+++ b/services/core/java/com/android/server/input/InputShellCommand.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import android.hardware.input.InputManager;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Command that sends input events to the device.
+ */
+
+public class InputShellCommand extends ShellCommand {
+ private static final String INVALID_ARGUMENTS = "Error: Invalid arguments for command: ";
+ private static final String INVALID_DISPLAY_ARGUMENTS =
+ "Error: Invalid arguments for display ID.";
+ private static final int DEFAULT_DEVICE_ID = 0;
+ private static final float DEFAULT_PRESSURE = 1.0f;
+ private static final float NO_PRESSURE = 0.0f;
+ private static final float DEFAULT_SIZE = 1.0f;
+ private static final int DEFAULT_META_STATE = 0;
+ private static final float DEFAULT_PRECISION_X = 1.0f;
+ private static final float DEFAULT_PRECISION_Y = 1.0f;
+ private static final int DEFAULT_EDGE_FLAGS = 0;
+
+ private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{
+ put("keyboard", InputDevice.SOURCE_KEYBOARD);
+ put("dpad", InputDevice.SOURCE_DPAD);
+ put("gamepad", InputDevice.SOURCE_GAMEPAD);
+ put("touchscreen", InputDevice.SOURCE_TOUCHSCREEN);
+ put("mouse", InputDevice.SOURCE_MOUSE);
+ put("stylus", InputDevice.SOURCE_STYLUS);
+ put("trackball", InputDevice.SOURCE_TRACKBALL);
+ put("touchpad", InputDevice.SOURCE_TOUCHPAD);
+ put("touchnavigation", InputDevice.SOURCE_TOUCH_NAVIGATION);
+ put("joystick", InputDevice.SOURCE_JOYSTICK);
+ }};
+
+ private void injectKeyEvent(KeyEvent event) {
+ InputManager.getInstance().injectInputEvent(event,
+ InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
+ }
+
+ private int getInputDeviceId(int inputSource) {
+ int[] devIds = InputDevice.getDeviceIds();
+ for (int devId : devIds) {
+ InputDevice inputDev = InputDevice.getDevice(devId);
+ if (inputDev.supportsSource(inputSource)) {
+ return devId;
+ }
+ }
+ return DEFAULT_DEVICE_ID;
+ }
+
+ private int getDisplayId() {
+ String displayArg = getNextArgRequired();
+ if ("INVALID_DISPLAY".equalsIgnoreCase(displayArg)) {
+ return INVALID_DISPLAY;
+ } else if ("DEFAULT_DISPLAY".equalsIgnoreCase(displayArg)) {
+ return DEFAULT_DISPLAY;
+ } else {
+ try {
+ final int displayId = Integer.parseInt(displayArg);
+ if (displayId == INVALID_DISPLAY) {
+ return INVALID_DISPLAY;
+ }
+ return Math.max(displayId, 0);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(INVALID_DISPLAY_ARGUMENTS);
+ }
+ }
+ }
+
+ /**
+ * Builds a MotionEvent and injects it into the event stream.
+ *
+ * @param inputSource the InputDevice.SOURCE_* sending the input event
+ * @param action the MotionEvent.ACTION_* for the event
+ * @param downTime the value of the ACTION_DOWN event happened
+ * @param when the value of SystemClock.uptimeMillis() at which the event happened
+ * @param x x coordinate of event
+ * @param y y coordinate of event
+ * @param pressure pressure of event
+ */
+ private void injectMotionEvent(int inputSource, int action, long downTime, long when,
+ float x, float y, float pressure, int displayId) {
+ MotionEvent event = MotionEvent.obtain(downTime, when, action, x, y, pressure,
+ DEFAULT_SIZE, DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y,
+ getInputDeviceId(inputSource), DEFAULT_EDGE_FLAGS);
+ event.setSource(inputSource);
+ if (displayId == INVALID_DISPLAY
+ && (inputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ displayId = DEFAULT_DISPLAY;
+ }
+ event.setDisplayId(displayId);
+ InputManager.getInstance().injectInputEvent(event,
+ InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
+ }
+
+ private float lerp(float a, float b, float alpha) {
+ return (b - a) * alpha + a;
+ }
+
+ private int getSource(int inputSource, int defaultSource) {
+ return inputSource == InputDevice.SOURCE_UNKNOWN ? defaultSource : inputSource;
+ }
+
+ @Override
+ public final int onCommand(String cmd) {
+ String arg = cmd;
+ int inputSource = InputDevice.SOURCE_UNKNOWN;
+ // Get source (optional).
+ if (SOURCES.containsKey(arg)) {
+ inputSource = SOURCES.get(arg);
+ arg = getNextArgRequired();
+ }
+
+ // Get displayId (optional).
+ int displayId = INVALID_DISPLAY;
+ if ("-d".equals(arg)) {
+ displayId = getDisplayId();
+ arg = getNextArgRequired();
+ }
+
+ try {
+ if ("text".equals(arg)) {
+ runText(inputSource, displayId);
+ } else if ("keyevent".equals(arg)) {
+ runKeyEvent(inputSource, displayId);
+ } else if ("tap".equals(arg)) {
+ runTap(inputSource, displayId);
+ } else if ("swipe".equals(arg)) {
+ runSwipe(inputSource, displayId);
+ } else if ("draganddrop".equals(arg)) {
+ runDragAndDrop(inputSource, displayId);
+ } else if ("press".equals(arg)) {
+ runPress(inputSource, displayId);
+ } else if ("roll".equals(arg)) {
+ runRoll(inputSource, displayId);
+ } else if ("motionevent".equals(arg)) {
+ runMotionEvent(inputSource, displayId);
+ } else {
+ handleDefaultCommands(arg);
+ }
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException(INVALID_ARGUMENTS + arg);
+ }
+ return 0;
+ }
+
+ @Override
+ public final void onHelp() {
+ try (PrintWriter out = getOutPrintWriter();) {
+ out.println("Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]");
+ out.println();
+ out.println("The sources are: ");
+ for (String src : SOURCES.keySet()) {
+ out.println(" " + src);
+ }
+ out.println();
+ out.printf("-d: specify the display ID.\n (Default: %d for key event, "
+ + "%d for motion event if not specified.)",
+ INVALID_DISPLAY, DEFAULT_DISPLAY);
+ out.println();
+ out.println("The commands and default sources are:");
+ out.println(" text <string> (Default: touchscreen)");
+ out.println(" keyevent [--longpress] <key code number or name> ..."
+ + " (Default: keyboard)");
+ out.println(" tap <x> <y> (Default: touchscreen)");
+ out.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]"
+ + " (Default: touchscreen)");
+ out.println(" draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
+ + " (Default: touchscreen)");
+ out.println(" press (Default: trackball)");
+ out.println(" roll <dx> <dy> (Default: trackball)");
+ out.println(" motionevent <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)");
+ }
+ }
+
+ private void runText(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
+ sendText(inputSource, getNextArgRequired(), displayId);
+ }
+
+ /**
+ * Convert the characters of string text into key event's and send to
+ * device.
+ *
+ * @param text is a string of characters you want to input to the device.
+ */
+ private void sendText(int source, final String text, int displayId) {
+ final StringBuffer buff = new StringBuffer(text);
+ boolean escapeFlag = false;
+ for (int i = 0; i < buff.length(); i++) {
+ if (escapeFlag) {
+ escapeFlag = false;
+ if (buff.charAt(i) == 's') {
+ buff.setCharAt(i, ' ');
+ buff.deleteCharAt(--i);
+ }
+ }
+ if (buff.charAt(i) == '%') {
+ escapeFlag = true;
+ }
+ }
+
+ final char[] chars = buff.toString().toCharArray();
+ final KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+ final KeyEvent[] events = kcm.getEvents(chars);
+ for (int i = 0; i < events.length; i++) {
+ KeyEvent e = events[i];
+ if (source != e.getSource()) {
+ e.setSource(source);
+ }
+ e.setDisplayId(displayId);
+ injectKeyEvent(e);
+ }
+ }
+
+ private void runKeyEvent(int inputSource, int displayId) {
+ String arg = getNextArgRequired();
+ final boolean longpress = "--longpress".equals(arg);
+ if (longpress) {
+ arg = getNextArgRequired();
+ }
+
+ do {
+ final int keycode = KeyEvent.keyCodeFromString(arg);
+ sendKeyEvent(inputSource, keycode, longpress, displayId);
+ } while ((arg = getNextArg()) != null);
+ }
+
+ private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) {
+ final long now = SystemClock.uptimeMillis();
+ int repeatCount = 0;
+
+ KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount,
+ 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
+ inputSource);
+ event.setDisplayId(displayId);
+
+ injectKeyEvent(event);
+ if (longpress) {
+ repeatCount++;
+ injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount,
+ KeyEvent.FLAG_LONG_PRESS));
+ }
+ injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
+ }
+
+ private void runTap(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendTap(inputSource, Float.parseFloat(getNextArgRequired()),
+ Float.parseFloat(getNextArgRequired()), displayId);
+ }
+
+ private void sendTap(int inputSource, float x, float y, int displayId) {
+ final long now = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, now, x, y, 1.0f,
+ displayId);
+ injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, now, x, y, 0.0f, displayId);
+ }
+
+ private void runPress(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+ sendTap(inputSource, 0.0f, 0.0f, displayId);
+ }
+
+ private void runSwipe(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendSwipe(inputSource, displayId, false);
+ }
+
+ private void sendSwipe(int inputSource, int displayId, boolean isDragDrop) {
+ // Parse two points and duration.
+ final float x1 = Float.parseFloat(getNextArgRequired());
+ final float y1 = Float.parseFloat(getNextArgRequired());
+ final float x2 = Float.parseFloat(getNextArgRequired());
+ final float y2 = Float.parseFloat(getNextArgRequired());
+ String durationArg = getNextArg();
+ int duration = durationArg != null ? Integer.parseInt(durationArg) : -1;
+ if (duration < 0) {
+ duration = 300;
+ }
+
+ final long down = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, down, down, x1, y1, 1.0f,
+ displayId);
+ if (isDragDrop) {
+ // long press until drag start.
+ try {
+ Thread.sleep(ViewConfiguration.getLongPressTimeout());
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ long now = SystemClock.uptimeMillis();
+ final long endTime = down + duration;
+ while (now < endTime) {
+ final long elapsedTime = now - down;
+ final float alpha = (float) elapsedTime / duration;
+ injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now,
+ lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId);
+ now = SystemClock.uptimeMillis();
+ }
+ injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f,
+ displayId);
+ }
+
+ private void runDragAndDrop(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendSwipe(inputSource, displayId, true);
+ }
+
+ private void runRoll(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+ sendMove(inputSource, Float.parseFloat(getNextArgRequired()),
+ Float.parseFloat(getNextArgRequired()), displayId);
+ }
+
+ /**
+ * Sends a simple zero-pressure move event.
+ *
+ * @param inputSource the InputDevice.SOURCE_* sending the input event
+ * @param dx change in x coordinate due to move
+ * @param dy change in y coordinate due to move
+ */
+ private void sendMove(int inputSource, float dx, float dy, int displayId) {
+ final long now = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, now, dx, dy, 0.0f,
+ displayId);
+ }
+
+ private void runMotionEvent(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendMotionEvent(inputSource, getNextArgRequired(), Float.parseFloat(getNextArgRequired()),
+ Float.parseFloat(getNextArgRequired()), displayId);
+ }
+
+ private void sendMotionEvent(int inputSource, String motionEventType, float x, float y,
+ int displayId) {
+ final int action;
+ final float pressure;
+
+ switch (motionEventType.toUpperCase()) {
+ case "DOWN":
+ action = MotionEvent.ACTION_DOWN;
+ pressure = DEFAULT_PRESSURE;
+ break;
+ case "UP":
+ action = MotionEvent.ACTION_UP;
+ pressure = NO_PRESSURE;
+ break;
+ case "MOVE":
+ action = MotionEvent.ACTION_MOVE;
+ pressure = DEFAULT_PRESSURE;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown motionevent " + motionEventType);
+ }
+
+ final long now = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, action, now, now, x, y, pressure, displayId);
+ }
+}