1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.documentsui.bots; 18 19 import android.app.UiAutomation; 20 import android.content.Context; 21 import android.graphics.Point; 22 import android.graphics.Rect; 23 import android.os.SystemClock; 24 import android.support.test.uiautomator.Configurator; 25 import android.support.test.uiautomator.UiDevice; 26 import android.support.test.uiautomator.UiObject; 27 import android.support.test.uiautomator.UiObjectNotFoundException; 28 import android.support.test.uiautomator.UiSelector; 29 import android.view.InputDevice; 30 import android.view.MotionEvent; 31 import android.view.MotionEvent.PointerCoords; 32 import android.view.MotionEvent.PointerProperties; 33 34 /** 35 * A test helper class that provides support for controlling directory list 36 * and making assertions against the state of it. 37 */ 38 public class GestureBot extends Bots.BaseBot { 39 private static final String DIR_CONTAINER_ID = "com.android.documentsui:id/container_directory"; 40 private static final String DIR_LIST_ID = "com.android.documentsui:id/dir_list"; 41 private static final int LONGPRESS_STEPS = 60; 42 private static final int TRAVELING_STEPS = 20; 43 private static final int BAND_SELECTION_DEFAULT_STEPS = 100; 44 private static final int STEPS_INBETWEEN_POINTS = 2; 45 // Inserted after each motion event injection. 46 private static final int MOTION_EVENT_INJECTION_DELAY_MILLIS = 5; 47 private final UiAutomation mAutomation; 48 private long mDownTime = 0; 49 GestureBot(UiDevice device, UiAutomation automation, Context context, int timeout)50 public GestureBot(UiDevice device, UiAutomation automation, Context context, int timeout) { 51 super(device, context, timeout); 52 mAutomation = automation; 53 } 54 gestureSelectFiles(String startLabel, String endLabel)55 public void gestureSelectFiles(String startLabel, String endLabel) throws Exception { 56 int toolType = Configurator.getInstance().getToolType(); 57 Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER); 58 Rect startCoord = findDocument(startLabel).getBounds(); 59 Rect endCoord = findDocument(endLabel).getBounds(); 60 double diffX = endCoord.centerX() - startCoord.centerX(); 61 double diffY = endCoord.centerY() - startCoord.centerY(); 62 Point[] points = new Point[LONGPRESS_STEPS + TRAVELING_STEPS]; 63 64 // First simulate long-press by having a bunch of MOVE events in the same coordinate 65 for (int i = 0; i < LONGPRESS_STEPS; i++) { 66 points[i] = new Point(startCoord.centerX(), startCoord.centerY()); 67 } 68 69 // Next put the actual drag/move events 70 for (int i = 0; i < TRAVELING_STEPS; i++) { 71 int newX = startCoord.centerX() + (int) (diffX / TRAVELING_STEPS * i); 72 int newY = startCoord.centerY() + (int) (diffY / TRAVELING_STEPS * i); 73 points[i + LONGPRESS_STEPS] = new Point(newX, newY); 74 } 75 mDevice.swipe(points, STEPS_INBETWEEN_POINTS); 76 Configurator.getInstance().setToolType(toolType); 77 } 78 bandSelection(Point start, Point end)79 public void bandSelection(Point start, Point end) throws Exception { 80 bandSelection(start, end, BAND_SELECTION_DEFAULT_STEPS); 81 } 82 bandSelection(Point start, Point end, int steps)83 public void bandSelection(Point start, Point end, int steps) throws Exception { 84 int toolType = Configurator.getInstance().getToolType(); 85 Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE); 86 swipe(start.x, start.y, end.x, end.y, steps, MotionEvent.BUTTON_PRIMARY); 87 Configurator.getInstance().setToolType(toolType); 88 } 89 findDocument(String label)90 public UiObject findDocument(String label) throws UiObjectNotFoundException { 91 final UiSelector docList = new UiSelector().resourceId( 92 DIR_CONTAINER_ID).childSelector( 93 new UiSelector().resourceId(DIR_LIST_ID)); 94 95 // Wait for the first list item to appear 96 new UiObject(docList.childSelector(new UiSelector())).waitForExists(mTimeout); 97 98 return mDevice.findObject(docList.childSelector(new UiSelector().text(label))); 99 } 100 swipe(int downX, int downY, int upX, int upY, int steps, int button)101 private void swipe(int downX, int downY, int upX, int upY, int steps, int button) { 102 int swipeSteps = steps; 103 double xStep = 0; 104 double yStep = 0; 105 106 // avoid a divide by zero 107 if(swipeSteps == 0) 108 swipeSteps = 1; 109 110 xStep = ((double)(upX - downX)) / swipeSteps; 111 yStep = ((double)(upY - downY)) / swipeSteps; 112 113 // first touch starts exactly at the point requested 114 touchDown(downX, downY, button); 115 for(int i = 1; i < swipeSteps; i++) { 116 touchMove(downX + (int)(xStep * i), downY + (int)(yStep * i), button); 117 // set some known constant delay between steps as without it this 118 // become completely dependent on the speed of the system and results 119 // may vary on different devices. This guarantees at minimum we have 120 // a preset delay. 121 SystemClock.sleep(MOTION_EVENT_INJECTION_DELAY_MILLIS); 122 } 123 touchUp(upX, upY); 124 } 125 touchDown(int x, int y, int button)126 private boolean touchDown(int x, int y, int button) { 127 long mDownTime = SystemClock.uptimeMillis(); 128 MotionEvent event = getMotionEvent(mDownTime, mDownTime, MotionEvent.ACTION_DOWN, button, x, 129 y); 130 return mAutomation.injectInputEvent(event, true); 131 } 132 touchUp(int x, int y)133 private boolean touchUp(int x, int y) { 134 final long eventTime = SystemClock.uptimeMillis(); 135 MotionEvent event = getMotionEvent(mDownTime, eventTime, MotionEvent.ACTION_UP, 0, x, y); 136 mDownTime = 0; 137 return mAutomation.injectInputEvent(event, true); 138 } 139 touchMove(int x, int y, int button)140 private boolean touchMove(int x, int y, int button) { 141 final long eventTime = SystemClock.uptimeMillis(); 142 MotionEvent event = getMotionEvent(mDownTime, eventTime, MotionEvent.ACTION_MOVE, button, x, 143 y); 144 return mAutomation.injectInputEvent(event, true); 145 } 146 147 /** Helper function to obtain a MotionEvent. */ getMotionEvent(long downTime, long eventTime, int action, int button, float x, float y)148 private static MotionEvent getMotionEvent(long downTime, long eventTime, int action, int button, 149 float x, float y) { 150 151 PointerProperties properties = new PointerProperties(); 152 properties.id = 0; 153 properties.toolType = Configurator.getInstance().getToolType(); 154 155 PointerCoords coords = new PointerCoords(); 156 coords.pressure = 1; 157 coords.size = 1; 158 coords.x = x; 159 coords.y = y; 160 161 return MotionEvent.obtain(downTime, eventTime, action, 1, 162 new PointerProperties[] { properties }, new PointerCoords[] { coords }, 163 0, button, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0); 164 } 165 } 166