1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chromoting; 6 7 import android.graphics.PointF; 8 import android.os.SystemClock; 9 import android.util.SparseArray; 10 import android.view.InputDevice; 11 import android.view.MotionEvent; 12 13 /** 14 * Utility for creating MotionEvents for multi-touch event simulation. The events are created by 15 * calling MotionEvent.obtain(...) with suitable parameters. 16 */ 17 public class TouchEventGenerator { 18 /** 19 * Stores the current set of held-down fingers. This is a sparse array since the indices of the 20 * fingers are not contiguous. For example, if fingers 0, 1, 2 are pressed then finger 1 is 21 * lifted, the device ids 0, 2 would correspond to the indexes 0, 1. 22 */ 23 private SparseArray<PointF> mFingerPositions = new SparseArray<PointF>(); 24 25 /** 26 * The event's DownTime. This is set to the system's current time when the first finger is 27 * pressed. 28 */ 29 private long mDownTime; 30 31 /** 32 * Creates a fake event with the given action and device id. The event is generated using the 33 * information in |mFingerPositions|. 34 * 35 * @param actionMasked The (masked) action to generate, for example, ACTION_POINTER_DOWN. 36 * @param id The device id of the event. There must already be an entry for |id| in 37 * |mFingerPositions|, so that |id| can be converted to an index and combined with 38 * |actionMasked| to set the new event's |action| property. 39 */ 40 private MotionEvent obtainEvent(int actionMasked, int id) { 41 int actionIndex = mFingerPositions.indexOfKey(id); 42 assert actionIndex >= 0; 43 int action = (actionIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) | actionMasked; 44 45 long eventTime = SystemClock.uptimeMillis(); 46 int size = mFingerPositions.size(); 47 48 // Generate the arrays of pointers and positions for the event. 49 MotionEvent.PointerProperties[] pointers = new MotionEvent.PointerProperties[size]; 50 MotionEvent.PointerCoords[] positions = new MotionEvent.PointerCoords[size]; 51 for (int i = 0; i < size; i++) { 52 int key = mFingerPositions.keyAt(i); 53 PointF position = mFingerPositions.valueAt(i); 54 55 pointers[i] = new MotionEvent.PointerProperties(); 56 pointers[i].id = key; 57 58 positions[i] = new MotionEvent.PointerCoords(); 59 positions[i].x = position.x; 60 positions[i].y = position.y; 61 } 62 63 return MotionEvent.obtain(mDownTime, eventTime, action, size, pointers, positions, 64 0, 0, 1, 1, id, 0, InputDevice.SOURCE_TOUCHSCREEN, 0); 65 } 66 67 /** 68 * Obtains a finger-down event. 69 * 70 * @param id The device id of the new finger that is pressed. The caller must ensure this is 71 * a finger not currently held down. 72 * @param x The x-coordinate of the new finger position. 73 * @param y The y-coordinate of the new finger position. 74 */ 75 public MotionEvent obtainDownEvent(int id, float x, float y) { 76 assert mFingerPositions.get(id) == null; 77 mFingerPositions.put(id, new PointF(x, y)); 78 int actionMasked; 79 if (mFingerPositions.size() == 1) { 80 mDownTime = SystemClock.uptimeMillis(); 81 actionMasked = MotionEvent.ACTION_DOWN; 82 } else { 83 actionMasked = MotionEvent.ACTION_POINTER_DOWN; 84 } 85 return obtainEvent(actionMasked, id); 86 } 87 88 /** 89 * Obtains a finger-up event. 90 * 91 * @param id The device id of the finger to be lifted. The caller must ensure this is a 92 * finger previously held down. 93 */ 94 public MotionEvent obtainUpEvent(int id) { 95 assert mFingerPositions.get(id) != null; 96 97 int actionMasked = mFingerPositions.size() == 1 98 ? MotionEvent.ACTION_UP : MotionEvent.ACTION_POINTER_UP; 99 100 MotionEvent event = obtainEvent(actionMasked, id); 101 mFingerPositions.remove(id); 102 return event; 103 } 104 105 /** 106 * Obtains a finger-move event. This moves a single finger, keeping any others in the same 107 * same position. 108 * 109 * @param id The device id of the finger being moved. The caller must ensure this is a 110 * finger previously held down. 111 * @param x The x-coordinate of the new finger position. 112 * @param y The y-coordinate of the new finger position. 113 */ 114 public MotionEvent obtainMoveEvent(int id, float x, float y) { 115 PointF position = mFingerPositions.get(id); 116 assert position != null; 117 position.x = x; 118 position.y = y; 119 return obtainEvent(MotionEvent.ACTION_MOVE, id); 120 } 121 } 122