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 17package com.android.commands.input; 18 19import android.hardware.input.InputManager; 20import android.os.SystemClock; 21import android.util.Log; 22import android.view.InputDevice; 23import android.view.KeyCharacterMap; 24import android.view.KeyEvent; 25import android.view.MotionEvent; 26 27import java.util.HashMap; 28import java.util.Map; 29 30/** 31 * Command that sends key events to the device, either by their keycode, or by 32 * desired character output. 33 */ 34 35public class Input { 36 private static final String TAG = "Input"; 37 private static final String INVALID_ARGUMENTS = "Error: Invalid arguments for command: "; 38 39 private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{ 40 put("keyboard", InputDevice.SOURCE_KEYBOARD); 41 put("dpad", InputDevice.SOURCE_DPAD); 42 put("gamepad", InputDevice.SOURCE_GAMEPAD); 43 put("touchscreen", InputDevice.SOURCE_TOUCHSCREEN); 44 put("mouse", InputDevice.SOURCE_MOUSE); 45 put("stylus", InputDevice.SOURCE_STYLUS); 46 put("trackball", InputDevice.SOURCE_TRACKBALL); 47 put("touchpad", InputDevice.SOURCE_TOUCHPAD); 48 put("touchnavigation", InputDevice.SOURCE_TOUCH_NAVIGATION); 49 put("joystick", InputDevice.SOURCE_JOYSTICK); 50 }}; 51 52 53 /** 54 * Command-line entry point. 55 * 56 * @param args The command-line arguments 57 */ 58 public static void main(String[] args) { 59 (new Input()).run(args); 60 } 61 62 private void run(String[] args) { 63 if (args.length < 1) { 64 showUsage(); 65 return; 66 } 67 68 int index = 0; 69 String command = args[index]; 70 int inputSource = InputDevice.SOURCE_UNKNOWN; 71 if (SOURCES.containsKey(command)) { 72 inputSource = SOURCES.get(command); 73 index++; 74 command = args[index]; 75 } 76 final int length = args.length - index; 77 78 try { 79 if (command.equals("text")) { 80 if (length == 2) { 81 inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD); 82 sendText(inputSource, args[index+1]); 83 return; 84 } 85 } else if (command.equals("keyevent")) { 86 if (length >= 2) { 87 final boolean longpress = "--longpress".equals(args[index + 1]); 88 final int start = longpress ? index + 2 : index + 1; 89 inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD); 90 if (length > start) { 91 for (int i = start; i < length; i++) { 92 int keyCode = KeyEvent.keyCodeFromString(args[i]); 93 if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { 94 keyCode = KeyEvent.keyCodeFromString("KEYCODE_" + args[i]); 95 } 96 sendKeyEvent(inputSource, keyCode, longpress); 97 } 98 return; 99 } 100 } 101 } else if (command.equals("tap")) { 102 if (length == 3) { 103 inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN); 104 sendTap(inputSource, Float.parseFloat(args[index+1]), 105 Float.parseFloat(args[index+2])); 106 return; 107 } 108 } else if (command.equals("swipe")) { 109 int duration = -1; 110 inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN); 111 switch (length) { 112 case 6: 113 duration = Integer.parseInt(args[index+5]); 114 case 5: 115 sendSwipe(inputSource, 116 Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]), 117 Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]), 118 duration); 119 return; 120 } 121 } else if (command.equals("press")) { 122 inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL); 123 if (length == 1) { 124 sendTap(inputSource, 0.0f, 0.0f); 125 return; 126 } 127 } else if (command.equals("roll")) { 128 inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL); 129 if (length == 3) { 130 sendMove(inputSource, Float.parseFloat(args[index+1]), 131 Float.parseFloat(args[index+2])); 132 return; 133 } 134 } else { 135 System.err.println("Error: Unknown command: " + command); 136 showUsage(); 137 return; 138 } 139 } catch (NumberFormatException ex) { 140 } 141 System.err.println(INVALID_ARGUMENTS + command); 142 showUsage(); 143 } 144 145 /** 146 * Convert the characters of string text into key event's and send to 147 * device. 148 * 149 * @param text is a string of characters you want to input to the device. 150 */ 151 private void sendText(int source, String text) { 152 153 StringBuffer buff = new StringBuffer(text); 154 155 boolean escapeFlag = false; 156 for (int i=0; i<buff.length(); i++) { 157 if (escapeFlag) { 158 escapeFlag = false; 159 if (buff.charAt(i) == 's') { 160 buff.setCharAt(i, ' '); 161 buff.deleteCharAt(--i); 162 } 163 } 164 if (buff.charAt(i) == '%') { 165 escapeFlag = true; 166 } 167 } 168 169 char[] chars = buff.toString().toCharArray(); 170 171 KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); 172 KeyEvent[] events = kcm.getEvents(chars); 173 for(int i = 0; i < events.length; i++) { 174 KeyEvent e = events[i]; 175 if (source != e.getSource()) { 176 e.setSource(source); 177 } 178 injectKeyEvent(e); 179 } 180 } 181 182 private void sendKeyEvent(int inputSource, int keyCode, boolean longpress) { 183 long now = SystemClock.uptimeMillis(); 184 injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0, 185 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource)); 186 if (longpress) { 187 injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 1, 0, 188 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_LONG_PRESS, 189 inputSource)); 190 } 191 injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0, 192 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource)); 193 } 194 195 private void sendTap(int inputSource, float x, float y) { 196 long now = SystemClock.uptimeMillis(); 197 injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x, y, 1.0f); 198 injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x, y, 0.0f); 199 } 200 201 private void sendSwipe(int inputSource, float x1, float y1, float x2, float y2, int duration) { 202 if (duration < 0) { 203 duration = 300; 204 } 205 long now = SystemClock.uptimeMillis(); 206 injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f); 207 long startTime = now; 208 long endTime = startTime + duration; 209 while (now < endTime) { 210 long elapsedTime = now - startTime; 211 float alpha = (float) elapsedTime / duration; 212 injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha), 213 lerp(y1, y2, alpha), 1.0f); 214 now = SystemClock.uptimeMillis(); 215 } 216 injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f); 217 } 218 219 /** 220 * Sends a simple zero-pressure move event. 221 * 222 * @param inputSource the InputDevice.SOURCE_* sending the input event 223 * @param dx change in x coordinate due to move 224 * @param dy change in y coordinate due to move 225 */ 226 private void sendMove(int inputSource, float dx, float dy) { 227 long now = SystemClock.uptimeMillis(); 228 injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, dx, dy, 0.0f); 229 } 230 231 private void injectKeyEvent(KeyEvent event) { 232 Log.i(TAG, "injectKeyEvent: " + event); 233 InputManager.getInstance().injectInputEvent(event, 234 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); 235 } 236 237 /** 238 * Builds a MotionEvent and injects it into the event stream. 239 * 240 * @param inputSource the InputDevice.SOURCE_* sending the input event 241 * @param action the MotionEvent.ACTION_* for the event 242 * @param when the value of SystemClock.uptimeMillis() at which the event happened 243 * @param x x coordinate of event 244 * @param y y coordinate of event 245 * @param pressure pressure of event 246 */ 247 private void injectMotionEvent(int inputSource, int action, long when, float x, float y, float pressure) { 248 final float DEFAULT_SIZE = 1.0f; 249 final int DEFAULT_META_STATE = 0; 250 final float DEFAULT_PRECISION_X = 1.0f; 251 final float DEFAULT_PRECISION_Y = 1.0f; 252 final int DEFAULT_DEVICE_ID = 0; 253 final int DEFAULT_EDGE_FLAGS = 0; 254 MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, DEFAULT_SIZE, 255 DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y, DEFAULT_DEVICE_ID, 256 DEFAULT_EDGE_FLAGS); 257 event.setSource(inputSource); 258 Log.i(TAG, "injectMotionEvent: " + event); 259 InputManager.getInstance().injectInputEvent(event, 260 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); 261 } 262 263 private static final float lerp(float a, float b, float alpha) { 264 return (b - a) * alpha + a; 265 } 266 267 private static final int getSource(int inputSource, int defaultSource) { 268 return inputSource == InputDevice.SOURCE_UNKNOWN ? defaultSource : inputSource; 269 } 270 271 private void showUsage() { 272 System.err.println("Usage: input [<source>] <command> [<arg>...]"); 273 System.err.println(); 274 System.err.println("The sources are: "); 275 for (String src : SOURCES.keySet()) { 276 System.err.println(" " + src); 277 } 278 System.err.println(); 279 System.err.println("The commands and default sources are:"); 280 System.err.println(" text <string> (Default: touchscreen)"); 281 System.err.println(" keyevent [--longpress] <key code number or name> ..." 282 + " (Default: keyboard)"); 283 System.err.println(" tap <x> <y> (Default: touchscreen)"); 284 System.err.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]" 285 + " (Default: touchscreen)"); 286 System.err.println(" press (Default: trackball)"); 287 System.err.println(" roll <dx> <dy> (Default: trackball)"); 288 } 289} 290