11273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin/* 21273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * Copyright (C) 2016 The Android Open Source Project 31273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * 41273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * Licensed under the Apache License, Version 2.0 (the "License"); 51273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * you may not use this file except in compliance with the License. 61273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * You may obtain a copy of the License at 71273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * 81273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * http://www.apache.org/licenses/LICENSE-2.0 91273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * 101273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * Unless required by applicable law or agreed to in writing, software 111273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * distributed under the License is distributed on an "AS IS" BASIS, 121273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * See the License for the specific language governing permissions and 141273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * limitations under the License. 151273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin */ 161273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin 171273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Linpackage com.android.documentsui.bots; 181273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin 196526b38f2871c8d59eb80f4855138de1313de485Ben Linimport android.app.UiAutomation; 201273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Linimport android.content.Context; 211273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Linimport android.graphics.Point; 221273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Linimport android.graphics.Rect; 236526b38f2871c8d59eb80f4855138de1313de485Ben Linimport android.os.SystemClock; 241273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Linimport android.support.test.uiautomator.Configurator; 251273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Linimport android.support.test.uiautomator.UiDevice; 261273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Linimport android.support.test.uiautomator.UiObject; 271273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Linimport android.support.test.uiautomator.UiObjectNotFoundException; 281273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Linimport android.support.test.uiautomator.UiSelector; 296526b38f2871c8d59eb80f4855138de1313de485Ben Linimport android.view.InputDevice; 301273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Linimport android.view.MotionEvent; 316526b38f2871c8d59eb80f4855138de1313de485Ben Linimport android.view.MotionEvent.PointerCoords; 326526b38f2871c8d59eb80f4855138de1313de485Ben Linimport android.view.MotionEvent.PointerProperties; 331273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin 341273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin/** 351273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * A test helper class that provides support for controlling directory list 361273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin * and making assertions against the state of it. 371273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin */ 381273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Linpublic class GestureBot extends Bots.BaseBot { 391273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin private static final String DIR_CONTAINER_ID = "com.android.documentsui:id/container_directory"; 401273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin private static final String DIR_LIST_ID = "com.android.documentsui:id/dir_list"; 411273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin private static final int LONGPRESS_STEPS = 60; 421273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin private static final int TRAVELING_STEPS = 20; 431273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin private static final int BAND_SELECTION_DEFAULT_STEPS = 100; 441273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin private static final int STEPS_INBETWEEN_POINTS = 2; 456526b38f2871c8d59eb80f4855138de1313de485Ben Lin // Inserted after each motion event injection. 466526b38f2871c8d59eb80f4855138de1313de485Ben Lin private static final int MOTION_EVENT_INJECTION_DELAY_MILLIS = 5; 476526b38f2871c8d59eb80f4855138de1313de485Ben Lin private final UiAutomation mAutomation; 486526b38f2871c8d59eb80f4855138de1313de485Ben Lin private long mDownTime = 0; 491273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin 506526b38f2871c8d59eb80f4855138de1313de485Ben Lin public GestureBot(UiDevice device, UiAutomation automation, Context context, int timeout) { 511273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin super(device, context, timeout); 526526b38f2871c8d59eb80f4855138de1313de485Ben Lin mAutomation = automation; 531273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin } 541273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin 551273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin public void gestureSelectFiles(String startLabel, String endLabel) throws Exception { 561273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin int toolType = Configurator.getInstance().getToolType(); 571273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER); 581273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin Rect startCoord = findDocument(startLabel).getBounds(); 591273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin Rect endCoord = findDocument(endLabel).getBounds(); 601273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin double diffX = endCoord.centerX() - startCoord.centerX(); 611273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin double diffY = endCoord.centerY() - startCoord.centerY(); 621273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin Point[] points = new Point[LONGPRESS_STEPS + TRAVELING_STEPS]; 631273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin 641273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin // First simulate long-press by having a bunch of MOVE events in the same coordinate 651273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin for (int i = 0; i < LONGPRESS_STEPS; i++) { 661273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin points[i] = new Point(startCoord.centerX(), startCoord.centerY()); 671273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin } 681273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin 691273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin // Next put the actual drag/move events 701273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin for (int i = 0; i < TRAVELING_STEPS; i++) { 711273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin int newX = startCoord.centerX() + (int) (diffX / TRAVELING_STEPS * i); 721273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin int newY = startCoord.centerY() + (int) (diffY / TRAVELING_STEPS * i); 731273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin points[i + LONGPRESS_STEPS] = new Point(newX, newY); 741273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin } 751273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin mDevice.swipe(points, STEPS_INBETWEEN_POINTS); 761273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin Configurator.getInstance().setToolType(toolType); 771273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin } 781273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin 791273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin public void bandSelection(Point start, Point end) throws Exception { 801273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin bandSelection(start, end, BAND_SELECTION_DEFAULT_STEPS); 811273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin } 821273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin 831273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin public void bandSelection(Point start, Point end, int steps) throws Exception { 841273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin int toolType = Configurator.getInstance().getToolType(); 851273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE); 866526b38f2871c8d59eb80f4855138de1313de485Ben Lin swipe(start.x, start.y, end.x, end.y, steps, MotionEvent.BUTTON_PRIMARY); 871273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin Configurator.getInstance().setToolType(toolType); 881273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin } 891273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin 901273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin public UiObject findDocument(String label) throws UiObjectNotFoundException { 911273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin final UiSelector docList = new UiSelector().resourceId( 921273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin DIR_CONTAINER_ID).childSelector( 931273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin new UiSelector().resourceId(DIR_LIST_ID)); 941273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin 951273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin // Wait for the first list item to appear 961273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin new UiObject(docList.childSelector(new UiSelector())).waitForExists(mTimeout); 971273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin 981273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin return mDevice.findObject(docList.childSelector(new UiSelector().text(label))); 991273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin } 1006526b38f2871c8d59eb80f4855138de1313de485Ben Lin 1016526b38f2871c8d59eb80f4855138de1313de485Ben Lin private void swipe(int downX, int downY, int upX, int upY, int steps, int button) { 1026526b38f2871c8d59eb80f4855138de1313de485Ben Lin int swipeSteps = steps; 1036526b38f2871c8d59eb80f4855138de1313de485Ben Lin double xStep = 0; 1046526b38f2871c8d59eb80f4855138de1313de485Ben Lin double yStep = 0; 1056526b38f2871c8d59eb80f4855138de1313de485Ben Lin 1066526b38f2871c8d59eb80f4855138de1313de485Ben Lin // avoid a divide by zero 1076526b38f2871c8d59eb80f4855138de1313de485Ben Lin if(swipeSteps == 0) 1086526b38f2871c8d59eb80f4855138de1313de485Ben Lin swipeSteps = 1; 1096526b38f2871c8d59eb80f4855138de1313de485Ben Lin 1106526b38f2871c8d59eb80f4855138de1313de485Ben Lin xStep = ((double)(upX - downX)) / swipeSteps; 1116526b38f2871c8d59eb80f4855138de1313de485Ben Lin yStep = ((double)(upY - downY)) / swipeSteps; 1126526b38f2871c8d59eb80f4855138de1313de485Ben Lin 1136526b38f2871c8d59eb80f4855138de1313de485Ben Lin // first touch starts exactly at the point requested 1146526b38f2871c8d59eb80f4855138de1313de485Ben Lin touchDown(downX, downY, button); 1156526b38f2871c8d59eb80f4855138de1313de485Ben Lin for(int i = 1; i < swipeSteps; i++) { 1166526b38f2871c8d59eb80f4855138de1313de485Ben Lin touchMove(downX + (int)(xStep * i), downY + (int)(yStep * i), button); 1176526b38f2871c8d59eb80f4855138de1313de485Ben Lin // set some known constant delay between steps as without it this 1186526b38f2871c8d59eb80f4855138de1313de485Ben Lin // become completely dependent on the speed of the system and results 1196526b38f2871c8d59eb80f4855138de1313de485Ben Lin // may vary on different devices. This guarantees at minimum we have 1206526b38f2871c8d59eb80f4855138de1313de485Ben Lin // a preset delay. 1216526b38f2871c8d59eb80f4855138de1313de485Ben Lin SystemClock.sleep(MOTION_EVENT_INJECTION_DELAY_MILLIS); 1226526b38f2871c8d59eb80f4855138de1313de485Ben Lin } 1236526b38f2871c8d59eb80f4855138de1313de485Ben Lin touchUp(upX, upY); 1246526b38f2871c8d59eb80f4855138de1313de485Ben Lin } 1256526b38f2871c8d59eb80f4855138de1313de485Ben Lin 1266526b38f2871c8d59eb80f4855138de1313de485Ben Lin private boolean touchDown(int x, int y, int button) { 1276526b38f2871c8d59eb80f4855138de1313de485Ben Lin long mDownTime = SystemClock.uptimeMillis(); 1286526b38f2871c8d59eb80f4855138de1313de485Ben Lin MotionEvent event = getMotionEvent(mDownTime, mDownTime, MotionEvent.ACTION_DOWN, button, x, 1296526b38f2871c8d59eb80f4855138de1313de485Ben Lin y); 1306526b38f2871c8d59eb80f4855138de1313de485Ben Lin return mAutomation.injectInputEvent(event, true); 1316526b38f2871c8d59eb80f4855138de1313de485Ben Lin } 1326526b38f2871c8d59eb80f4855138de1313de485Ben Lin 1336526b38f2871c8d59eb80f4855138de1313de485Ben Lin private boolean touchUp(int x, int y) { 1346526b38f2871c8d59eb80f4855138de1313de485Ben Lin final long eventTime = SystemClock.uptimeMillis(); 1356526b38f2871c8d59eb80f4855138de1313de485Ben Lin MotionEvent event = getMotionEvent(mDownTime, eventTime, MotionEvent.ACTION_UP, 0, x, y); 1366526b38f2871c8d59eb80f4855138de1313de485Ben Lin mDownTime = 0; 1376526b38f2871c8d59eb80f4855138de1313de485Ben Lin return mAutomation.injectInputEvent(event, true); 1386526b38f2871c8d59eb80f4855138de1313de485Ben Lin } 1396526b38f2871c8d59eb80f4855138de1313de485Ben Lin 1406526b38f2871c8d59eb80f4855138de1313de485Ben Lin private boolean touchMove(int x, int y, int button) { 1416526b38f2871c8d59eb80f4855138de1313de485Ben Lin final long eventTime = SystemClock.uptimeMillis(); 1426526b38f2871c8d59eb80f4855138de1313de485Ben Lin MotionEvent event = getMotionEvent(mDownTime, eventTime, MotionEvent.ACTION_MOVE, button, x, 1436526b38f2871c8d59eb80f4855138de1313de485Ben Lin y); 1446526b38f2871c8d59eb80f4855138de1313de485Ben Lin return mAutomation.injectInputEvent(event, true); 1456526b38f2871c8d59eb80f4855138de1313de485Ben Lin } 1466526b38f2871c8d59eb80f4855138de1313de485Ben Lin 1476526b38f2871c8d59eb80f4855138de1313de485Ben Lin /** Helper function to obtain a MotionEvent. */ 1486526b38f2871c8d59eb80f4855138de1313de485Ben Lin private static MotionEvent getMotionEvent(long downTime, long eventTime, int action, int button, 1496526b38f2871c8d59eb80f4855138de1313de485Ben Lin float x, float y) { 1506526b38f2871c8d59eb80f4855138de1313de485Ben Lin 1516526b38f2871c8d59eb80f4855138de1313de485Ben Lin PointerProperties properties = new PointerProperties(); 1526526b38f2871c8d59eb80f4855138de1313de485Ben Lin properties.id = 0; 1536526b38f2871c8d59eb80f4855138de1313de485Ben Lin properties.toolType = Configurator.getInstance().getToolType(); 1546526b38f2871c8d59eb80f4855138de1313de485Ben Lin 1556526b38f2871c8d59eb80f4855138de1313de485Ben Lin PointerCoords coords = new PointerCoords(); 1566526b38f2871c8d59eb80f4855138de1313de485Ben Lin coords.pressure = 1; 1576526b38f2871c8d59eb80f4855138de1313de485Ben Lin coords.size = 1; 1586526b38f2871c8d59eb80f4855138de1313de485Ben Lin coords.x = x; 1596526b38f2871c8d59eb80f4855138de1313de485Ben Lin coords.y = y; 1606526b38f2871c8d59eb80f4855138de1313de485Ben Lin 1616526b38f2871c8d59eb80f4855138de1313de485Ben Lin return MotionEvent.obtain(downTime, eventTime, action, 1, 1626526b38f2871c8d59eb80f4855138de1313de485Ben Lin new PointerProperties[] { properties }, new PointerCoords[] { coords }, 1636526b38f2871c8d59eb80f4855138de1313de485Ben Lin 0, button, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0); 1646526b38f2871c8d59eb80f4855138de1313de485Ben Lin } 1651273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin} 166