Merge "Accelerate input commands execution"
This commit is contained in:
committed by
Android (Google) Code Review
commit
cf9d4f5451
@@ -1,8 +1,7 @@
|
||||
// Copyright 2008 The Android Open Source Project
|
||||
//
|
||||
|
||||
java_binary {
|
||||
sh_binary {
|
||||
name: "input",
|
||||
wrapper: "input",
|
||||
srcs: ["**/*.java"],
|
||||
src: "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 "$@"
|
||||
|
||||
@@ -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)");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user