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.dumprendertree; 18 19 import android.os.SystemClock; 20 import android.util.*; 21 import android.view.KeyEvent; 22 import android.view.MotionEvent; 23 import android.webkit.WebView; 24 25 import java.util.Arrays; 26 import java.util.Vector; 27 28 public class WebViewEventSender implements EventSender { 29 30 private static final String LOGTAG = "WebViewEventSender"; 31 32 WebViewEventSender(WebView webView) { 33 mWebView = webView; 34 mWebView.getSettings().setBuiltInZoomControls(true); 35 mTouchPoints = new Vector<TouchPoint>(); 36 } 37 38 public void resetMouse() { 39 mouseX = mouseY = 0; 40 } 41 42 public void enableDOMUIEventLogging(int DOMNode) { 43 // TODO Auto-generated method stub 44 45 } 46 47 public void fireKeyboardEventsToElement(int DOMNode) { 48 // TODO Auto-generated method stub 49 50 } 51 52 public void keyDown(String character, String[] withModifiers) { 53 Log.e("EventSender", "KeyDown: " + character + "(" 54 + character.getBytes()[0] + ") Modifiers: " 55 + Arrays.toString(withModifiers)); 56 KeyEvent modifier = null; 57 if (withModifiers != null && withModifiers.length > 0) { 58 for (int i = 0; i < withModifiers.length; i++) { 59 int keyCode = modifierMapper(withModifiers[i]); 60 modifier = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode); 61 mWebView.onKeyDown(modifier.getKeyCode(), modifier); 62 } 63 } 64 int keyCode = keyMapper(character.toLowerCase().toCharArray()[0]); 65 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode); 66 mWebView.onKeyDown(event.getKeyCode(), event); 67 68 } 69 70 public void keyDown(String character) { 71 keyDown(character, null); 72 } 73 74 public void leapForward(int milliseconds) { 75 // TODO Auto-generated method stub 76 77 } 78 79 public void mouseClick() { 80 mouseDown(); 81 mouseUp(); 82 } 83 84 public void mouseDown() { 85 long ts = SystemClock.uptimeMillis(); 86 MotionEvent event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_DOWN, mouseX, mouseY, 0); 87 mWebView.onTouchEvent(event); 88 } 89 90 public void mouseMoveTo(int X, int Y) { 91 mouseX= X; 92 mouseY= Y; 93 } 94 95 public void mouseUp() { 96 long ts = SystemClock.uptimeMillis(); 97 MotionEvent event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_UP, mouseX, mouseY, 0); 98 mWebView.onTouchEvent(event); 99 } 100 101 // Assumes lowercase chars, case needs to be 102 // handled by calling function. 103 static int keyMapper(char c) { 104 // handle numbers 105 if (c >= '0' && c<= '9') { 106 int offset = c - '0'; 107 return KeyEvent.KEYCODE_0 + offset; 108 } 109 110 // handle characters 111 if (c >= 'a' && c <= 'z') { 112 int offset = c - 'a'; 113 return KeyEvent.KEYCODE_A + offset; 114 } 115 116 // handle all others 117 switch (c) { 118 case '*': 119 return KeyEvent.KEYCODE_STAR; 120 case '#': 121 return KeyEvent.KEYCODE_POUND; 122 case ',': 123 return KeyEvent.KEYCODE_COMMA; 124 case '.': 125 return KeyEvent.KEYCODE_PERIOD; 126 case '\t': 127 return KeyEvent.KEYCODE_TAB; 128 case ' ': 129 return KeyEvent.KEYCODE_SPACE; 130 case '\n': 131 return KeyEvent.KEYCODE_ENTER; 132 case '\b': 133 case 0x7F: 134 return KeyEvent.KEYCODE_DEL; 135 case '~': 136 return KeyEvent.KEYCODE_GRAVE; 137 case '-': 138 return KeyEvent.KEYCODE_MINUS; 139 case '=': 140 return KeyEvent.KEYCODE_EQUALS; 141 case '(': 142 return KeyEvent.KEYCODE_LEFT_BRACKET; 143 case ')': 144 return KeyEvent.KEYCODE_RIGHT_BRACKET; 145 case '\\': 146 return KeyEvent.KEYCODE_BACKSLASH; 147 case ';': 148 return KeyEvent.KEYCODE_SEMICOLON; 149 case '\'': 150 return KeyEvent.KEYCODE_APOSTROPHE; 151 case '/': 152 return KeyEvent.KEYCODE_SLASH; 153 default: 154 break; 155 } 156 157 return c; 158 } 159 160 static int modifierMapper(String modifier) { 161 if (modifier.equals("ctrlKey")) { 162 return KeyEvent.KEYCODE_ALT_LEFT; 163 } else if (modifier.equals("shiftKey")) { 164 return KeyEvent.KEYCODE_SHIFT_LEFT; 165 } else if (modifier.equals("altKey")) { 166 return KeyEvent.KEYCODE_SYM; 167 } else if (modifier.equals("metaKey")) { 168 return KeyEvent.KEYCODE_UNKNOWN; 169 } 170 return KeyEvent.KEYCODE_UNKNOWN; 171 } 172 173 public void touchStart() { 174 final int numPoints = mTouchPoints.size(); 175 if (numPoints == 0) { 176 return; 177 } 178 179 int[] pointerIds = new int[numPoints]; 180 MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints]; 181 long downTime = SystemClock.uptimeMillis(); 182 183 for (int i = 0; i < numPoints; ++i) { 184 pointerIds[i] = mTouchPoints.get(i).getId(); 185 pointerCoords[i] = new MotionEvent.PointerCoords(); 186 pointerCoords[i].x = mTouchPoints.get(i).getX(); 187 pointerCoords[i].y = mTouchPoints.get(i).getY(); 188 mTouchPoints.get(i).setDownTime(downTime); 189 } 190 191 MotionEvent event = MotionEvent.obtain(downTime, downTime, 192 MotionEvent.ACTION_DOWN, numPoints, pointerIds, pointerCoords, 193 mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0); 194 195 mWebView.onTouchEvent(event); 196 } 197 198 public void touchMove() { 199 final int numPoints = mTouchPoints.size(); 200 if (numPoints == 0) { 201 return; 202 } 203 204 int[] pointerIds = new int[numPoints]; 205 MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints]; 206 int numMovedPoints = 0; 207 for (int i = 0; i < numPoints; ++i) { 208 TouchPoint tp = mTouchPoints.get(i); 209 if (tp.hasMoved()) { 210 pointerIds[numMovedPoints] = mTouchPoints.get(i).getId(); 211 pointerCoords[i] = new MotionEvent.PointerCoords(); 212 pointerCoords[numMovedPoints].x = mTouchPoints.get(i).getX(); 213 pointerCoords[numMovedPoints].y = mTouchPoints.get(i).getY(); 214 ++numMovedPoints; 215 tp.setMoved(false); 216 } 217 } 218 219 if (numMovedPoints == 0) { 220 return; 221 } 222 223 MotionEvent event = MotionEvent.obtain(mTouchPoints.get(0).downTime(), 224 SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, 225 numMovedPoints, pointerIds, pointerCoords, 226 mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0); 227 mWebView.onTouchEvent(event); 228 } 229 230 public void touchEnd() { 231 final int numPoints = mTouchPoints.size(); 232 if (numPoints == 0) { 233 return; 234 } 235 236 int[] pointerIds = new int[numPoints]; 237 MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints]; 238 239 for (int i = 0; i < numPoints; ++i) { 240 pointerIds[i] = mTouchPoints.get(i).getId(); 241 pointerCoords[i] = new MotionEvent.PointerCoords(); 242 pointerCoords[i].x = mTouchPoints.get(i).getX(); 243 pointerCoords[i].y = mTouchPoints.get(i).getY(); 244 } 245 246 MotionEvent event = MotionEvent.obtain(mTouchPoints.get(0).downTime(), 247 SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 248 numPoints, pointerIds, pointerCoords, 249 mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0); 250 mWebView.onTouchEvent(event); 251 252 for (int i = numPoints - 1; i >= 0; --i) { // remove released points. 253 TouchPoint tp = mTouchPoints.get(i); 254 if (tp.isReleased()) { 255 mTouchPoints.remove(i); 256 } 257 } 258 } 259 260 public void touchCancel() { 261 final int numPoints = mTouchPoints.size(); 262 if (numPoints == 0) { 263 return; 264 } 265 266 int[] pointerIds = new int[numPoints]; 267 MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints]; 268 long cancelTime = SystemClock.uptimeMillis(); 269 int numCanceledPoints = 0; 270 271 for (int i = 0; i < numPoints; ++i) { 272 TouchPoint tp = mTouchPoints.get(i); 273 if (tp.cancelled()) { 274 pointerIds[numCanceledPoints] = mTouchPoints.get(i).getId(); 275 pointerCoords[numCanceledPoints] = new MotionEvent.PointerCoords(); 276 pointerCoords[numCanceledPoints].x = mTouchPoints.get(i).getX(); 277 pointerCoords[numCanceledPoints].y = mTouchPoints.get(i).getY(); 278 ++numCanceledPoints; 279 } 280 } 281 282 if (numCanceledPoints == 0) { 283 return; 284 } 285 286 MotionEvent event = MotionEvent.obtain(mTouchPoints.get(0).downTime(), 287 SystemClock.uptimeMillis(), MotionEvent.ACTION_CANCEL, 288 numCanceledPoints, pointerIds, pointerCoords, 289 mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0); 290 291 mWebView.onTouchEvent(event); 292 } 293 294 public void cancelTouchPoint(int id) { 295 TouchPoint tp = mTouchPoints.get(id); 296 if (tp == null) { 297 return; 298 } 299 300 tp.cancel(); 301 } 302 303 public void addTouchPoint(int x, int y) { 304 final int numPoints = mTouchPoints.size(); 305 int id; 306 if (numPoints == 0) { 307 id = 0; 308 } else { 309 id = mTouchPoints.get(numPoints - 1).getId() + 1; 310 } 311 312 mTouchPoints.add(new TouchPoint(id, contentsToWindowX(x), contentsToWindowY(y))); 313 } 314 315 public void updateTouchPoint(int i, int x, int y) { 316 TouchPoint tp = mTouchPoints.get(i); 317 if (tp == null) { 318 return; 319 } 320 321 tp.update(contentsToWindowX(x), contentsToWindowY(y)); 322 tp.setMoved(true); 323 } 324 325 public void setTouchModifier(String modifier, boolean enabled) { 326 int mask = 0; 327 if ("alt".equals(modifier.toLowerCase())) { 328 mask = KeyEvent.META_ALT_ON; 329 } else if ("shift".equals(modifier.toLowerCase())) { 330 mask = KeyEvent.META_SHIFT_ON; 331 } else if ("ctrl".equals(modifier.toLowerCase())) { 332 mask = KeyEvent.META_SYM_ON; 333 } 334 335 if (enabled) { 336 mTouchMetaState |= mask; 337 } else { 338 mTouchMetaState &= ~mask; 339 } 340 } 341 342 public void releaseTouchPoint(int id) { 343 TouchPoint tp = mTouchPoints.get(id); 344 if (tp == null) { 345 return; 346 } 347 348 tp.release(); 349 } 350 351 public void clearTouchPoints() { 352 mTouchPoints.clear(); 353 } 354 355 public void clearTouchMetaState() { 356 mTouchMetaState = 0; 357 } 358 359 private int contentsToWindowX(int x) { 360 return Math.round(x * mWebView.getScale()) - mWebView.getScrollX(); 361 } 362 363 private int contentsToWindowY(int y) { 364 return Math.round(y * mWebView.getScale()) - mWebView.getScrollY(); 365 } 366 367 private WebView mWebView = null; 368 private int mouseX; 369 private int mouseY; 370 371 private class TouchPoint { 372 private int mId; 373 private int mX; 374 private int mY; 375 private long mDownTime; 376 private boolean mReleased; 377 private boolean mMoved; 378 private boolean mCancelled; 379 380 public TouchPoint(int id, int x, int y) { 381 mId = id; 382 mX = x; 383 mY = y; 384 mReleased = false; 385 mMoved = false; 386 mCancelled = false; 387 } 388 389 public void setDownTime(long downTime) { mDownTime = downTime; } 390 public long downTime() { return mDownTime; } 391 public void cancel() { mCancelled = true; } 392 393 public boolean cancelled() { return mCancelled; } 394 395 public void release() { mReleased = true; } 396 public boolean isReleased() { return mReleased; } 397 398 public void setMoved(boolean moved) { mMoved = moved; } 399 public boolean hasMoved() { return mMoved; } 400 401 public int getId() { return mId; } 402 public int getX() { return mX; } 403 public int getY() { return mY; } 404 405 public void update(int x, int y) { 406 mX = x; 407 mY = y; 408 } 409 } 410 411 private Vector<TouchPoint> mTouchPoints; 412 private int mTouchMetaState; 413 } 414