1 /* 2 * Copyright (C) 2012 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.uiautomator.core; 18 19 import android.app.UiAutomation; 20 import android.app.UiAutomation.AccessibilityEventFilter; 21 import android.graphics.Point; 22 import android.os.Build; 23 import android.os.Environment; 24 import android.os.RemoteException; 25 import android.os.SystemClock; 26 import android.util.DisplayMetrics; 27 import android.util.Log; 28 import android.view.Display; 29 import android.view.KeyEvent; 30 import android.view.Surface; 31 import android.view.accessibility.AccessibilityEvent; 32 import android.view.accessibility.AccessibilityNodeInfo; 33 34 import java.io.File; 35 import java.util.ArrayList; 36 import java.util.HashMap; 37 import java.util.List; 38 import java.util.concurrent.TimeoutException; 39 40 /** 41 * UiDevice provides access to state information about the device. 42 * You can also use this class to simulate user actions on the device, 43 * such as pressing the d-pad or pressing the Home and Menu buttons. 44 * @since API Level 16 45 */ 46 public class UiDevice { 47 private static final String LOG_TAG = UiDevice.class.getSimpleName(); 48 49 // Sometimes HOME and BACK key presses will generate no events if already on 50 // home page or there is nothing to go back to, Set low timeouts. 51 private static final long KEY_PRESS_EVENT_TIMEOUT = 1 * 1000; 52 53 // store for registered UiWatchers 54 private final HashMap<String, UiWatcher> mWatchers = new HashMap<String, UiWatcher>(); 55 private final List<String> mWatchersTriggers = new ArrayList<String>(); 56 57 // remember if we're executing in the context of a UiWatcher 58 private boolean mInWatcherContext = false; 59 60 // provides access the {@link QueryController} and {@link InteractionController} 61 private UiAutomatorBridge mUiAutomationBridge; 62 63 // reference to self 64 private static UiDevice sDevice; 65 UiDevice()66 private UiDevice() { 67 /* hide constructor */ 68 } 69 70 /** 71 * @hide 72 */ initialize(UiAutomatorBridge uiAutomatorBridge)73 public void initialize(UiAutomatorBridge uiAutomatorBridge) { 74 mUiAutomationBridge = uiAutomatorBridge; 75 } 76 isInWatcherContext()77 boolean isInWatcherContext() { 78 return mInWatcherContext; 79 } 80 81 /** 82 * Provides access the {@link QueryController} and {@link InteractionController} 83 * @return {@link ShellUiAutomatorBridge} 84 */ getAutomatorBridge()85 UiAutomatorBridge getAutomatorBridge() { 86 if (mUiAutomationBridge == null) { 87 throw new RuntimeException("UiDevice not initialized"); 88 } 89 return mUiAutomationBridge; 90 } 91 92 /** 93 * Enables or disables layout hierarchy compression. 94 * 95 * If compression is enabled, the layout hierarchy derived from the Acessibility 96 * framework will only contain nodes that are important for uiautomator 97 * testing. Any unnecessary surrounding layout nodes that make viewing 98 * and searching the hierarchy inefficient are removed. 99 * 100 * @param compressed true to enable compression; else, false to disable 101 * @since API Level 18 102 */ setCompressedLayoutHeirarchy(boolean compressed)103 public void setCompressedLayoutHeirarchy(boolean compressed) { 104 getAutomatorBridge().setCompressedLayoutHierarchy(compressed); 105 } 106 107 /** 108 * Retrieves a singleton instance of UiDevice 109 * 110 * @return UiDevice instance 111 * @since API Level 16 112 */ getInstance()113 public static UiDevice getInstance() { 114 if (sDevice == null) { 115 sDevice = new UiDevice(); 116 } 117 return sDevice; 118 } 119 120 /** 121 * Returns the display size in dp (device-independent pixel) 122 * 123 * The returned display size is adjusted per screen rotation. Also this will return the actual 124 * size of the screen, rather than adjusted per system decorations (like status bar). 125 * 126 * @return a Point containing the display size in dp 127 */ getDisplaySizeDp()128 public Point getDisplaySizeDp() { 129 Tracer.trace(); 130 Display display = getAutomatorBridge().getDefaultDisplay(); 131 Point p = new Point(); 132 display.getRealSize(p); 133 DisplayMetrics metrics = new DisplayMetrics(); 134 display.getRealMetrics(metrics); 135 float dpx = p.x / metrics.density; 136 float dpy = p.y / metrics.density; 137 p.x = Math.round(dpx); 138 p.y = Math.round(dpy); 139 return p; 140 } 141 142 /** 143 * Retrieves the product name of the device. 144 * 145 * This method provides information on what type of device the test is running on. This value is 146 * the same as returned by invoking #adb shell getprop ro.product.name. 147 * 148 * @return product name of the device 149 * @since API Level 17 150 */ getProductName()151 public String getProductName() { 152 Tracer.trace(); 153 return Build.PRODUCT; 154 } 155 156 /** 157 * Retrieves the text from the last UI traversal event received. 158 * 159 * You can use this method to read the contents in a WebView container 160 * because the accessibility framework fires events 161 * as each text is highlighted. You can write a test to perform 162 * directional arrow presses to focus on different elements inside a WebView, 163 * and call this method to get the text from each traversed element. 164 * If you are testing a view container that can return a reference to a 165 * Document Object Model (DOM) object, your test should use the view's 166 * DOM instead. 167 * 168 * @return text of the last traversal event, else return an empty string 169 * @since API Level 16 170 */ getLastTraversedText()171 public String getLastTraversedText() { 172 Tracer.trace(); 173 return getAutomatorBridge().getQueryController().getLastTraversedText(); 174 } 175 176 /** 177 * Clears the text from the last UI traversal event. 178 * See {@link #getLastTraversedText()}. 179 * @since API Level 16 180 */ clearLastTraversedText()181 public void clearLastTraversedText() { 182 Tracer.trace(); 183 getAutomatorBridge().getQueryController().clearLastTraversedText(); 184 } 185 186 /** 187 * Simulates a short press on the MENU button. 188 * @return true if successful, else return false 189 * @since API Level 16 190 */ pressMenu()191 public boolean pressMenu() { 192 Tracer.trace(); 193 waitForIdle(); 194 return getAutomatorBridge().getInteractionController().sendKeyAndWaitForEvent( 195 KeyEvent.KEYCODE_MENU, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, 196 KEY_PRESS_EVENT_TIMEOUT); 197 } 198 199 /** 200 * Simulates a short press on the BACK button. 201 * @return true if successful, else return false 202 * @since API Level 16 203 */ pressBack()204 public boolean pressBack() { 205 Tracer.trace(); 206 waitForIdle(); 207 return getAutomatorBridge().getInteractionController().sendKeyAndWaitForEvent( 208 KeyEvent.KEYCODE_BACK, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, 209 KEY_PRESS_EVENT_TIMEOUT); 210 } 211 212 /** 213 * Simulates a short press on the HOME button. 214 * @return true if successful, else return false 215 * @since API Level 16 216 */ pressHome()217 public boolean pressHome() { 218 Tracer.trace(); 219 waitForIdle(); 220 return getAutomatorBridge().getInteractionController().sendKeyAndWaitForEvent( 221 KeyEvent.KEYCODE_HOME, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, 222 KEY_PRESS_EVENT_TIMEOUT); 223 } 224 225 /** 226 * Simulates a short press on the SEARCH button. 227 * @return true if successful, else return false 228 * @since API Level 16 229 */ pressSearch()230 public boolean pressSearch() { 231 Tracer.trace(); 232 return pressKeyCode(KeyEvent.KEYCODE_SEARCH); 233 } 234 235 /** 236 * Simulates a short press on the CENTER button. 237 * @return true if successful, else return false 238 * @since API Level 16 239 */ pressDPadCenter()240 public boolean pressDPadCenter() { 241 Tracer.trace(); 242 return pressKeyCode(KeyEvent.KEYCODE_DPAD_CENTER); 243 } 244 245 /** 246 * Simulates a short press on the DOWN button. 247 * @return true if successful, else return false 248 * @since API Level 16 249 */ pressDPadDown()250 public boolean pressDPadDown() { 251 Tracer.trace(); 252 return pressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN); 253 } 254 255 /** 256 * Simulates a short press on the UP button. 257 * @return true if successful, else return false 258 * @since API Level 16 259 */ pressDPadUp()260 public boolean pressDPadUp() { 261 Tracer.trace(); 262 return pressKeyCode(KeyEvent.KEYCODE_DPAD_UP); 263 } 264 265 /** 266 * Simulates a short press on the LEFT button. 267 * @return true if successful, else return false 268 * @since API Level 16 269 */ pressDPadLeft()270 public boolean pressDPadLeft() { 271 Tracer.trace(); 272 return pressKeyCode(KeyEvent.KEYCODE_DPAD_LEFT); 273 } 274 275 /** 276 * Simulates a short press on the RIGHT button. 277 * @return true if successful, else return false 278 * @since API Level 16 279 */ pressDPadRight()280 public boolean pressDPadRight() { 281 Tracer.trace(); 282 return pressKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT); 283 } 284 285 /** 286 * Simulates a short press on the DELETE key. 287 * @return true if successful, else return false 288 * @since API Level 16 289 */ pressDelete()290 public boolean pressDelete() { 291 Tracer.trace(); 292 return pressKeyCode(KeyEvent.KEYCODE_DEL); 293 } 294 295 /** 296 * Simulates a short press on the ENTER key. 297 * @return true if successful, else return false 298 * @since API Level 16 299 */ pressEnter()300 public boolean pressEnter() { 301 Tracer.trace(); 302 return pressKeyCode(KeyEvent.KEYCODE_ENTER); 303 } 304 305 /** 306 * Simulates a short press using a key code. 307 * 308 * See {@link KeyEvent} 309 * @return true if successful, else return false 310 * @since API Level 16 311 */ pressKeyCode(int keyCode)312 public boolean pressKeyCode(int keyCode) { 313 Tracer.trace(keyCode); 314 waitForIdle(); 315 return getAutomatorBridge().getInteractionController().sendKey(keyCode, 0); 316 } 317 318 /** 319 * Simulates a short press using a key code. 320 * 321 * See {@link KeyEvent}. 322 * @param keyCode the key code of the event. 323 * @param metaState an integer in which each bit set to 1 represents a pressed meta key 324 * @return true if successful, else return false 325 * @since API Level 16 326 */ pressKeyCode(int keyCode, int metaState)327 public boolean pressKeyCode(int keyCode, int metaState) { 328 Tracer.trace(keyCode, metaState); 329 waitForIdle(); 330 return getAutomatorBridge().getInteractionController().sendKey(keyCode, metaState); 331 } 332 333 /** 334 * Simulates a short press on the Recent Apps button. 335 * 336 * @return true if successful, else return false 337 * @throws RemoteException 338 * @since API Level 16 339 */ pressRecentApps()340 public boolean pressRecentApps() throws RemoteException { 341 Tracer.trace(); 342 waitForIdle(); 343 return getAutomatorBridge().getInteractionController().toggleRecentApps(); 344 } 345 346 /** 347 * Opens the notification shade. 348 * 349 * @return true if successful, else return false 350 * @since API Level 18 351 */ openNotification()352 public boolean openNotification() { 353 Tracer.trace(); 354 waitForIdle(); 355 return getAutomatorBridge().getInteractionController().openNotification(); 356 } 357 358 /** 359 * Opens the Quick Settings shade. 360 * 361 * @return true if successful, else return false 362 * @since API Level 18 363 */ openQuickSettings()364 public boolean openQuickSettings() { 365 Tracer.trace(); 366 waitForIdle(); 367 return getAutomatorBridge().getInteractionController().openQuickSettings(); 368 } 369 370 /** 371 * Gets the width of the display, in pixels. The width and height details 372 * are reported based on the current orientation of the display. 373 * @return width in pixels or zero on failure 374 * @since API Level 16 375 */ getDisplayWidth()376 public int getDisplayWidth() { 377 Tracer.trace(); 378 Display display = getAutomatorBridge().getDefaultDisplay(); 379 Point p = new Point(); 380 display.getSize(p); 381 return p.x; 382 } 383 384 /** 385 * Gets the height of the display, in pixels. The size is adjusted based 386 * on the current orientation of the display. 387 * @return height in pixels or zero on failure 388 * @since API Level 16 389 */ getDisplayHeight()390 public int getDisplayHeight() { 391 Tracer.trace(); 392 Display display = getAutomatorBridge().getDefaultDisplay(); 393 Point p = new Point(); 394 display.getSize(p); 395 return p.y; 396 } 397 398 /** 399 * Perform a click at arbitrary coordinates specified by the user 400 * 401 * @param x coordinate 402 * @param y coordinate 403 * @return true if the click succeeded else false 404 * @since API Level 16 405 */ click(int x, int y)406 public boolean click(int x, int y) { 407 Tracer.trace(x, y); 408 if (x >= getDisplayWidth() || y >= getDisplayHeight()) { 409 return (false); 410 } 411 return getAutomatorBridge().getInteractionController().clickNoSync(x, y); 412 } 413 414 /** 415 * Performs a swipe from one coordinate to another using the number of steps 416 * to determine smoothness and speed. Each step execution is throttled to 5ms 417 * per step. So for a 100 steps, the swipe will take about 1/2 second to complete. 418 * 419 * @param startX 420 * @param startY 421 * @param endX 422 * @param endY 423 * @param steps is the number of move steps sent to the system 424 * @return false if the operation fails or the coordinates are invalid 425 * @since API Level 16 426 */ swipe(int startX, int startY, int endX, int endY, int steps)427 public boolean swipe(int startX, int startY, int endX, int endY, int steps) { 428 Tracer.trace(startX, startY, endX, endY, steps); 429 return getAutomatorBridge().getInteractionController() 430 .swipe(startX, startY, endX, endY, steps); 431 } 432 433 /** 434 * Performs a swipe from one coordinate to another coordinate. You can control 435 * the smoothness and speed of the swipe by specifying the number of steps. 436 * Each step execution is throttled to 5 milliseconds per step, so for a 100 437 * steps, the swipe will take around 0.5 seconds to complete. 438 * 439 * @param startX X-axis value for the starting coordinate 440 * @param startY Y-axis value for the starting coordinate 441 * @param endX X-axis value for the ending coordinate 442 * @param endY Y-axis value for the ending coordinate 443 * @param steps is the number of steps for the swipe action 444 * @return true if swipe is performed, false if the operation fails 445 * or the coordinates are invalid 446 * @since API Level 18 447 */ drag(int startX, int startY, int endX, int endY, int steps)448 public boolean drag(int startX, int startY, int endX, int endY, int steps) { 449 Tracer.trace(startX, startY, endX, endY, steps); 450 return getAutomatorBridge().getInteractionController() 451 .swipe(startX, startY, endX, endY, steps, true); 452 } 453 454 /** 455 * Performs a swipe between points in the Point array. Each step execution is throttled 456 * to 5ms per step. So for a 100 steps, the swipe will take about 1/2 second to complete 457 * 458 * @param segments is Point array containing at least one Point object 459 * @param segmentSteps steps to inject between two Points 460 * @return true on success 461 * @since API Level 16 462 */ swipe(Point[] segments, int segmentSteps)463 public boolean swipe(Point[] segments, int segmentSteps) { 464 Tracer.trace(segments, segmentSteps); 465 return getAutomatorBridge().getInteractionController().swipe(segments, segmentSteps); 466 } 467 468 /** 469 * Waits for the current application to idle. 470 * Default wait timeout is 10 seconds 471 * @since API Level 16 472 */ waitForIdle()473 public void waitForIdle() { 474 Tracer.trace(); 475 waitForIdle(Configurator.getInstance().getWaitForIdleTimeout()); 476 } 477 478 /** 479 * Waits for the current application to idle. 480 * @param timeout in milliseconds 481 * @since API Level 16 482 */ waitForIdle(long timeout)483 public void waitForIdle(long timeout) { 484 Tracer.trace(timeout); 485 getAutomatorBridge().waitForIdle(timeout); 486 } 487 488 /** 489 * Retrieves the last activity to report accessibility events. 490 * @deprecated The results returned should be considered unreliable 491 * @return String name of activity 492 * @since API Level 16 493 */ 494 @Deprecated getCurrentActivityName()495 public String getCurrentActivityName() { 496 Tracer.trace(); 497 return getAutomatorBridge().getQueryController().getCurrentActivityName(); 498 } 499 500 /** 501 * Retrieves the name of the last package to report accessibility events. 502 * @return String name of package 503 * @since API Level 16 504 */ getCurrentPackageName()505 public String getCurrentPackageName() { 506 Tracer.trace(); 507 return getAutomatorBridge().getQueryController().getCurrentPackageName(); 508 } 509 510 /** 511 * Registers a {@link UiWatcher} to run automatically when the testing framework is unable to 512 * find a match using a {@link UiSelector}. See {@link #runWatchers()} 513 * 514 * @param name to register the UiWatcher 515 * @param watcher {@link UiWatcher} 516 * @since API Level 16 517 */ registerWatcher(String name, UiWatcher watcher)518 public void registerWatcher(String name, UiWatcher watcher) { 519 Tracer.trace(name, watcher); 520 if (mInWatcherContext) { 521 throw new IllegalStateException("Cannot register new watcher from within another"); 522 } 523 mWatchers.put(name, watcher); 524 } 525 526 /** 527 * Removes a previously registered {@link UiWatcher}. 528 * 529 * See {@link #registerWatcher(String, UiWatcher)} 530 * @param name used to register the UiWatcher 531 * @since API Level 16 532 */ removeWatcher(String name)533 public void removeWatcher(String name) { 534 Tracer.trace(name); 535 if (mInWatcherContext) { 536 throw new IllegalStateException("Cannot remove a watcher from within another"); 537 } 538 mWatchers.remove(name); 539 } 540 541 /** 542 * This method forces all registered watchers to run. 543 * See {@link #registerWatcher(String, UiWatcher)} 544 * @since API Level 16 545 */ runWatchers()546 public void runWatchers() { 547 Tracer.trace(); 548 if (mInWatcherContext) { 549 return; 550 } 551 552 for (String watcherName : mWatchers.keySet()) { 553 UiWatcher watcher = mWatchers.get(watcherName); 554 if (watcher != null) { 555 try { 556 mInWatcherContext = true; 557 if (watcher.checkForCondition()) { 558 setWatcherTriggered(watcherName); 559 } 560 } catch (Exception e) { 561 Log.e(LOG_TAG, "Exceuting watcher: " + watcherName, e); 562 } finally { 563 mInWatcherContext = false; 564 } 565 } 566 } 567 } 568 569 /** 570 * Resets a {@link UiWatcher} that has been triggered. 571 * If a UiWatcher runs and its {@link UiWatcher#checkForCondition()} call 572 * returned <code>true</code>, then the UiWatcher is considered triggered. 573 * See {@link #registerWatcher(String, UiWatcher)} 574 * @since API Level 16 575 */ resetWatcherTriggers()576 public void resetWatcherTriggers() { 577 Tracer.trace(); 578 mWatchersTriggers.clear(); 579 } 580 581 /** 582 * Checks if a specific registered {@link UiWatcher} has triggered. 583 * See {@link #registerWatcher(String, UiWatcher)}. If a UiWatcher runs and its 584 * {@link UiWatcher#checkForCondition()} call returned <code>true</code>, then 585 * the UiWatcher is considered triggered. This is helpful if a watcher is detecting errors 586 * from ANR or crash dialogs and the test needs to know if a UiWatcher has been triggered. 587 * 588 * @param watcherName 589 * @return true if triggered else false 590 * @since API Level 16 591 */ hasWatcherTriggered(String watcherName)592 public boolean hasWatcherTriggered(String watcherName) { 593 Tracer.trace(watcherName); 594 return mWatchersTriggers.contains(watcherName); 595 } 596 597 /** 598 * Checks if any registered {@link UiWatcher} have triggered. 599 * 600 * See {@link #registerWatcher(String, UiWatcher)} 601 * See {@link #hasWatcherTriggered(String)} 602 * @since API Level 16 603 */ hasAnyWatcherTriggered()604 public boolean hasAnyWatcherTriggered() { 605 Tracer.trace(); 606 return mWatchersTriggers.size() > 0; 607 } 608 609 /** 610 * Used internally by this class to set a {@link UiWatcher} state as triggered. 611 * @param watcherName 612 */ setWatcherTriggered(String watcherName)613 private void setWatcherTriggered(String watcherName) { 614 Tracer.trace(watcherName); 615 if (!hasWatcherTriggered(watcherName)) { 616 mWatchersTriggers.add(watcherName); 617 } 618 } 619 620 /** 621 * Check if the device is in its natural orientation. This is determined by checking if the 622 * orientation is at 0 or 180 degrees. 623 * @return true if it is in natural orientation 624 * @since API Level 17 625 */ isNaturalOrientation()626 public boolean isNaturalOrientation() { 627 Tracer.trace(); 628 waitForIdle(); 629 int ret = getAutomatorBridge().getRotation(); 630 return ret == UiAutomation.ROTATION_FREEZE_0 || 631 ret == UiAutomation.ROTATION_FREEZE_180; 632 } 633 634 /** 635 * Returns the current rotation of the display, as defined in {@link Surface} 636 * @since API Level 17 637 */ getDisplayRotation()638 public int getDisplayRotation() { 639 Tracer.trace(); 640 waitForIdle(); 641 return getAutomatorBridge().getRotation(); 642 } 643 644 /** 645 * Disables the sensors and freezes the device rotation at its 646 * current rotation state. 647 * @throws RemoteException 648 * @since API Level 16 649 */ freezeRotation()650 public void freezeRotation() throws RemoteException { 651 Tracer.trace(); 652 getAutomatorBridge().getInteractionController().freezeRotation(); 653 } 654 655 /** 656 * Re-enables the sensors and un-freezes the device rotation allowing its contents 657 * to rotate with the device physical rotation. During a test execution, it is best to 658 * keep the device frozen in a specific orientation until the test case execution has completed. 659 * @throws RemoteException 660 */ unfreezeRotation()661 public void unfreezeRotation() throws RemoteException { 662 Tracer.trace(); 663 getAutomatorBridge().getInteractionController().unfreezeRotation(); 664 } 665 666 /** 667 * Simulates orienting the device to the left and also freezes rotation 668 * by disabling the sensors. 669 * 670 * If you want to un-freeze the rotation and re-enable the sensors 671 * see {@link #unfreezeRotation()}. 672 * @throws RemoteException 673 * @since API Level 17 674 */ setOrientationLeft()675 public void setOrientationLeft() throws RemoteException { 676 Tracer.trace(); 677 getAutomatorBridge().getInteractionController().setRotationLeft(); 678 waitForIdle(); // we don't need to check for idle on entry for this. We'll sync on exit 679 } 680 681 /** 682 * Simulates orienting the device to the right and also freezes rotation 683 * by disabling the sensors. 684 * 685 * If you want to un-freeze the rotation and re-enable the sensors 686 * see {@link #unfreezeRotation()}. 687 * @throws RemoteException 688 * @since API Level 17 689 */ setOrientationRight()690 public void setOrientationRight() throws RemoteException { 691 Tracer.trace(); 692 getAutomatorBridge().getInteractionController().setRotationRight(); 693 waitForIdle(); // we don't need to check for idle on entry for this. We'll sync on exit 694 } 695 696 /** 697 * Simulates orienting the device into its natural orientation and also freezes rotation 698 * by disabling the sensors. 699 * 700 * If you want to un-freeze the rotation and re-enable the sensors 701 * see {@link #unfreezeRotation()}. 702 * @throws RemoteException 703 * @since API Level 17 704 */ setOrientationNatural()705 public void setOrientationNatural() throws RemoteException { 706 Tracer.trace(); 707 getAutomatorBridge().getInteractionController().setRotationNatural(); 708 waitForIdle(); // we don't need to check for idle on entry for this. We'll sync on exit 709 } 710 711 /** 712 * This method simulates pressing the power button if the screen is OFF else 713 * it does nothing if the screen is already ON. 714 * 715 * If the screen was OFF and it just got turned ON, this method will insert a 500ms delay 716 * to allow the device time to wake up and accept input. 717 * @throws RemoteException 718 * @since API Level 16 719 */ wakeUp()720 public void wakeUp() throws RemoteException { 721 Tracer.trace(); 722 if(getAutomatorBridge().getInteractionController().wakeDevice()) { 723 // sync delay to allow the window manager to start accepting input 724 // after the device is awakened. 725 SystemClock.sleep(500); 726 } 727 } 728 729 /** 730 * Checks the power manager if the screen is ON. 731 * 732 * @return true if the screen is ON else false 733 * @throws RemoteException 734 * @since API Level 16 735 */ isScreenOn()736 public boolean isScreenOn() throws RemoteException { 737 Tracer.trace(); 738 return getAutomatorBridge().getInteractionController().isScreenOn(); 739 } 740 741 /** 742 * This method simply presses the power button if the screen is ON else 743 * it does nothing if the screen is already OFF. 744 * 745 * @throws RemoteException 746 * @since API Level 16 747 */ sleep()748 public void sleep() throws RemoteException { 749 Tracer.trace(); 750 getAutomatorBridge().getInteractionController().sleepDevice(); 751 } 752 753 /** 754 * Helper method used for debugging to dump the current window's layout hierarchy. 755 * The file root location is /data/local/tmp 756 * 757 * @param fileName 758 * @since API Level 16 759 */ dumpWindowHierarchy(String fileName)760 public void dumpWindowHierarchy(String fileName) { 761 Tracer.trace(fileName); 762 AccessibilityNodeInfo root = 763 getAutomatorBridge().getQueryController().getAccessibilityRootNode(); 764 if(root != null) { 765 Display display = getAutomatorBridge().getDefaultDisplay(); 766 Point size = new Point(); 767 display.getSize(size); 768 AccessibilityNodeInfoDumper.dumpWindowToFile(root, 769 new File(new File(Environment.getDataDirectory(), "local/tmp"), fileName), 770 display.getRotation(), size.x, size.y); 771 } 772 } 773 774 /** 775 * Waits for a window content update event to occur. 776 * 777 * If a package name for the window is specified, but the current window 778 * does not have the same package name, the function returns immediately. 779 * 780 * @param packageName the specified window package name (can be <code>null</code>). 781 * If <code>null</code>, a window update from any front-end window will end the wait 782 * @param timeout the timeout for the wait 783 * 784 * @return true if a window update occurred, false if timeout has elapsed or if the current 785 * window does not have the specified package name 786 * @since API Level 16 787 */ waitForWindowUpdate(final String packageName, long timeout)788 public boolean waitForWindowUpdate(final String packageName, long timeout) { 789 Tracer.trace(packageName, timeout); 790 if (packageName != null) { 791 if (!packageName.equals(getCurrentPackageName())) { 792 return false; 793 } 794 } 795 Runnable emptyRunnable = new Runnable() { 796 @Override 797 public void run() { 798 } 799 }; 800 AccessibilityEventFilter checkWindowUpdate = new AccessibilityEventFilter() { 801 @Override 802 public boolean accept(AccessibilityEvent t) { 803 if (t.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) { 804 return packageName == null || packageName.equals(t.getPackageName()); 805 } 806 return false; 807 } 808 }; 809 try { 810 getAutomatorBridge().executeCommandAndWaitForAccessibilityEvent( 811 emptyRunnable, checkWindowUpdate, timeout); 812 } catch (TimeoutException e) { 813 return false; 814 } catch (Exception e) { 815 Log.e(LOG_TAG, "waitForWindowUpdate: general exception from bridge", e); 816 return false; 817 } 818 return true; 819 } 820 821 /** 822 * Take a screenshot of current window and store it as PNG 823 * 824 * Default scale of 1.0f (original size) and 90% quality is used 825 * The screenshot is adjusted per screen rotation 826 * 827 * @param storePath where the PNG should be written to 828 * @return true if screen shot is created successfully, false otherwise 829 * @since API Level 17 830 */ takeScreenshot(File storePath)831 public boolean takeScreenshot(File storePath) { 832 Tracer.trace(storePath); 833 return takeScreenshot(storePath, 1.0f, 90); 834 } 835 836 /** 837 * Take a screenshot of current window and store it as PNG 838 * 839 * The screenshot is adjusted per screen rotation 840 * 841 * @param storePath where the PNG should be written to 842 * @param scale scale the screenshot down if needed; 1.0f for original size 843 * @param quality quality of the PNG compression; range: 0-100 844 * @return true if screen shot is created successfully, false otherwise 845 * @since API Level 17 846 */ takeScreenshot(File storePath, float scale, int quality)847 public boolean takeScreenshot(File storePath, float scale, int quality) { 848 Tracer.trace(storePath, scale, quality); 849 return getAutomatorBridge().takeScreenshot(storePath, quality); 850 } 851 } 852