1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.commands.input; 18 19 import android.hardware.input.InputManager; 20 import android.os.SystemClock; 21 import android.util.Log; 22 import android.view.InputDevice; 23 import android.view.KeyCharacterMap; 24 import android.view.KeyEvent; 25 import android.view.MotionEvent; 26 import android.view.ViewConfiguration; 27 28 import java.util.HashMap; 29 import java.util.Map; 30 31 /** 32 * Command that sends key events to the device, either by their keycode, or by 33 * desired character output. 34 */ 35 36 public class Input { 37 private static final String TAG = "Input"; 38 private static final String INVALID_ARGUMENTS = "Error: Invalid arguments for command: "; 39 40 private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{ 41 put("keyboard", InputDevice.SOURCE_KEYBOARD); 42 put("dpad", InputDevice.SOURCE_DPAD); 43 put("gamepad", InputDevice.SOURCE_GAMEPAD); 44 put("touchscreen", InputDevice.SOURCE_TOUCHSCREEN); 45 put("mouse", InputDevice.SOURCE_MOUSE); 46 put("stylus", InputDevice.SOURCE_STYLUS); 47 put("trackball", InputDevice.SOURCE_TRACKBALL); 48 put("touchpad", InputDevice.SOURCE_TOUCHPAD); 49 put("touchnavigation", InputDevice.SOURCE_TOUCH_NAVIGATION); 50 put("joystick", InputDevice.SOURCE_JOYSTICK); 51 }}; 52 53 54 /** 55 * Command-line entry point. 56 * 57 * @param args The command-line arguments 58 */ main(String[] args)59 public static void main(String[] args) { 60 (new Input()).run(args); 61 } 62 run(String[] args)63 private void run(String[] args) { 64 if (args.length < 1) { 65 showUsage(); 66 return; 67 } 68 69 int index = 0; 70 String command = args[index]; 71 int inputSource = InputDevice.SOURCE_UNKNOWN; 72 if (SOURCES.containsKey(command)) { 73 inputSource = SOURCES.get(command); 74 index++; 75 command = args[index]; 76 } 77 final int length = args.length - index; 78 79 try { 80 if (command.equals("text")) { 81 if (length == 2) { 82 inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD); 83 sendText(inputSource, args[index+1]); 84 return; 85 } 86 } else if (command.equals("keyevent")) { 87 if (length >= 2) { 88 final boolean longpress = "--longpress".equals(args[index + 1]); 89 final int start = longpress ? index + 2 : index + 1; 90 inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD); 91 if (args.length > start) { 92 for (int i = start; i < args.length; i++) { 93 int keyCode = KeyEvent.keyCodeFromString(args[i]); 94 if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { 95 keyCode = KeyEvent.keyCodeFromString("KEYCODE_" + args[i]); 96 } 97 sendKeyEvent(inputSource, keyCode, longpress); 98 } 99 return; 100 } 101 } 102 } else if (command.equals("tap")) { 103 if (length == 3) { 104 inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN); 105 sendTap(inputSource, Float.parseFloat(args[index+1]), 106 Float.parseFloat(args[index+2])); 107 return; 108 } 109 } else if (command.equals("swipe")) { 110 int duration = -1; 111 inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN); 112 switch (length) { 113 case 6: 114 duration = Integer.parseInt(args[index+5]); 115 case 5: 116 sendSwipe(inputSource, 117 Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]), 118 Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]), 119 duration); 120 return; 121 } 122 } else if (command.equals("draganddrop")) { 123 int duration = -1; 124 inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN); 125 switch (length) { 126 case 6: 127 duration = Integer.parseInt(args[index+5]); 128 case 5: 129 sendDragAndDrop(inputSource, 130 Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]), 131 Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]), 132 duration); 133 return; 134 } 135 } else if (command.equals("press")) { 136 inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL); 137 if (length == 1) { 138 sendTap(inputSource, 0.0f, 0.0f); 139 return; 140 } 141 } else if (command.equals("roll")) { 142 inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL); 143 if (length == 3) { 144 sendMove(inputSource, Float.parseFloat(args[index+1]), 145 Float.parseFloat(args[index+2])); 146 return; 147 } 148 } else { 149 System.err.println("Error: Unknown command: " + command); 150 showUsage(); 151 return; 152 } 153 } catch (NumberFormatException ex) { 154 } 155 System.err.println(INVALID_ARGUMENTS + command); 156 showUsage(); 157 } 158 159 /** 160 * Convert the characters of string text into key event's and send to 161 * device. 162 * 163 * @param text is a string of characters you want to input to the device. 164 */ sendText(int source, String text)165 private void sendText(int source, String text) { 166 167 StringBuffer buff = new StringBuffer(text); 168 169 boolean escapeFlag = false; 170 for (int i=0; i<buff.length(); i++) { 171 if (escapeFlag) { 172 escapeFlag = false; 173 if (buff.charAt(i) == 's') { 174 buff.setCharAt(i, ' '); 175 buff.deleteCharAt(--i); 176 } 177 } 178 if (buff.charAt(i) == '%') { 179 escapeFlag = true; 180 } 181 } 182 183 char[] chars = buff.toString().toCharArray(); 184 185 KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); 186 KeyEvent[] events = kcm.getEvents(chars); 187 for(int i = 0; i < events.length; i++) { 188 KeyEvent e = events[i]; 189 if (source != e.getSource()) { 190 e.setSource(source); 191 } 192 injectKeyEvent(e); 193 } 194 } 195 sendKeyEvent(int inputSource, int keyCode, boolean longpress)196 private void sendKeyEvent(int inputSource, int keyCode, boolean longpress) { 197 long now = SystemClock.uptimeMillis(); 198 injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0, 199 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource)); 200 if (longpress) { 201 injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 1, 0, 202 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_LONG_PRESS, 203 inputSource)); 204 } 205 injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0, 206 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource)); 207 } 208 sendTap(int inputSource, float x, float y)209 private void sendTap(int inputSource, float x, float y) { 210 long now = SystemClock.uptimeMillis(); 211 injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x, y, 1.0f); 212 injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x, y, 0.0f); 213 } 214 sendSwipe(int inputSource, float x1, float y1, float x2, float y2, int duration)215 private void sendSwipe(int inputSource, float x1, float y1, float x2, float y2, int duration) { 216 if (duration < 0) { 217 duration = 300; 218 } 219 long now = SystemClock.uptimeMillis(); 220 injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f); 221 long startTime = now; 222 long endTime = startTime + duration; 223 while (now < endTime) { 224 long elapsedTime = now - startTime; 225 float alpha = (float) elapsedTime / duration; 226 injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha), 227 lerp(y1, y2, alpha), 1.0f); 228 now = SystemClock.uptimeMillis(); 229 } 230 injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f); 231 } 232 sendDragAndDrop(int inputSource, float x1, float y1, float x2, float y2, int dragDuration)233 private void sendDragAndDrop(int inputSource, float x1, float y1, float x2, float y2, 234 int dragDuration) { 235 if (dragDuration < 0) { 236 dragDuration = 300; 237 } 238 long now = SystemClock.uptimeMillis(); 239 injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f); 240 try { 241 Thread.sleep(ViewConfiguration.getLongPressTimeout()); 242 } catch (InterruptedException e) { 243 throw new RuntimeException(e); 244 } 245 now = SystemClock.uptimeMillis(); 246 long startTime = now; 247 long endTime = startTime + dragDuration; 248 while (now < endTime) { 249 long elapsedTime = now - startTime; 250 float alpha = (float) elapsedTime / dragDuration; 251 injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha), 252 lerp(y1, y2, alpha), 1.0f); 253 now = SystemClock.uptimeMillis(); 254 } 255 injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f); 256 } 257 258 /** 259 * Sends a simple zero-pressure move event. 260 * 261 * @param inputSource the InputDevice.SOURCE_* sending the input event 262 * @param dx change in x coordinate due to move 263 * @param dy change in y coordinate due to move 264 */ sendMove(int inputSource, float dx, float dy)265 private void sendMove(int inputSource, float dx, float dy) { 266 long now = SystemClock.uptimeMillis(); 267 injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, dx, dy, 0.0f); 268 } 269 injectKeyEvent(KeyEvent event)270 private void injectKeyEvent(KeyEvent event) { 271 Log.i(TAG, "injectKeyEvent: " + event); 272 InputManager.getInstance().injectInputEvent(event, 273 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); 274 } 275 getInputDeviceId(int inputSource)276 private int getInputDeviceId(int inputSource) { 277 final int DEFAULT_DEVICE_ID = 0; 278 int[] devIds = InputDevice.getDeviceIds(); 279 for (int devId : devIds) { 280 InputDevice inputDev = InputDevice.getDevice(devId); 281 if (inputDev.supportsSource(inputSource)) { 282 return devId; 283 } 284 } 285 return DEFAULT_DEVICE_ID; 286 } 287 288 /** 289 * Builds a MotionEvent and injects it into the event stream. 290 * 291 * @param inputSource the InputDevice.SOURCE_* sending the input event 292 * @param action the MotionEvent.ACTION_* for the event 293 * @param when the value of SystemClock.uptimeMillis() at which the event happened 294 * @param x x coordinate of event 295 * @param y y coordinate of event 296 * @param pressure pressure of event 297 */ injectMotionEvent(int inputSource, int action, long when, float x, float y, float pressure)298 private void injectMotionEvent(int inputSource, int action, long when, float x, float y, float pressure) { 299 final float DEFAULT_SIZE = 1.0f; 300 final int DEFAULT_META_STATE = 0; 301 final float DEFAULT_PRECISION_X = 1.0f; 302 final float DEFAULT_PRECISION_Y = 1.0f; 303 final int DEFAULT_EDGE_FLAGS = 0; 304 MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, DEFAULT_SIZE, 305 DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y, 306 getInputDeviceId(inputSource), DEFAULT_EDGE_FLAGS); 307 event.setSource(inputSource); 308 Log.i(TAG, "injectMotionEvent: " + event); 309 InputManager.getInstance().injectInputEvent(event, 310 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); 311 } 312 lerp(float a, float b, float alpha)313 private static final float lerp(float a, float b, float alpha) { 314 return (b - a) * alpha + a; 315 } 316 getSource(int inputSource, int defaultSource)317 private static final int getSource(int inputSource, int defaultSource) { 318 return inputSource == InputDevice.SOURCE_UNKNOWN ? defaultSource : inputSource; 319 } 320 showUsage()321 private void showUsage() { 322 System.err.println("Usage: input [<source>] <command> [<arg>...]"); 323 System.err.println(); 324 System.err.println("The sources are: "); 325 for (String src : SOURCES.keySet()) { 326 System.err.println(" " + src); 327 } 328 System.err.println(); 329 System.err.println("The commands and default sources are:"); 330 System.err.println(" text <string> (Default: touchscreen)"); 331 System.err.println(" keyevent [--longpress] <key code number or name> ..." 332 + " (Default: keyboard)"); 333 System.err.println(" tap <x> <y> (Default: touchscreen)"); 334 System.err.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]" 335 + " (Default: touchscreen)"); 336 System.err.println(" draganddrop <x1> <y1> <x2> <y2> [duration(ms)]" 337 + " (Default: touchscreen)"); 338 System.err.println(" press (Default: trackball)"); 339 System.err.println(" roll <dx> <dy> (Default: trackball)"); 340 } 341 } 342