blob: 9a0fb6ea1a1deb6a7812d53d8d334992a7e356de [file] [log] [blame]
/*
* Copyright (C) 2016 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.documentsui.bots;
import android.app.UiAutomation;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
import android.support.test.uiautomator.Configurator;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObjectNotFoundException;
import android.support.test.uiautomator.UiSelector;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
/**
* A test helper class that provides support for controlling directory list
* and making assertions against the state of it.
*/
public class GestureBot extends Bots.BaseBot {
private static final int LONGPRESS_STEPS = 60;
private static final int TRAVELING_STEPS = 20;
private static final int BAND_SELECTION_DEFAULT_STEPS = 100;
private static final int STEPS_INBETWEEN_POINTS = 2;
// Inserted after each motion event injection.
private static final int MOTION_EVENT_INJECTION_DELAY_MILLIS = 5;
private static final int LONG_PRESS_EVENT_INJECTION_DELAY_MILIS = 1000;
private final String mDirContainerId;
private final String mDirListId;
private final UiAutomation mAutomation;
private long mDownTime = 0;
public GestureBot(UiDevice device, UiAutomation automation, Context context, int timeout) {
super(device, context, timeout);
mDirContainerId = mTargetPackage + ":id/container_directory";
mDirListId = mTargetPackage + ":id/dir_list";
mAutomation = automation;
}
public void gestureSelectFiles(String startLabel, String endLabel) throws Exception {
int toolType = Configurator.getInstance().getToolType();
Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER);
Rect startCoord = findDocument(startLabel).getBounds();
Rect endCoord = findDocument(endLabel).getBounds();
double diffX = endCoord.centerX() - startCoord.centerX();
double diffY = endCoord.centerY() - startCoord.centerY();
Point[] points = new Point[LONGPRESS_STEPS + TRAVELING_STEPS];
// First simulate long-press by having a bunch of MOVE events in the same coordinate
for (int i = 0; i < LONGPRESS_STEPS; i++) {
points[i] = new Point(startCoord.centerX(), startCoord.centerY());
}
// Next put the actual drag/move events
for (int i = 0; i < TRAVELING_STEPS; i++) {
int newX = startCoord.centerX() + (int) (diffX / TRAVELING_STEPS * i);
int newY = startCoord.centerY() + (int) (diffY / TRAVELING_STEPS * i);
points[i + LONGPRESS_STEPS] = new Point(newX, newY);
}
mDevice.swipe(points, STEPS_INBETWEEN_POINTS);
Configurator.getInstance().setToolType(toolType);
}
public void bandSelection(Point start, Point end) throws Exception {
bandSelection(start, end, BAND_SELECTION_DEFAULT_STEPS);
}
public void fingerSelection(Point start, Point end) throws Exception {
fingerSelection(start, end, BAND_SELECTION_DEFAULT_STEPS);
}
public void bandSelection(Point start, Point end, int steps) throws Exception {
int toolType = Configurator.getInstance().getToolType();
Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE);
swipe(start.x, start.y, end.x, end.y, steps, MotionEvent.BUTTON_PRIMARY, false);
Configurator.getInstance().setToolType(toolType);
}
private void fingerSelection(Point start, Point end, int steps) throws Exception {
int toolType = Configurator.getInstance().getToolType();
Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER);
swipe(start.x, start.y, end.x, end.y, steps, MotionEvent.BUTTON_PRIMARY, true);
Configurator.getInstance().setToolType(toolType);
}
public UiObject findDocument(String label) throws UiObjectNotFoundException {
final UiSelector docList = new UiSelector().resourceId(
mDirContainerId).childSelector(
new UiSelector().resourceId(mDirListId));
// Wait for the first list item to appear
new UiObject(docList.childSelector(new UiSelector())).waitForExists(mTimeout);
return mDevice.findObject(docList.childSelector(new UiSelector().text(label)));
}
private void swipe(int downX, int downY, int upX, int upY, int steps, int button,
boolean fingerSelection) {
int swipeSteps = steps;
double xStep = 0;
double yStep = 0;
// avoid a divide by zero
if (swipeSteps == 0) {
swipeSteps = 1;
}
xStep = ((double) (upX - downX)) / swipeSteps;
yStep = ((double) (upY - downY)) / swipeSteps;
// first touch starts exactly at the point requested
touchDown(downX, downY, button);
if (fingerSelection) {
SystemClock.sleep(LONG_PRESS_EVENT_INJECTION_DELAY_MILIS);
}
for (int i = 1; i < swipeSteps; i++) {
touchMove(downX + (int) (xStep * i), downY + (int) (yStep * i), button);
// set some known constant delay between steps as without it this
// become completely dependent on the speed of the system and results
// may vary on different devices. This guarantees at minimum we have
// a preset delay.
SystemClock.sleep(MOTION_EVENT_INJECTION_DELAY_MILLIS);
}
touchUp(upX, upY);
}
private boolean touchDown(int x, int y, int button) {
long mDownTime = SystemClock.uptimeMillis();
MotionEvent event = getMotionEvent(mDownTime, mDownTime, MotionEvent.ACTION_DOWN, button, x,
y);
return mAutomation.injectInputEvent(event, true);
}
private boolean touchUp(int x, int y) {
final long eventTime = SystemClock.uptimeMillis();
MotionEvent event = getMotionEvent(mDownTime, eventTime, MotionEvent.ACTION_UP, 0, x, y);
mDownTime = 0;
return mAutomation.injectInputEvent(event, true);
}
private boolean touchMove(int x, int y, int button) {
final long eventTime = SystemClock.uptimeMillis();
MotionEvent event = getMotionEvent(mDownTime, eventTime, MotionEvent.ACTION_MOVE, button, x,
y);
return mAutomation.injectInputEvent(event, true);
}
/** Helper function to obtain a MotionEvent. */
private static MotionEvent getMotionEvent(long downTime, long eventTime, int action, int button,
float x, float y) {
PointerProperties properties = new PointerProperties();
properties.id = 0;
properties.toolType = Configurator.getInstance().getToolType();
PointerCoords coords = new PointerCoords();
coords.pressure = 1;
coords.size = 1;
coords.x = x;
coords.y = y;
return MotionEvent.obtain(downTime, eventTime, action, 1,
new PointerProperties[]{properties}, new PointerCoords[]{coords},
0, button, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
}
}