TouchCommon.java revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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 on the specified relative coordinates inside 122 * a given view. 123 * 124 * @param view The view to be clicked. 125 * @param x screen absolute 126 * @param y screen absolute 127 * @see TouchUtils 128 */ 129 public void singleClickViewRelative(View view, int x, int y) { 130 long downTime = SystemClock.uptimeMillis(); 131 long eventTime = SystemClock.uptimeMillis(); 132 133 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 134 MotionEvent.ACTION_DOWN, x, y, 0); 135 dispatchTouchEvent(view, event); 136 137 eventTime = SystemClock.uptimeMillis(); 138 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, 139 x, y, 0); 140 dispatchTouchEvent(view, event); 141 } 142 143 /** 144 * Sends (synchronously) a long press to an absolute screen coordinates. 145 * 146 * @param x screen absolute 147 * @param y screen absolute 148 * @see TouchUtils 149 */ 150 public void longPress(float x, float y) { 151 152 long downTime = SystemClock.uptimeMillis(); 153 long eventTime = SystemClock.uptimeMillis(); 154 155 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 156 MotionEvent.ACTION_DOWN, x, y, 0); 157 dispatchTouchEvent(event); 158 159 int longPressTimeout = ViewConfiguration.get( 160 mActivityTestCase.getActivity()).getLongPressTimeout(); 161 162 // Long press is flaky with just longPressTimeout. Doubling the time to be safe. 163 SystemClock.sleep(longPressTimeout * 2); 164 165 eventTime = SystemClock.uptimeMillis(); 166 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, 167 x, y, 0); 168 dispatchTouchEvent(event); 169 } 170 171 /** 172 * Sends (synchronously) a long press to the View at the specified coordinates. 173 * 174 * @param v The view to be clicked. 175 * @param x Relative x location to v 176 * @param y Relative y location to v 177 */ 178 public void longPressView(View v, int x, int y) { 179 int location[] = getAbsoluteLocationFromRelative(v, x, y); 180 int absoluteX = location[0]; 181 int absoluteY = location[1]; 182 longPress(absoluteX, absoluteY); 183 } 184 185 /** 186 * Send a MotionEvent to the root view of the activity. 187 * @param event 188 */ 189 private void dispatchTouchEvent(final MotionEvent event) { 190 View view = 191 mActivityTestCase.getActivity().findViewById(android.R.id.content).getRootView(); 192 dispatchTouchEvent(view, event); 193 } 194 195 /** 196 * Send a MotionEvent to the specified view instead of the root view. 197 * For example AutofillPopup window that is above the root view. 198 * @param view The view that should receive the event. 199 * @param event The view to be dispatched. 200 */ 201 private void dispatchTouchEvent(final View view, final MotionEvent event) { 202 try { 203 mActivityTestCase.runTestOnUiThread(new Runnable() { 204 @Override 205 public void run() { 206 view.dispatchTouchEvent(event); 207 } 208 }); 209 } catch(Throwable e) { 210 throw new RuntimeException("Dispatching touch event failed", e); 211 } 212 } 213 214 /** 215 * Returns the absolute location in screen coordinates from location relative 216 * to view. 217 * @param v The view the coordinates are relative to. 218 * @param x Relative x location. 219 * @param y Relative y location. 220 * @return absolute x and y location in an array. 221 */ 222 private static int[] getAbsoluteLocationFromRelative(View v, int x, int y) { 223 int location[] = new int[2]; 224 v.getLocationOnScreen(location); 225 location[0] += x; 226 location[1] += y; 227 return location; 228 } 229} 230