TouchCommon.java revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package org.chromium.content.browser.test.util; 6 7import android.os.SystemClock; 8import android.view.MotionEvent; 9import android.view.View; 10import android.view.ViewConfiguration; 11import android.test.ActivityInstrumentationTestCase2; 12 13/** 14 * Touch-related functionality reused across test cases. 15 */ 16public class TouchCommon { 17 private ActivityInstrumentationTestCase2 mActivityTestCase; 18 19 // TODO(leandrogracia): This method should receive and use an activity 20 // instead of the ActivityInstrumentationTestCase2. However this is causing 21 // problems downstream. Any fix for this should be landed downstream first. 22 public TouchCommon(ActivityInstrumentationTestCase2 activityTestCase) { 23 mActivityTestCase = activityTestCase; 24 } 25 26 /** 27 * Starts (synchronously) a drag motion. Normally followed by dragTo() and dragEnd(). 28 * 29 * @param x 30 * @param y 31 * @param downTime (in ms) 32 * @see TouchUtils 33 */ 34 public void dragStart(float x, float y, long downTime) { 35 MotionEvent event = MotionEvent.obtain(downTime, downTime, 36 MotionEvent.ACTION_DOWN, x, y, 0); 37 dispatchTouchEvent(event); 38 } 39 40 /** 41 * Drags / moves (synchronously) to the specified coordinates. Normally preceeded by 42 * dragStart() and followed by dragEnd() 43 * 44 * @param fromX 45 * @param toX 46 * @param fromY 47 * @param toY 48 * @param stepCount 49 * @param downTime (in ms) 50 * @see TouchUtils 51 */ 52 public void dragTo(float fromX, float toX, float fromY, 53 float toY, int stepCount, long downTime) { 54 float x = fromX; 55 float y = fromY; 56 float yStep = (toY - fromY) / stepCount; 57 float xStep = (toX - fromX) / stepCount; 58 for (int i = 0; i < stepCount; ++i) { 59 y += yStep; 60 x += xStep; 61 long eventTime = SystemClock.uptimeMillis(); 62 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 63 MotionEvent.ACTION_MOVE, x, y, 0); 64 dispatchTouchEvent(event); 65 } 66 } 67 68 /** 69 * Finishes (synchronously) a drag / move at the specified coordinate. 70 * Normally preceeded by dragStart() and dragTo(). 71 * 72 * @param x 73 * @param y 74 * @param downTime (in ms) 75 * @see TouchUtils 76 */ 77 public void dragEnd(float x, float y, long downTime) { 78 long eventTime = SystemClock.uptimeMillis(); 79 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 80 MotionEvent.ACTION_UP, x, y, 0); 81 dispatchTouchEvent(event); 82 } 83 84 /** 85 * Sends (synchronously) a single click to an absolute screen coordinates. 86 * 87 * @param x screen absolute 88 * @param y screen absolute 89 * @see TouchUtils 90 */ 91 public void singleClick(float x, float y) { 92 93 long downTime = SystemClock.uptimeMillis(); 94 long eventTime = SystemClock.uptimeMillis(); 95 96 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 97 MotionEvent.ACTION_DOWN, x, y, 0); 98 dispatchTouchEvent(event); 99 100 eventTime = SystemClock.uptimeMillis(); 101 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, 102 x, y, 0); 103 dispatchTouchEvent(event); 104 } 105 106 /** 107 * Sends (synchronously) a single click to the View at the specified coordinates. 108 * 109 * @param v The view to be clicked. 110 * @param x Relative x location to v 111 * @param y Relative y location to v 112 */ 113 public void singleClickView(View v, int x, int y) { 114 int location[] = getAbsoluteLocationFromRelative(v, x, y); 115 int absoluteX = location[0]; 116 int absoluteY = location[1]; 117 singleClick(absoluteX, absoluteY); 118 } 119 120 /** 121 * Sends (synchronously) a single click to the center of the View. 122 */ 123 public void singleClickView(View v) { 124 singleClickView(v, v.getWidth() / 2, v.getHeight() / 2); 125 } 126 127 /** 128 * Sends (synchronously) a single click on the specified relative coordinates inside 129 * a given view. 130 * 131 * @param view The view to be clicked. 132 * @param x screen absolute 133 * @param y screen absolute 134 * @see TouchUtils 135 */ 136 public void singleClickViewRelative(View view, int x, int y) { 137 long downTime = SystemClock.uptimeMillis(); 138 long eventTime = SystemClock.uptimeMillis(); 139 140 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 141 MotionEvent.ACTION_DOWN, x, y, 0); 142 dispatchTouchEvent(view, event); 143 144 eventTime = SystemClock.uptimeMillis(); 145 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, 146 x, y, 0); 147 dispatchTouchEvent(view, event); 148 } 149 150 /** 151 * Sends (synchronously) a long press to an absolute screen coordinates. 152 * 153 * @param x screen absolute 154 * @param y screen absolute 155 * @see TouchUtils 156 */ 157 public void longPress(float x, float y) { 158 159 long downTime = SystemClock.uptimeMillis(); 160 long eventTime = SystemClock.uptimeMillis(); 161 162 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 163 MotionEvent.ACTION_DOWN, x, y, 0); 164 dispatchTouchEvent(event); 165 166 int longPressTimeout = ViewConfiguration.get( 167 mActivityTestCase.getActivity()).getLongPressTimeout(); 168 169 // Long press is flaky with just longPressTimeout. Doubling the time to be safe. 170 SystemClock.sleep(longPressTimeout * 2); 171 172 eventTime = SystemClock.uptimeMillis(); 173 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, 174 x, y, 0); 175 dispatchTouchEvent(event); 176 } 177 178 /** 179 * Sends (synchronously) a long press to the View at the specified coordinates. 180 * 181 * @param v The view to be clicked. 182 * @param x Relative x location to v 183 * @param y Relative y location to v 184 */ 185 public void longPressView(View v, int x, int y) { 186 int location[] = getAbsoluteLocationFromRelative(v, x, y); 187 int absoluteX = location[0]; 188 int absoluteY = location[1]; 189 longPress(absoluteX, absoluteY); 190 } 191 192 /** 193 * Send a MotionEvent to the root view of the activity. 194 * @param event 195 */ 196 private void dispatchTouchEvent(final MotionEvent event) { 197 View view = 198 mActivityTestCase.getActivity().findViewById(android.R.id.content).getRootView(); 199 dispatchTouchEvent(view, event); 200 } 201 202 /** 203 * Send a MotionEvent to the specified view instead of the root view. 204 * For example AutofillPopup window that is above the root view. 205 * @param view The view that should receive the event. 206 * @param event The view to be dispatched. 207 */ 208 private void dispatchTouchEvent(final View view, final MotionEvent event) { 209 try { 210 mActivityTestCase.runTestOnUiThread(new Runnable() { 211 @Override 212 public void run() { 213 view.dispatchTouchEvent(event); 214 } 215 }); 216 } catch(Throwable e) { 217 throw new RuntimeException("Dispatching touch event failed", e); 218 } 219 } 220 221 /** 222 * Returns the absolute location in screen coordinates from location relative 223 * to view. 224 * @param v The view the coordinates are relative to. 225 * @param x Relative x location. 226 * @param y Relative y location. 227 * @return absolute x and y location in an array. 228 */ 229 private static int[] getAbsoluteLocationFromRelative(View v, int x, int y) { 230 int location[] = new int[2]; 231 v.getLocationOnScreen(location); 232 location[0] += x; 233 location[1] += y; 234 return location; 235 } 236} 237