summaryrefslogtreecommitdiffstats
path: root/tests/src/com/android/launcher3/testcomponent/TouchEventGenerator.java
diff options
context:
space:
mode:
Diffstat (limited to 'tests/src/com/android/launcher3/testcomponent/TouchEventGenerator.java')
-rw-r--r--tests/src/com/android/launcher3/testcomponent/TouchEventGenerator.java275
1 files changed, 275 insertions, 0 deletions
diff --git a/tests/src/com/android/launcher3/testcomponent/TouchEventGenerator.java b/tests/src/com/android/launcher3/testcomponent/TouchEventGenerator.java
new file mode 100644
index 000000000..80d6341e7
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/TouchEventGenerator.java
@@ -0,0 +1,275 @@
+/*
+ * 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;
+ }
+}