| // Copyright 2012 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.os.SystemClock; |
| import android.test.ActivityInstrumentationTestCase2; |
| import android.test.TouchUtils; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.ViewConfiguration; |
| |
| /** |
| * Touch-related functionality reused across test cases. |
| */ |
| public class TouchCommon { |
| private ActivityInstrumentationTestCase2 mActivityTestCase; |
| |
| // TODO(leandrogracia): This method should receive and use an activity |
| // instead of the ActivityInstrumentationTestCase2. However this is causing |
| // problems downstream. Any fix for this should be landed downstream first. |
| public TouchCommon(ActivityInstrumentationTestCase2 activityTestCase) { |
| mActivityTestCase = activityTestCase; |
| } |
| |
| /** |
| * Starts (synchronously) a drag motion. Normally followed by dragTo() and dragEnd(). |
| * |
| * @param x |
| * @param y |
| * @param downTime (in ms) |
| * @see TouchUtils |
| */ |
| public void dragStart(float x, float y, long downTime) { |
| MotionEvent event = MotionEvent.obtain(downTime, downTime, |
| MotionEvent.ACTION_DOWN, x, y, 0); |
| dispatchTouchEvent(event); |
| } |
| |
| /** |
| * Drags / moves (synchronously) to the specified coordinates. Normally preceeded by |
| * dragStart() and followed by dragEnd() |
| * |
| * @param fromX |
| * @param toX |
| * @param fromY |
| * @param toY |
| * @param stepCount |
| * @param downTime (in ms) |
| * @see TouchUtils |
| */ |
| public void dragTo(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; |
| long eventTime = SystemClock.uptimeMillis(); |
| MotionEvent event = MotionEvent.obtain(downTime, eventTime, |
| MotionEvent.ACTION_MOVE, x, y, 0); |
| dispatchTouchEvent(event); |
| } |
| } |
| |
| /** |
| * Finishes (synchronously) a drag / move at the specified coordinate. |
| * Normally preceeded by dragStart() and dragTo(). |
| * |
| * @param x |
| * @param y |
| * @param downTime (in ms) |
| * @see TouchUtils |
| */ |
| public void dragEnd(float x, float y, long downTime) { |
| long eventTime = SystemClock.uptimeMillis(); |
| MotionEvent event = MotionEvent.obtain(downTime, eventTime, |
| MotionEvent.ACTION_UP, x, y, 0); |
| dispatchTouchEvent(event); |
| } |
| |
| /** |
| * Sends (synchronously) a single click to an absolute screen coordinates. |
| * |
| * @param x screen absolute |
| * @param y screen absolute |
| * @see TouchUtils |
| */ |
| public void singleClick(float x, float y) { |
| |
| long downTime = SystemClock.uptimeMillis(); |
| long eventTime = SystemClock.uptimeMillis(); |
| |
| MotionEvent event = MotionEvent.obtain(downTime, eventTime, |
| MotionEvent.ACTION_DOWN, x, y, 0); |
| dispatchTouchEvent(event); |
| |
| eventTime = SystemClock.uptimeMillis(); |
| event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, |
| x, y, 0); |
| dispatchTouchEvent(event); |
| } |
| |
| /** |
| * Sends (synchronously) a single click to the View at the specified coordinates. |
| * |
| * @param v The view to be clicked. |
| * @param x Relative x location to v |
| * @param y Relative y location to v |
| */ |
| public void singleClickView(View v, int x, int y) { |
| int location[] = getAbsoluteLocationFromRelative(v, x, y); |
| int absoluteX = location[0]; |
| int absoluteY = location[1]; |
| singleClick(absoluteX, absoluteY); |
| } |
| |
| /** |
| * Sends (synchronously) a single click to the center of the View. |
| */ |
| public void singleClickView(View v) { |
| singleClickView(v, v.getWidth() / 2, v.getHeight() / 2); |
| } |
| |
| /** |
| * Sends (synchronously) a single click on the specified relative coordinates inside |
| * a given view. |
| * |
| * @param view The view to be clicked. |
| * @param x screen absolute |
| * @param y screen absolute |
| * @see TouchUtils |
| */ |
| public void singleClickViewRelative(View view, int x, int y) { |
| long downTime = SystemClock.uptimeMillis(); |
| long eventTime = SystemClock.uptimeMillis(); |
| |
| MotionEvent event = MotionEvent.obtain(downTime, eventTime, |
| MotionEvent.ACTION_DOWN, x, y, 0); |
| dispatchTouchEvent(view, event); |
| |
| eventTime = SystemClock.uptimeMillis(); |
| event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, |
| x, y, 0); |
| dispatchTouchEvent(view, event); |
| } |
| |
| /** |
| * Sends (synchronously) a long press to an absolute screen coordinates. |
| * |
| * @param x screen absolute |
| * @param y screen absolute |
| * @see TouchUtils |
| */ |
| public void longPress(float x, float y) { |
| |
| long downTime = SystemClock.uptimeMillis(); |
| long eventTime = SystemClock.uptimeMillis(); |
| |
| MotionEvent event = MotionEvent.obtain(downTime, eventTime, |
| MotionEvent.ACTION_DOWN, x, y, 0); |
| dispatchTouchEvent(event); |
| |
| int longPressTimeout = ViewConfiguration.get( |
| mActivityTestCase.getActivity()).getLongPressTimeout(); |
| |
| // Long press is flaky with just longPressTimeout. Doubling the time to be safe. |
| SystemClock.sleep(longPressTimeout * 2); |
| |
| eventTime = SystemClock.uptimeMillis(); |
| event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, |
| x, y, 0); |
| dispatchTouchEvent(event); |
| } |
| |
| /** |
| * Sends (synchronously) a long press to the View at the specified coordinates. |
| * |
| * @param v The view to be clicked. |
| * @param x Relative x location to v |
| * @param y Relative y location to v |
| */ |
| public void longPressView(View v, int x, int y) { |
| int location[] = getAbsoluteLocationFromRelative(v, x, y); |
| int absoluteX = location[0]; |
| int absoluteY = location[1]; |
| longPress(absoluteX, absoluteY); |
| } |
| |
| /** |
| * Send a MotionEvent to the root view of the activity. |
| * @param event |
| */ |
| private void dispatchTouchEvent(final MotionEvent event) { |
| View view = |
| mActivityTestCase.getActivity().findViewById(android.R.id.content).getRootView(); |
| dispatchTouchEvent(view, event); |
| } |
| |
| /** |
| * Send a MotionEvent to the specified view instead of the root view. |
| * For example AutofillPopup window that is above the root view. |
| * @param view The view that should receive the event. |
| * @param event The view to be dispatched. |
| */ |
| private void dispatchTouchEvent(final View view, final MotionEvent event) { |
| try { |
| mActivityTestCase.runTestOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| view.dispatchTouchEvent(event); |
| } |
| }); |
| } catch (Throwable e) { |
| throw new RuntimeException("Dispatching touch event failed", e); |
| } |
| } |
| |
| /** |
| * 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 absolute x and y location in an array. |
| */ |
| private 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; |
| } |
| } |