blob: 2af861a8689e679904e8bf0277b48184c26ebcb7 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chromoting;
import android.graphics.PointF;
import android.os.SystemClock;
import android.util.SparseArray;
import android.view.InputDevice;
import android.view.MotionEvent;
/**
* Utility for creating MotionEvents for multi-touch event simulation. The events are created by
* calling MotionEvent.obtain(...) with suitable parameters.
*/
public class TouchEventGenerator {
/**
* Stores the current set of held-down fingers. This is a sparse array since the indices of the
* fingers are not contiguous. For example, if fingers 0, 1, 2 are pressed then finger 1 is
* lifted, the device ids 0, 2 would correspond to the indexes 0, 1.
*/
private SparseArray<PointF> mFingerPositions = new SparseArray<PointF>();
/**
* The event's DownTime. This is set to the system's current time when the first finger is
* pressed.
*/
private long mDownTime;
/**
* Creates a fake event with the given action and device id. The event is generated using the
* information in |mFingerPositions|.
*
* @param actionMasked The (masked) action to generate, for example, ACTION_POINTER_DOWN.
* @param id The device id of the event. There must already be an entry for |id| in
* |mFingerPositions|, so that |id| can be converted to an index and combined with
* |actionMasked| to set the new event's |action| property.
*/
private MotionEvent obtainEvent(int actionMasked, int id) {
int actionIndex = mFingerPositions.indexOfKey(id);
assert actionIndex >= 0;
int action = (actionIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) | actionMasked;
long eventTime = SystemClock.uptimeMillis();
int size = mFingerPositions.size();
// Generate the arrays of pointers and positions for the event.
MotionEvent.PointerProperties[] pointers = new MotionEvent.PointerProperties[size];
MotionEvent.PointerCoords[] positions = new MotionEvent.PointerCoords[size];
for (int i = 0; i < size; i++) {
int key = mFingerPositions.keyAt(i);
PointF position = mFingerPositions.valueAt(i);
pointers[i] = new MotionEvent.PointerProperties();
pointers[i].id = key;
positions[i] = new MotionEvent.PointerCoords();
positions[i].x = position.x;
positions[i].y = position.y;
}
return MotionEvent.obtain(mDownTime, eventTime, action, size, pointers, positions,
0, 0, 1, 1, id, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
}
/**
* Obtains a finger-down event.
*
* @param id The device id of the new finger that is pressed. The caller must ensure this is
* a finger not currently held down.
* @param x The x-coordinate of the new finger position.
* @param y The y-coordinate of the new finger position.
*/
public MotionEvent obtainDownEvent(int id, float x, float y) {
assert mFingerPositions.get(id) == null;
mFingerPositions.put(id, new PointF(x, y));
int actionMasked;
if (mFingerPositions.size() == 1) {
mDownTime = SystemClock.uptimeMillis();
actionMasked = MotionEvent.ACTION_DOWN;
} else {
actionMasked = MotionEvent.ACTION_POINTER_DOWN;
}
return obtainEvent(actionMasked, id);
}
/**
* Obtains a finger-up event.
*
* @param id The device id of the finger to be lifted. The caller must ensure this is a
* finger previously held down.
*/
public MotionEvent obtainUpEvent(int id) {
assert mFingerPositions.get(id) != null;
int actionMasked = mFingerPositions.size() == 1
? MotionEvent.ACTION_UP : MotionEvent.ACTION_POINTER_UP;
MotionEvent event = obtainEvent(actionMasked, id);
mFingerPositions.remove(id);
return event;
}
/**
* Obtains a finger-move event. This moves a single finger, keeping any others in the same
* same position.
*
* @param id The device id of the finger being moved. The caller must ensure this is a
* finger previously held down.
* @param x The x-coordinate of the new finger position.
* @param y The y-coordinate of the new finger position.
*/
public MotionEvent obtainMoveEvent(int id, float x, float y) {
PointF position = mFingerPositions.get(id);
assert position != null;
position.x = x;
position.y = y;
return obtainEvent(MotionEvent.ACTION_MOVE, id);
}
}