blob: 370fed28e1b25bacb535ac93983e552b9cba23bc [file] [log] [blame]
/*
* Copyright (C) 2015 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.benchmark.ui.automation;
import android.os.SystemClock;
import android.support.annotation.IntDef;
import android.view.MotionEvent;
import java.util.ArrayList;
import java.util.List;
/**
* Encodes a UI interaction as a series of MotionEvents
*/
public class Interaction {
private static final int STEP_COUNT = 20;
// TODO: scale to device display density
private static final int DEFAULT_FLING_SIZE_PX = 500;
private static final int DEFAULT_FLING_DURATION_MS = 20;
private static final int DEFAULT_TAP_DURATION_MS = 20;
private List<MotionEvent> mEvents;
// Interaction parameters
private final float[] mXPositions;
private final float[] mYPositions;
private final long mDuration;
private final int[] mKeyCodes;
private final @Interaction.Type int mType;
@IntDef({
Interaction.Type.TAP,
Interaction.Type.FLING,
Interaction.Type.PINCH,
Interaction.Type.KEY_EVENT})
public @interface Type {
int TAP = 0;
int FLING = 1;
int PINCH = 2;
int KEY_EVENT = 3;
}
public static Interaction newFling(float startX, float startY,
float endX, float endY, long duration) {
return new Interaction(Interaction.Type.FLING, new float[]{startX, endX},
new float[]{startY, endY}, duration);
}
public static Interaction newFlingDown(float startX, float startY) {
return new Interaction(Interaction.Type.FLING,
new float[]{startX, startX},
new float[]{startY, startY + DEFAULT_FLING_SIZE_PX}, DEFAULT_FLING_DURATION_MS);
}
public static Interaction newFlingUp(float startX, float startY) {
return new Interaction(Interaction.Type.FLING,
new float[]{startX, startX}, new float[]{startY, startY - DEFAULT_FLING_SIZE_PX},
DEFAULT_FLING_DURATION_MS);
}
public static Interaction newTap(float startX, float startY) {
return new Interaction(Interaction.Type.TAP,
new float[]{startX, startX}, new float[]{startY, startY},
DEFAULT_FLING_DURATION_MS);
}
public static Interaction newKeyInput(int[] keyCodes) {
return new Interaction(keyCodes);
}
public List<MotionEvent> getEvents() {
switch (mType) {
case Type.FLING:
mEvents = createInterpolatedEventList(mXPositions, mYPositions, mDuration);
break;
case Type.PINCH:
break;
case Type.TAP:
mEvents = createInterpolatedEventList(mXPositions, mYPositions, mDuration);
break;
}
return mEvents;
}
public int getType() {
return mType;
}
public int[] getKeyCodes() {
return mKeyCodes;
}
private static List<MotionEvent> createInterpolatedEventList(
float[] xPos, float[] yPos, long duration) {
long startTime = SystemClock.uptimeMillis() + 100;
List<MotionEvent> result = new ArrayList<>();
float startX = xPos[0];
float startY = yPos[0];
MotionEvent downEvent = MotionEvent.obtain(
startTime, startTime, MotionEvent.ACTION_DOWN, startX, startY, 0);
result.add(downEvent);
for (int i = 1; i < xPos.length; i++) {
float endX = xPos[i];
float endY = yPos[i];
float stepX = (endX - startX) / STEP_COUNT;
float stepY = (endY - startY) / STEP_COUNT;
float stepT = duration / STEP_COUNT;
for (int j = 0; j < STEP_COUNT; j++) {
long deltaT = Math.round(j * stepT);
long deltaX = Math.round(j * stepX);
long deltaY = Math.round(j * stepY);
MotionEvent moveEvent = MotionEvent.obtain(startTime, startTime + deltaT,
MotionEvent.ACTION_MOVE, startX + deltaX, startY + deltaY, 0);
result.add(moveEvent);
}
startX = endX;
startY = endY;
}
float lastX = xPos[xPos.length - 1];
float lastY = yPos[yPos.length - 1];
MotionEvent lastEvent = MotionEvent.obtain(startTime, startTime + duration,
MotionEvent.ACTION_UP, lastX, lastY, 0);
result.add(lastEvent);
return result;
}
private Interaction(@Interaction.Type int type,
float[] xPos, float[] yPos, long duration) {
mType = type;
mXPositions = xPos;
mYPositions = yPos;
mDuration = duration;
mKeyCodes = null;
}
private Interaction(int[] codes) {
mKeyCodes = codes;
mType = Type.KEY_EVENT;
mYPositions = null;
mXPositions = null;
mDuration = 0;
}
private Interaction(@Interaction.Type int type,
List<Float> xPositions, List<Float> yPositions, long duration) {
if (xPositions.size() != yPositions.size()) {
throw new IllegalArgumentException("must have equal number of x and y positions");
}
int current = 0;
mXPositions = new float[xPositions.size()];
for (float p : xPositions) {
mXPositions[current++] = p;
}
current = 0;
mYPositions = new float[yPositions.size()];
for (float p : xPositions) {
mXPositions[current++] = p;
}
mType = type;
mDuration = duration;
mKeyCodes = null;
}
}