f40e94955c
Change-Id: I09ab4f22d7204ad806825ab0d6374c2b9616bf39
276 lines
7.9 KiB
Java
276 lines
7.9 KiB
Java
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.android.launcher3.testcomponent;
|
|
|
|
import android.graphics.Point;
|
|
import android.util.Pair;
|
|
import android.view.InputDevice;
|
|
import android.view.MotionEvent;
|
|
import android.view.MotionEvent.PointerCoords;
|
|
import android.view.MotionEvent.PointerProperties;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* Utility class to generate MotionEvent event sequences for testing touch gesture detectors.
|
|
*/
|
|
public class TouchEventGenerator {
|
|
|
|
/**
|
|
* Amount of time between two generated events.
|
|
*/
|
|
private static final long TIME_INCREMENT_MS = 20L;
|
|
|
|
/**
|
|
* Id of the fake device generating the events.
|
|
*/
|
|
private static final int DEVICE_ID = 2104;
|
|
|
|
/**
|
|
* The fingers currently present on the emulated touch screen.
|
|
*/
|
|
private Map<Integer, Point> mFingers;
|
|
|
|
/**
|
|
* Initial event time for the current sequence.
|
|
*/
|
|
private long mInitialTime;
|
|
|
|
/**
|
|
* Time of the last generated event.
|
|
*/
|
|
private long mLastEventTime;
|
|
|
|
/**
|
|
* Time of the next event.
|
|
*/
|
|
private long mTime;
|
|
|
|
/**
|
|
* Receives the generated events.
|
|
*/
|
|
public interface Listener {
|
|
|
|
/**
|
|
* Called when an event was generated.
|
|
*/
|
|
void onTouchEvent(MotionEvent event);
|
|
}
|
|
private final Listener mListener;
|
|
|
|
public TouchEventGenerator(Listener listener) {
|
|
mListener = listener;
|
|
mFingers = new HashMap<Integer, Point>();
|
|
}
|
|
|
|
/**
|
|
* Adds a finger on the touchscreen.
|
|
*/
|
|
public TouchEventGenerator put(int id, int x, int y, long ms) {
|
|
checkFingerExistence(id, false);
|
|
boolean isInitialDown = mFingers.isEmpty();
|
|
mFingers.put(id, new Point(x, y));
|
|
int action;
|
|
if (isInitialDown) {
|
|
action = MotionEvent.ACTION_DOWN;
|
|
} else {
|
|
action = MotionEvent.ACTION_POINTER_DOWN;
|
|
// Set the id of the changed pointer.
|
|
action |= id << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
|
}
|
|
generateEvent(action, ms);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Adds a finger on the touchscreen after advancing default time interval.
|
|
*/
|
|
public TouchEventGenerator put(int id, int x, int y) {
|
|
return put(id, x, y, TIME_INCREMENT_MS);
|
|
}
|
|
|
|
/**
|
|
* Adjusts the position of a finger for an upcoming move event.
|
|
*
|
|
* @see #move(long ms)
|
|
*/
|
|
public TouchEventGenerator position(int id, int x, int y) {
|
|
checkFingerExistence(id, true);
|
|
mFingers.get(id).set(x, y);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Commits the finger position changes of {@link #position(int, int, int)} by generating a move
|
|
* event.
|
|
*
|
|
* @see #position(int, int, int)
|
|
*/
|
|
public TouchEventGenerator move(long ms) {
|
|
generateEvent(MotionEvent.ACTION_MOVE, ms);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Commits the finger position changes of {@link #position(int, int, int)} by generating a move
|
|
* event after advancing the default time interval.
|
|
*
|
|
* @see #position(int, int, int)
|
|
*/
|
|
public TouchEventGenerator move() {
|
|
return move(TIME_INCREMENT_MS);
|
|
}
|
|
|
|
/**
|
|
* Moves a single finger on the touchscreen.
|
|
*/
|
|
public TouchEventGenerator move(int id, int x, int y, long ms) {
|
|
return position(id, x, y).move(ms);
|
|
}
|
|
|
|
/**
|
|
* Moves a single finger on the touchscreen after advancing default time interval.
|
|
*/
|
|
public TouchEventGenerator move(int id, int x, int y) {
|
|
return move(id, x, y, TIME_INCREMENT_MS);
|
|
}
|
|
|
|
/**
|
|
* Removes an existing finger from the touchscreen.
|
|
*/
|
|
public TouchEventGenerator lift(int id, long ms) {
|
|
checkFingerExistence(id, true);
|
|
boolean isFinalUp = mFingers.size() == 1;
|
|
int action;
|
|
if (isFinalUp) {
|
|
action = MotionEvent.ACTION_UP;
|
|
} else {
|
|
action = MotionEvent.ACTION_POINTER_UP;
|
|
// Set the id of the changed pointer.
|
|
action |= id << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
|
}
|
|
generateEvent(action, ms);
|
|
mFingers.remove(id);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Removes a finger from the touchscreen.
|
|
*/
|
|
public TouchEventGenerator lift(int id, int x, int y, long ms) {
|
|
checkFingerExistence(id, true);
|
|
mFingers.get(id).set(x, y);
|
|
return lift(id, ms);
|
|
}
|
|
|
|
/**
|
|
* Removes an existing finger from the touchscreen after advancing default time interval.
|
|
*/
|
|
public TouchEventGenerator lift(int id) {
|
|
return lift(id, TIME_INCREMENT_MS);
|
|
}
|
|
|
|
/**
|
|
* Cancels an ongoing sequence.
|
|
*/
|
|
public TouchEventGenerator cancel(long ms) {
|
|
generateEvent(MotionEvent.ACTION_CANCEL, ms);
|
|
mFingers.clear();
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Cancels an ongoing sequence.
|
|
*/
|
|
public TouchEventGenerator cancel() {
|
|
return cancel(TIME_INCREMENT_MS);
|
|
}
|
|
|
|
private void checkFingerExistence(int id, boolean shouldExist) {
|
|
if (shouldExist != mFingers.containsKey(id)) {
|
|
throw new IllegalArgumentException(
|
|
shouldExist ? "Finger does not exist" : "Finger already exists");
|
|
}
|
|
}
|
|
|
|
private void generateEvent(int action, long ms) {
|
|
mTime = mLastEventTime + ms;
|
|
Pair<PointerProperties[], PointerCoords[]> state = getFingerState();
|
|
MotionEvent event = MotionEvent.obtain(
|
|
mInitialTime,
|
|
mTime,
|
|
action,
|
|
state.first.length,
|
|
state.first,
|
|
state.second,
|
|
0 /* metaState */,
|
|
0 /* buttonState */,
|
|
1.0f /* xPrecision */,
|
|
1.0f /* yPrecision */,
|
|
DEVICE_ID,
|
|
0 /* edgeFlags */,
|
|
InputDevice.SOURCE_TOUCHSCREEN,
|
|
0 /* flags */);
|
|
mListener.onTouchEvent(event);
|
|
if (action == MotionEvent.ACTION_UP) {
|
|
resetTime();
|
|
}
|
|
event.recycle();
|
|
mLastEventTime = mTime;
|
|
}
|
|
|
|
/**
|
|
* Returns the description of the fingers' state expected by MotionEvent.
|
|
*/
|
|
private Pair<PointerProperties[], PointerCoords[]> getFingerState() {
|
|
int nFingers = mFingers.size();
|
|
PointerProperties[] properties = new PointerProperties[nFingers];
|
|
PointerCoords[] coordinates = new PointerCoords[nFingers];
|
|
|
|
int index = 0;
|
|
for (Map.Entry<Integer, Point> entry : mFingers.entrySet()) {
|
|
int id = entry.getKey();
|
|
Point location = entry.getValue();
|
|
|
|
PointerProperties property = new PointerProperties();
|
|
property.id = id;
|
|
property.toolType = MotionEvent.TOOL_TYPE_FINGER;
|
|
properties[index] = property;
|
|
|
|
PointerCoords coordinate = new PointerCoords();
|
|
coordinate.x = location.x;
|
|
coordinate.y = location.y;
|
|
coordinate.pressure = 1.0f;
|
|
coordinates[index] = coordinate;
|
|
|
|
index++;
|
|
}
|
|
|
|
return new Pair<MotionEvent.PointerProperties[], MotionEvent.PointerCoords[]>(
|
|
properties, coordinates);
|
|
}
|
|
|
|
/**
|
|
* Resets the time references for a new sequence.
|
|
*/
|
|
private void resetTime() {
|
|
mInitialTime = 0L;
|
|
mLastEventTime = -1L;
|
|
mTime = 0L;
|
|
}
|
|
}
|