1 /* 2 * Copyright (C) 2016 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 17 package com.android.compatibility.common.util; 18 19 import android.app.Instrumentation; 20 import android.app.UiAutomation; 21 import android.graphics.Point; 22 import android.os.SystemClock; 23 import android.util.SparseArray; 24 import android.view.InputDevice; 25 import android.view.MotionEvent; 26 import android.view.View; 27 import android.view.ViewConfiguration; 28 import android.view.ViewGroup; 29 30 /** 31 * Test utilities for touch emulation. 32 */ 33 public final class CtsTouchUtils { 34 CtsTouchUtils()35 private CtsTouchUtils() {} 36 37 /** 38 * Emulates a tap in the center of the passed {@link View}. 39 * 40 * @param instrumentation the instrumentation used to run the test 41 * @param view the view to "tap" 42 */ emulateTapOnViewCenter(Instrumentation instrumentation, View view)43 public static void emulateTapOnViewCenter(Instrumentation instrumentation, View view) { 44 emulateTapOnView(instrumentation, view, view.getWidth() / 2, view.getHeight() / 2); 45 } 46 47 /** 48 * Emulates a tap on a point relative to the top-left corner of the passed {@link View}. Offset 49 * parameters are used to compute the final screen coordinates of the tap point. 50 * 51 * @param instrumentation the instrumentation used to run the test 52 * @param anchorView the anchor view to determine the tap location on the screen 53 * @param offsetX extra X offset for the tap 54 * @param offsetY extra Y offset for the tap 55 */ emulateTapOnView(Instrumentation instrumentation, View anchorView, int offsetX, int offsetY)56 public static void emulateTapOnView(Instrumentation instrumentation, View anchorView, 57 int offsetX, int offsetY) { 58 final int touchSlop = ViewConfiguration.get(anchorView.getContext()).getScaledTouchSlop(); 59 // Get anchor coordinates on the screen 60 final int[] viewOnScreenXY = new int[2]; 61 anchorView.getLocationOnScreen(viewOnScreenXY); 62 int xOnScreen = viewOnScreenXY[0] + offsetX; 63 int yOnScreen = viewOnScreenXY[1] + offsetY; 64 final UiAutomation uiAutomation = instrumentation.getUiAutomation(); 65 final long downTime = SystemClock.uptimeMillis(); 66 67 injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen); 68 injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen); 69 injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen); 70 71 // Wait for the system to process all events in the queue 72 instrumentation.waitForIdleSync(); 73 } 74 75 /** 76 * Emulates a double tap in the center of the passed {@link View}. 77 * 78 * @param instrumentation the instrumentation used to run the test 79 * @param view the view to "double tap" 80 */ emulateDoubleTapOnViewCenter(Instrumentation instrumentation, View view)81 public static void emulateDoubleTapOnViewCenter(Instrumentation instrumentation, View view) { 82 emulateDoubleTapOnView(instrumentation, view, view.getWidth() / 2, view.getHeight() / 2); 83 } 84 85 /** 86 * Emulates a double tap on a point relative to the top-left corner of the passed {@link View}. 87 * Offset parameters are used to compute the final screen coordinates of the tap points. 88 * 89 * @param instrumentation the instrumentation used to run the test 90 * @param anchorView the anchor view to determine the tap location on the screen 91 * @param offsetX extra X offset for the taps 92 * @param offsetY extra Y offset for the taps 93 */ emulateDoubleTapOnView(Instrumentation instrumentation, View anchorView, int offsetX, int offsetY)94 public static void emulateDoubleTapOnView(Instrumentation instrumentation, View anchorView, 95 int offsetX, int offsetY) { 96 final int touchSlop = ViewConfiguration.get(anchorView.getContext()).getScaledTouchSlop(); 97 // Get anchor coordinates on the screen 98 final int[] viewOnScreenXY = new int[2]; 99 anchorView.getLocationOnScreen(viewOnScreenXY); 100 int xOnScreen = viewOnScreenXY[0] + offsetX; 101 int yOnScreen = viewOnScreenXY[1] + offsetY; 102 final UiAutomation uiAutomation = instrumentation.getUiAutomation(); 103 final long downTime = SystemClock.uptimeMillis(); 104 105 injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen); 106 injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen); 107 injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen); 108 injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen); 109 injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen); 110 injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen); 111 112 // Wait for the system to process all events in the queue 113 instrumentation.waitForIdleSync(); 114 } 115 116 /** 117 * Emulates a linear drag gesture between 2 points across the screen. 118 * 119 * @param instrumentation the instrumentation used to run the test 120 * @param dragStartX Start X of the emulated drag gesture 121 * @param dragStartY Start Y of the emulated drag gesture 122 * @param dragAmountX X amount of the emulated drag gesture 123 * @param dragAmountY Y amount of the emulated drag gesture 124 */ emulateDragGesture(Instrumentation instrumentation, int dragStartX, int dragStartY, int dragAmountX, int dragAmountY)125 public static void emulateDragGesture(Instrumentation instrumentation, 126 int dragStartX, int dragStartY, int dragAmountX, int dragAmountY) { 127 emulateDragGesture(instrumentation, dragStartX, dragStartY, dragAmountX, dragAmountY, 128 2000, 20); 129 } 130 emulateDragGesture(Instrumentation instrumentation, int dragStartX, int dragStartY, int dragAmountX, int dragAmountY, int dragDurationMs, int moveEventCount)131 private static void emulateDragGesture(Instrumentation instrumentation, 132 int dragStartX, int dragStartY, int dragAmountX, int dragAmountY, 133 int dragDurationMs, int moveEventCount) { 134 // We are using the UiAutomation object to inject events so that drag works 135 // across view / window boundaries (such as for the emulated drag and drop 136 // sequences) 137 final UiAutomation uiAutomation = instrumentation.getUiAutomation(); 138 final long downTime = SystemClock.uptimeMillis(); 139 140 injectDownEvent(uiAutomation, downTime, dragStartX, dragStartY); 141 142 // Inject a sequence of MOVE events that emulate the "move" part of the gesture 143 injectMoveEventsForDrag(uiAutomation, downTime, true, dragStartX, dragStartY, 144 dragStartX + dragAmountX, dragStartY + dragAmountY, moveEventCount, dragDurationMs); 145 146 injectUpEvent(uiAutomation, downTime, true, dragStartX + dragAmountX, 147 dragStartY + dragAmountY); 148 149 // Wait for the system to process all events in the queue 150 instrumentation.waitForIdleSync(); 151 } 152 153 /** 154 * Emulates a series of linear drag gestures across the screen between multiple points without 155 * lifting the finger. Note that this function does not support curve movements between the 156 * points. 157 * 158 * @param instrumentation the instrumentation used to run the test 159 * @param coordinates the ordered list of points for the drag gesture 160 */ emulateDragGesture(Instrumentation instrumentation, SparseArray<Point> coordinates)161 public static void emulateDragGesture(Instrumentation instrumentation, 162 SparseArray<Point> coordinates) { 163 emulateDragGesture(instrumentation, coordinates, 2000, 20); 164 } 165 emulateDragGesture(Instrumentation instrumentation, SparseArray<Point> coordinates, int dragDurationMs, int moveEventCount)166 private static void emulateDragGesture(Instrumentation instrumentation, 167 SparseArray<Point> coordinates, int dragDurationMs, int moveEventCount) { 168 final int coordinatesSize = coordinates.size(); 169 if (coordinatesSize < 2) { 170 throw new IllegalArgumentException("Need at least 2 points for emulating drag"); 171 } 172 // We are using the UiAutomation object to inject events so that drag works 173 // across view / window boundaries (such as for the emulated drag and drop 174 // sequences) 175 final UiAutomation uiAutomation = instrumentation.getUiAutomation(); 176 final long downTime = SystemClock.uptimeMillis(); 177 178 injectDownEvent(uiAutomation, downTime, coordinates.get(0).x, coordinates.get(0).y); 179 180 // Move to each coordinate. 181 for (int i = 0; i < coordinatesSize - 1; i++) { 182 // Inject a sequence of MOVE events that emulate the "move" part of the gesture. 183 injectMoveEventsForDrag(uiAutomation, 184 downTime, 185 true, 186 coordinates.get(i).x, 187 coordinates.get(i).y, 188 coordinates.get(i + 1).x, 189 coordinates.get(i + 1).y, 190 moveEventCount, 191 dragDurationMs); 192 } 193 194 injectUpEvent(uiAutomation, 195 downTime, 196 true, 197 coordinates.get(coordinatesSize - 1).x, 198 coordinates.get(coordinatesSize - 1).y); 199 200 // Wait for the system to process all events in the queue 201 instrumentation.waitForIdleSync(); 202 } 203 injectDownEvent(UiAutomation uiAutomation, long downTime, int xOnScreen, int yOnScreen)204 private static long injectDownEvent(UiAutomation uiAutomation, long downTime, int xOnScreen, 205 int yOnScreen) { 206 MotionEvent eventDown = MotionEvent.obtain( 207 downTime, downTime, MotionEvent.ACTION_DOWN, xOnScreen, yOnScreen, 1); 208 eventDown.setSource(InputDevice.SOURCE_TOUCHSCREEN); 209 uiAutomation.injectInputEvent(eventDown, true); 210 eventDown.recycle(); 211 return downTime; 212 } 213 injectMoveEventForTap(UiAutomation uiAutomation, long downTime, int touchSlop, int xOnScreen, int yOnScreen)214 private static void injectMoveEventForTap(UiAutomation uiAutomation, long downTime, 215 int touchSlop, int xOnScreen, int yOnScreen) { 216 MotionEvent eventMove = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_MOVE, 217 xOnScreen + (touchSlop / 2.0f), yOnScreen + (touchSlop / 2.0f), 1); 218 eventMove.setSource(InputDevice.SOURCE_TOUCHSCREEN); 219 uiAutomation.injectInputEvent(eventMove, true); 220 eventMove.recycle(); 221 } 222 injectMoveEventsForDrag(UiAutomation uiAutomation, long downTime, boolean useCurrentEventTime, int dragStartX, int dragStartY, int dragEndX, int dragEndY, int moveEventCount, int dragDurationMs)223 private static void injectMoveEventsForDrag(UiAutomation uiAutomation, long downTime, 224 boolean useCurrentEventTime, int dragStartX, int dragStartY, int dragEndX, int dragEndY, 225 int moveEventCount, int dragDurationMs) { 226 final int dragAmountX = dragEndX - dragStartX; 227 final int dragAmountY = dragEndY - dragStartY; 228 final int sleepTime = dragDurationMs / moveEventCount; 229 230 // sleep for a bit to emulate the overall drag gesture. 231 long prevEventTime = downTime; 232 SystemClock.sleep(sleepTime); 233 for (int i = 0; i < moveEventCount; i++) { 234 // Note that the first MOVE event is generated "away" from the coordinates 235 // of the start / DOWN event, and the last MOVE event is generated 236 // at the same coordinates as the subsequent UP event. 237 final int moveX = dragStartX + dragAmountX * (i + 1) / moveEventCount; 238 final int moveY = dragStartY + dragAmountY * (i + 1) / moveEventCount; 239 long eventTime = useCurrentEventTime ? SystemClock.uptimeMillis() : downTime; 240 241 // If necessary, generate history for our next MOVE event. The history is generated 242 // to be spaced at 10 millisecond intervals, interpolating the coordinates from the 243 // last generated MOVE event to our current one. 244 int historyEventCount = (int) ((eventTime - prevEventTime) / 10); 245 MotionEvent eventMove = null; 246 if (historyEventCount == 0) { 247 eventMove = MotionEvent.obtain( 248 downTime, eventTime, MotionEvent.ACTION_MOVE, moveX, moveY, 1); 249 } else { 250 final int prevMoveX = dragStartX + dragAmountX * i / moveEventCount; 251 final int prevMoveY = dragStartY + dragAmountY * i / moveEventCount; 252 final int deltaMoveX = moveX - prevMoveX; 253 final int deltaMoveY = moveY - prevMoveY; 254 final long deltaTime = (eventTime - prevEventTime); 255 for (int historyIndex = 0; historyIndex < historyEventCount; historyIndex++) { 256 int stepMoveX = prevMoveX + deltaMoveX * (historyIndex + 1) / historyEventCount; 257 int stepMoveY = prevMoveY + deltaMoveY * (historyIndex + 1) / historyEventCount; 258 long stepEventTime = useCurrentEventTime 259 ? prevEventTime + deltaTime * (historyIndex + 1) / historyEventCount 260 : downTime; 261 if (historyIndex == 0) { 262 // Generate the first event in our sequence 263 eventMove = MotionEvent.obtain(downTime, stepEventTime, 264 MotionEvent.ACTION_MOVE, stepMoveX, stepMoveY, 1); 265 } else { 266 // and then add to it 267 eventMove.addBatch(stepEventTime, stepMoveX, stepMoveY, 1.0f, 1.0f, 1); 268 } 269 } 270 } 271 272 eventMove.setSource(InputDevice.SOURCE_TOUCHSCREEN); 273 uiAutomation.injectInputEvent(eventMove, true); 274 eventMove.recycle(); 275 prevEventTime = eventTime; 276 277 // sleep for a bit to emulate the overall drag gesture. 278 SystemClock.sleep(sleepTime); 279 } 280 } 281 injectUpEvent(UiAutomation uiAutomation, long downTime, boolean useCurrentEventTime, int xOnScreen, int yOnScreen)282 private static void injectUpEvent(UiAutomation uiAutomation, long downTime, 283 boolean useCurrentEventTime, int xOnScreen, int yOnScreen) { 284 long eventTime = useCurrentEventTime ? SystemClock.uptimeMillis() : downTime; 285 MotionEvent eventUp = MotionEvent.obtain( 286 downTime, eventTime, MotionEvent.ACTION_UP, xOnScreen, yOnScreen, 1); 287 eventUp.setSource(InputDevice.SOURCE_TOUCHSCREEN); 288 uiAutomation.injectInputEvent(eventUp, true); 289 eventUp.recycle(); 290 } 291 292 /** 293 * Emulates a fling gesture across the horizontal center of the passed view. 294 * 295 * @param instrumentation the instrumentation used to run the test 296 * @param view the view to fling 297 * @param isDownwardsFlingGesture if <code>true</code>, the emulated fling will 298 * be a downwards gesture 299 * @return The vertical amount of emulated fling in pixels 300 */ emulateFlingGesture(Instrumentation instrumentation, View view, boolean isDownwardsFlingGesture)301 public static int emulateFlingGesture(Instrumentation instrumentation, 302 View view, boolean isDownwardsFlingGesture) { 303 final ViewConfiguration configuration = ViewConfiguration.get(view.getContext()); 304 final int flingVelocity = (configuration.getScaledMinimumFlingVelocity() + 305 configuration.getScaledMaximumFlingVelocity()) / 2; 306 // Get view coordinates on the screen 307 final int[] viewOnScreenXY = new int[2]; 308 view.getLocationOnScreen(viewOnScreenXY); 309 310 // Our fling gesture will be from 25% height of the view to 75% height of the view 311 // for downwards fling gesture, and the other way around for upwards fling gesture 312 final int viewHeight = view.getHeight(); 313 final int x = viewOnScreenXY[0] + view.getWidth() / 2; 314 final int startY = isDownwardsFlingGesture ? viewOnScreenXY[1] + viewHeight / 4 315 : viewOnScreenXY[1] + 3 * viewHeight / 4; 316 final int amountY = isDownwardsFlingGesture ? viewHeight / 2 : -viewHeight / 2; 317 318 // Compute fling gesture duration based on the distance (50% height of the view) and 319 // fling velocity 320 final int durationMs = (1000 * viewHeight) / (2 * flingVelocity); 321 322 // And do the same event injection sequence as our generic drag gesture 323 emulateDragGesture(instrumentation, x, startY, 0, amountY, durationMs, durationMs / 16); 324 325 return amountY; 326 } 327 328 private static class ViewStateSnapshot { 329 final View mFirst; 330 final View mLast; 331 final int mFirstTop; 332 final int mLastBottom; 333 final int mChildCount; ViewStateSnapshot(ViewGroup viewGroup)334 private ViewStateSnapshot(ViewGroup viewGroup) { 335 mChildCount = viewGroup.getChildCount(); 336 if (mChildCount == 0) { 337 mFirst = mLast = null; 338 mFirstTop = mLastBottom = Integer.MIN_VALUE; 339 } else { 340 mFirst = viewGroup.getChildAt(0); 341 mLast = viewGroup.getChildAt(mChildCount - 1); 342 mFirstTop = mFirst.getTop(); 343 mLastBottom = mLast.getBottom(); 344 } 345 } 346 347 @Override equals(Object o)348 public boolean equals(Object o) { 349 if (this == o) { 350 return true; 351 } 352 if (o == null || getClass() != o.getClass()) { 353 return false; 354 } 355 356 final ViewStateSnapshot that = (ViewStateSnapshot) o; 357 return mFirstTop == that.mFirstTop && 358 mLastBottom == that.mLastBottom && 359 mFirst == that.mFirst && 360 mLast == that.mLast && 361 mChildCount == that.mChildCount; 362 } 363 364 @Override hashCode()365 public int hashCode() { 366 int result = mFirst != null ? mFirst.hashCode() : 0; 367 result = 31 * result + (mLast != null ? mLast.hashCode() : 0); 368 result = 31 * result + mFirstTop; 369 result = 31 * result + mLastBottom; 370 result = 31 * result + mChildCount; 371 return result; 372 } 373 } 374 375 /** 376 * Emulates a scroll to the bottom of the specified {@link ViewGroup}. 377 * 378 * @param instrumentation the instrumentation used to run the test 379 * @param viewGroup View group 380 */ emulateScrollToBottom(Instrumentation instrumentation, ViewGroup viewGroup)381 public static void emulateScrollToBottom(Instrumentation instrumentation, ViewGroup viewGroup) { 382 final int[] viewGroupOnScreenXY = new int[2]; 383 viewGroup.getLocationOnScreen(viewGroupOnScreenXY); 384 385 final int emulatedX = viewGroupOnScreenXY[0] + viewGroup.getWidth() / 2; 386 final int emulatedStartY = viewGroupOnScreenXY[1] + 3 * viewGroup.getHeight() / 4; 387 final int swipeAmount = viewGroup.getHeight() / 2; 388 389 ViewStateSnapshot prev; 390 ViewStateSnapshot next = new ViewStateSnapshot(viewGroup); 391 do { 392 prev = next; 393 emulateDragGesture(instrumentation, emulatedX, emulatedStartY, 0, -swipeAmount, 394 300, 10); 395 next = new ViewStateSnapshot(viewGroup); 396 } while (!prev.equals(next)); 397 } 398 399 /** 400 * Emulates a long press in the center of the passed {@link View}. 401 * 402 * @param instrumentation the instrumentation used to run the test 403 * @param view the view to "long press" 404 */ emulateLongPressOnViewCenter(Instrumentation instrumentation, View view)405 public static void emulateLongPressOnViewCenter(Instrumentation instrumentation, View view) { 406 emulateLongPressOnViewCenter(instrumentation, view, 0); 407 } 408 409 /** 410 * Emulates a long press in the center of the passed {@link View}. 411 * 412 * @param instrumentation the instrumentation used to run the test 413 * @param view the view to "long press" 414 * @param extraWaitMs the duration of emulated "long press" in milliseconds starting 415 * after system-level long press timeout. 416 */ emulateLongPressOnViewCenter(Instrumentation instrumentation, View view, long extraWaitMs)417 public static void emulateLongPressOnViewCenter(Instrumentation instrumentation, View view, 418 long extraWaitMs) { 419 final int touchSlop = ViewConfiguration.get(view.getContext()).getScaledTouchSlop(); 420 // Use instrumentation to emulate a tap on the spinner to bring down its popup 421 final int[] viewOnScreenXY = new int[2]; 422 view.getLocationOnScreen(viewOnScreenXY); 423 int xOnScreen = viewOnScreenXY[0] + view.getWidth() / 2; 424 int yOnScreen = viewOnScreenXY[1] + view.getHeight() / 2; 425 426 emulateLongPressOnScreen( 427 instrumentation, xOnScreen, yOnScreen, touchSlop, extraWaitMs, true); 428 } 429 430 /** 431 * Emulates a long press confirmed on a point relative to the top-left corner of the passed 432 * {@link View}. Offset parameters are used to compute the final screen coordinates of the 433 * press point. 434 * 435 * @param instrumentation the instrumentation used to run the test 436 * @param view the view to "long press" 437 * @param offsetX extra X offset for the tap 438 * @param offsetY extra Y offset for the tap 439 */ emulateLongPressOnView(Instrumentation instrumentation, View view, int offsetX, int offsetY)440 public static void emulateLongPressOnView(Instrumentation instrumentation, View view, 441 int offsetX, int offsetY) { 442 final int touchSlop = ViewConfiguration.get(view.getContext()).getScaledTouchSlop(); 443 final int[] viewOnScreenXY = new int[2]; 444 view.getLocationOnScreen(viewOnScreenXY); 445 int xOnScreen = viewOnScreenXY[0] + offsetX; 446 int yOnScreen = viewOnScreenXY[1] + offsetY; 447 448 emulateLongPressOnScreen(instrumentation, xOnScreen, yOnScreen, touchSlop, 0, true); 449 } 450 451 /** 452 * Emulates a long press then a linear drag gesture between 2 points across the screen. 453 * This is used for drag selection. 454 * 455 * @param instrumentation the instrumentation used to run the test 456 * @param dragStartX Start X of the emulated drag gesture 457 * @param dragStartY Start Y of the emulated drag gesture 458 * @param dragAmountX X amount of the emulated drag gesture 459 * @param dragAmountY Y amount of the emulated drag gesture 460 */ emulateLongPressAndDragGesture(Instrumentation instrumentation, int dragStartX, int dragStartY, int dragAmountX, int dragAmountY)461 public static void emulateLongPressAndDragGesture(Instrumentation instrumentation, 462 int dragStartX, int dragStartY, int dragAmountX, int dragAmountY) { 463 emulateLongPressOnScreen(instrumentation, dragStartX, dragStartY, 464 0 /* touchSlop */, 0 /* extraWaitMs */, false /* upGesture */); 465 emulateDragGesture(instrumentation, dragStartX, dragStartY, dragAmountX, dragAmountY); 466 } 467 468 /** 469 * Emulates a long press on the screen. 470 * 471 * @param instrumentation the instrumentation used to run the test 472 * @param xOnScreen X position on screen for the "long press" 473 * @param yOnScreen Y position on screen for the "long press" 474 * @param extraWaitMs extra duration of emulated long press in milliseconds added 475 * after the system-level "long press" timeout. 476 * @param upGesture whether to include an up event. 477 */ emulateLongPressOnScreen(Instrumentation instrumentation, int xOnScreen, int yOnScreen, int touchSlop, long extraWaitMs, boolean upGesture)478 private static void emulateLongPressOnScreen(Instrumentation instrumentation, 479 int xOnScreen, int yOnScreen, int touchSlop, long extraWaitMs, boolean upGesture) { 480 final UiAutomation uiAutomation = instrumentation.getUiAutomation(); 481 final long downTime = SystemClock.uptimeMillis(); 482 483 injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen); 484 injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen); 485 SystemClock.sleep((long) (ViewConfiguration.getLongPressTimeout() * 1.5f) + extraWaitMs); 486 if (upGesture) { 487 injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen); 488 } 489 490 // Wait for the system to process all events in the queue 491 instrumentation.waitForIdleSync(); 492 } 493 } 494