1/* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package androidx.recyclerview.widget; 18 19import android.app.Instrumentation; 20import android.os.SystemClock; 21import android.view.Gravity; 22import android.view.MotionEvent; 23import android.view.View; 24import android.view.ViewConfiguration; 25 26/** 27 * RV specific layout tests. 28 */ 29public class TouchUtils { 30 public static void tapView(Instrumentation inst, RecyclerView recyclerView, 31 View v) { 32 int[] xy = new int[2]; 33 v.getLocationOnScreen(xy); 34 35 final int viewWidth = v.getWidth(); 36 final int viewHeight = v.getHeight(); 37 38 final float x = xy[0] + (viewWidth / 2.0f); 39 float y = xy[1] + (viewHeight / 2.0f); 40 41 long downTime = SystemClock.uptimeMillis(); 42 long eventTime = SystemClock.uptimeMillis(); 43 44 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 45 MotionEvent.ACTION_DOWN, x, y, 0); 46 inst.sendPointerSync(event); 47 inst.waitForIdleSync(); 48 49 eventTime = SystemClock.uptimeMillis(); 50 final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop(); 51 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, 52 x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0); 53 inst.sendPointerSync(event); 54 inst.waitForIdleSync(); 55 56 eventTime = SystemClock.uptimeMillis(); 57 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0); 58 inst.sendPointerSync(event); 59 inst.waitForIdleSync(); 60 } 61 62 public static void touchAndCancelView(Instrumentation inst, View v) { 63 int[] xy = new int[2]; 64 v.getLocationOnScreen(xy); 65 66 final int viewWidth = v.getWidth(); 67 final int viewHeight = v.getHeight(); 68 69 final float x = xy[0] + (viewWidth / 2.0f); 70 float y = xy[1] + (viewHeight / 2.0f); 71 72 long downTime = SystemClock.uptimeMillis(); 73 long eventTime = SystemClock.uptimeMillis(); 74 75 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 76 MotionEvent.ACTION_DOWN, x, y, 0); 77 inst.sendPointerSync(event); 78 inst.waitForIdleSync(); 79 80 eventTime = SystemClock.uptimeMillis(); 81 final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop(); 82 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_CANCEL, 83 x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0); 84 inst.sendPointerSync(event); 85 inst.waitForIdleSync(); 86 87 } 88 89 public static void clickView(Instrumentation inst, View v) { 90 int[] xy = new int[2]; 91 v.getLocationOnScreen(xy); 92 93 final int viewWidth = v.getWidth(); 94 final int viewHeight = v.getHeight(); 95 96 final float x = xy[0] + (viewWidth / 2.0f); 97 float y = xy[1] + (viewHeight / 2.0f); 98 99 long downTime = SystemClock.uptimeMillis(); 100 long eventTime = SystemClock.uptimeMillis(); 101 102 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 103 MotionEvent.ACTION_DOWN, x, y, 0); 104 inst.sendPointerSync(event); 105 inst.waitForIdleSync(); 106 107 eventTime = SystemClock.uptimeMillis(); 108 final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop(); 109 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, 110 x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0); 111 inst.sendPointerSync(event); 112 inst.waitForIdleSync(); 113 114 eventTime = SystemClock.uptimeMillis(); 115 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0); 116 inst.sendPointerSync(event); 117 inst.waitForIdleSync(); 118 119 try { 120 Thread.sleep(1000); 121 } catch (InterruptedException e) { 122 e.printStackTrace(); 123 } 124 } 125 126 public static void longClickView(Instrumentation inst, View v) { 127 int[] xy = new int[2]; 128 v.getLocationOnScreen(xy); 129 130 final int viewWidth = v.getWidth(); 131 final int viewHeight = v.getHeight(); 132 133 final float x = xy[0] + (viewWidth / 2.0f); 134 float y = xy[1] + (viewHeight / 2.0f); 135 136 long downTime = SystemClock.uptimeMillis(); 137 long eventTime = SystemClock.uptimeMillis(); 138 139 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 140 MotionEvent.ACTION_DOWN, x, y, 0); 141 inst.sendPointerSync(event); 142 inst.waitForIdleSync(); 143 144 try { 145 Thread.sleep((long) (ViewConfiguration.getLongPressTimeout() * 1.5f)); 146 } catch (InterruptedException e) { 147 e.printStackTrace(); 148 } 149 150 eventTime = SystemClock.uptimeMillis(); 151 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0); 152 inst.sendPointerSync(event); 153 inst.waitForIdleSync(); 154 } 155 156 public static void scrollView(int axis, int axisValue, int inputDevice, View v) { 157 MotionEvent.PointerProperties[] pointerProperties = { new MotionEvent.PointerProperties() }; 158 MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords(); 159 coords.setAxisValue(axis, axisValue); 160 MotionEvent.PointerCoords[] pointerCoords = { coords }; 161 MotionEvent e = MotionEvent.obtain( 162 0, System.currentTimeMillis(), MotionEvent.ACTION_SCROLL, 163 1, pointerProperties, pointerCoords, 0, 0, 1, 1, 0, 0, inputDevice, 0); 164 v.onGenericMotionEvent(e); 165 e.recycle(); 166 } 167 168 public static void dragViewToTop(Instrumentation inst, View v) { 169 dragViewToTop(inst, v, calculateStepsForDistance(v.getTop())); 170 } 171 172 public static void dragViewToTop(Instrumentation inst, View v, int stepCount) { 173 int[] xy = new int[2]; 174 v.getLocationOnScreen(xy); 175 176 final int viewWidth = v.getWidth(); 177 final int viewHeight = v.getHeight(); 178 179 final float x = xy[0] + (viewWidth / 2.0f); 180 float fromY = xy[1] + (viewHeight / 2.0f); 181 float toY = 0; 182 183 drag(inst, x, x, fromY, toY, stepCount); 184 } 185 186 private static void getStartLocation(View v, int gravity, int[] xy) { 187 v.getLocationOnScreen(xy); 188 189 final int viewWidth = v.getWidth(); 190 final int viewHeight = v.getHeight(); 191 192 switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) { 193 case Gravity.TOP: 194 break; 195 case Gravity.CENTER_VERTICAL: 196 xy[1] += viewHeight / 2; 197 break; 198 case Gravity.BOTTOM: 199 xy[1] += viewHeight - 1; 200 break; 201 default: 202 // Same as top -- do nothing 203 } 204 205 switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 206 case Gravity.LEFT: 207 break; 208 case Gravity.CENTER_HORIZONTAL: 209 xy[0] += viewWidth / 2; 210 break; 211 case Gravity.RIGHT: 212 xy[0] += viewWidth - 1; 213 break; 214 default: 215 // Same as left -- do nothing 216 } 217 } 218 219 public static int dragViewTo(Instrumentation inst, View v, int gravity, int toX, 220 int toY) { 221 int[] xy = new int[2]; 222 223 getStartLocation(v, gravity, xy); 224 225 final int fromX = xy[0]; 226 final int fromY = xy[1]; 227 228 int deltaX = fromX - toX; 229 int deltaY = fromY - toY; 230 231 int distance = (int) Math.sqrt(deltaX * deltaX + deltaY * deltaY); 232 drag(inst, fromX, toX, fromY, toY, calculateStepsForDistance(distance)); 233 234 return distance; 235 } 236 237 public static int dragViewToX(Instrumentation inst, View v, int gravity, int toX) { 238 int[] xy = new int[2]; 239 240 getStartLocation(v, gravity, xy); 241 242 final int fromX = xy[0]; 243 final int fromY = xy[1]; 244 245 int deltaX = fromX - toX; 246 247 drag(inst, fromX, toX, fromY, fromY, calculateStepsForDistance(deltaX)); 248 249 return deltaX; 250 } 251 252 public static int dragViewToY(Instrumentation inst, View v, int gravity, int toY) { 253 int[] xy = new int[2]; 254 255 getStartLocation(v, gravity, xy); 256 257 final int fromX = xy[0]; 258 final int fromY = xy[1]; 259 260 int deltaY = fromY - toY; 261 262 drag(inst, fromX, fromX, fromY, toY, calculateStepsForDistance(deltaY)); 263 264 return deltaY; 265 } 266 267 268 public static void drag(Instrumentation inst, float fromX, float toX, float fromY, 269 float toY, int stepCount) { 270 long downTime = SystemClock.uptimeMillis(); 271 long eventTime = SystemClock.uptimeMillis(); 272 273 float y = fromY; 274 float x = fromX; 275 276 float yStep = (toY - fromY) / stepCount; 277 float xStep = (toX - fromX) / stepCount; 278 279 MotionEvent event = MotionEvent.obtain(downTime, eventTime, 280 MotionEvent.ACTION_DOWN, x, y, 0); 281 inst.sendPointerSync(event); 282 for (int i = 0; i < stepCount; ++i) { 283 y += yStep; 284 x += xStep; 285 eventTime = SystemClock.uptimeMillis(); 286 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0); 287 inst.sendPointerSync(event); 288 } 289 290 eventTime = SystemClock.uptimeMillis(); 291 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0); 292 inst.sendPointerSync(event); 293 inst.waitForIdleSync(); 294 } 295 296 private static int calculateStepsForDistance(int distance) { 297 return 1 + Math.abs(distance) / 10; 298 } 299} 300