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