// Copyright 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.content.browser.test.util; import android.app.Activity; import android.app.Instrumentation; import android.os.SystemClock; import android.test.InstrumentationTestCase; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; /** * Collection of utilities for generating touch events. * Based on android.test.TouchUtils, but slightly more flexible (allows to * specify coordinates for longClick, splits drag operation in three stages, etc). */ public class TestTouchUtils extends android.test.TouchUtils { /** * Returns the absolute location in screen coordinates from location relative * to view. * @param v The view the coordinates are relative to. * @param x Relative x location. * @param y Relative y location. * @return the absolute x and y location in an array. */ public static int[] getAbsoluteLocationFromRelative(View v, int x, int y) { int location[] = new int[2]; v.getLocationOnScreen(location); location[0] += x; location[1] += y; return location; } private static void sendAction(Instrumentation instrumentation, int action, long downTime, float x, float y) { long eventTime = SystemClock.uptimeMillis(); MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0); instrumentation.sendPointerSync(event); instrumentation.waitForIdleSync(); } /** * Sends (synchronously) a single click to an absolute screen coordinates. * * @param instrumentation Instrumentation object used by the test. * @param x Screen absolute x location. * @param y Screen absolute y location. */ public static void singleClick(Instrumentation instrumentation, float x, float y) { long downTime = SystemClock.uptimeMillis(); sendAction(instrumentation, MotionEvent.ACTION_DOWN, downTime, x, y); sendAction(instrumentation, MotionEvent.ACTION_UP, downTime, x, y); } /** * Sends (synchronously) a single click to the View at the specified coordinates. * * @param instrumentation Instrumentation object used by the test. * @param v The view the coordinates are relative to. * @param x Relative x location to the view. * @param y Relative y location to the view. */ public static void singleClickView(Instrumentation instrumentation, View v, int x, int y) { int location[] = getAbsoluteLocationFromRelative(v, x, y); int absoluteX = location[0]; int absoluteY = location[1]; singleClick(instrumentation, absoluteX, absoluteY); } /** * Sends (synchronously) a single click to the center of the View. * * @param instrumentation Instrumentation object used by the test. * @param v The view the coordinates are relative to. */ public static void singleClickView(Instrumentation instrumentation, View v) { int x = v.getWidth() / 2; int y = v.getHeight() / 2; singleClickView(instrumentation, v, x, y); } /** * Sleeps for at least the length of the double tap timeout. * * @param instrumentation Instrumentation object used by the test. */ public static void sleepForDoubleTapTimeout(Instrumentation instrumentation) { SystemClock.sleep((long) (ViewConfiguration.getDoubleTapTimeout() * 1.5)); } /** * Sends (synchronously) a long click to the View at the specified coordinates. * * @param instrumentation Instrumentation object used by the test. * @param v The view the coordinates are relative to. * @param x Relative x location to the view. * @param y Relative y location to the view. */ public static void longClickView(Instrumentation instrumentation, View v, int x, int y) { int location[] = getAbsoluteLocationFromRelative(v, x, y); int absoluteX = location[0]; int absoluteY = location[1]; long downTime = SystemClock.uptimeMillis(); sendAction(instrumentation, MotionEvent.ACTION_DOWN, downTime, absoluteX, absoluteY); SystemClock.sleep((long) (ViewConfiguration.getLongPressTimeout() * 1.5)); sendAction(instrumentation, MotionEvent.ACTION_UP, downTime, absoluteX, absoluteY); } /** * Sends (synchronously) a long click to the View at its center. * * @param instrumentation Instrumentation object used by the test. * @param v The view to long click. */ public static void longClickView(Instrumentation instrumentation, View v) { int x = v.getWidth() / 2; int y = v.getHeight() / 2; longClickView(instrumentation, v, x, y); } /** * Starts (synchronously) a drag motion. Normally followed by dragTo() and dragEnd(). * * @param instrumentation Instrumentation object used by the test. * @param x The x location. * @param y The y location. * @return The downTime of the triggered event. */ public static long dragStart(Instrumentation instrumentation, float x, float y) { long downTime = SystemClock.uptimeMillis(); sendAction(instrumentation, MotionEvent.ACTION_DOWN, downTime, x, y); return downTime; } /** * Drags / moves (synchronously) to the specified coordinates. Normally preceeded by * dragStart() and followed by dragEnd() * * @param instrumentation Instrumentation object used by the test. * @param fromX The relative x-coordinate of the start point of the drag. * @param toX The relative x-coordinate of the end point of the drag. * @param fromY The relative y-coordinate of the start point of the drag. * @param toY The relative y-coordinate of the end point of the drag. * @param stepCount The total number of motion events that should be generated during the drag. * @param downTime The initial time of the drag, in ms. */ public static void dragTo(Instrumentation instrumentation, float fromX, float toX, float fromY, float toY, int stepCount, long downTime) { float x = fromX; float y = fromY; float yStep = (toY - fromY) / stepCount; float xStep = (toX - fromX) / stepCount; for (int i = 0; i < stepCount; ++i) { y += yStep; x += xStep; sendAction(instrumentation, MotionEvent.ACTION_MOVE, downTime, x, y); } } /** * Finishes (synchronously) a drag / move at the specified coordinate. * Normally preceeded by dragStart() and dragTo(). * * @param instrumentation Instrumentation object used by the test. * @param x The x location. * @param y The y location. * @param downTime The initial time of the drag, in ms. */ public static void dragEnd(Instrumentation instrumentation, float x, float y, long downTime) { sendAction(instrumentation, MotionEvent.ACTION_UP, downTime, x, y); } /** * Performs a drag between the given coordinates, specified relative to the given view. * This method makes calls to dragStart, dragTo and dragEnd. * * @param instrumentation Instrumentation object used by the test. * @param view The view the coordinates are relative to. * @param fromX The relative x-coordinate of the start point of the drag. * @param toX The relative x-coordinate of the end point of the drag. * @param fromY The relative y-coordinate of the start point of the drag. * @param toY The relative y-coordinate of the end point of the drag. * @param stepCount The total number of motion events that should be generated during the drag. */ public static void dragCompleteView(Instrumentation instrumentation, View view, int fromX, int toX, int fromY, int toY, int stepCount) { int fromLocation[] = getAbsoluteLocationFromRelative(view, fromX, fromY); int toLocation[] = getAbsoluteLocationFromRelative(view, toX, toY); long downTime = dragStart(instrumentation, fromLocation[0], fromLocation[1]); dragTo(instrumentation, fromLocation[0], toLocation[0], fromLocation[1], toLocation[1], stepCount, downTime); dragEnd(instrumentation, toLocation[0], toLocation[1], downTime); } /** * Calls performClick on a View on the main UI thread. * * @param instrumentation Instrumentation object used by the test. * @param v The view to call performClick on. */ public static void performClickOnMainSync(Instrumentation instrumentation, final View v) { instrumentation.runOnMainSync(new Runnable() { @Override public void run() { v.performClick(); } }); } /** * Clicks on specified view in the given {@link Activity}. * * @param test The test case using this utility. * @param activity Activity containing the view. * @param id The view to be clicked. * @return {@code true} if a view with the given id exists. */ public static boolean clickById(InstrumentationTestCase test, Activity activity, int id) { View v = activity.findViewById(id); if (v == null) return false; clickView(test, v); return true; } }