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.graphics.Point; 20 import android.graphics.Rect; 21 import android.os.SystemClock; 22 import android.util.Log; 23 import android.view.KeyEvent; 24 import android.view.MotionEvent.PointerCoords; 25 import android.view.accessibility.AccessibilityNodeInfo; 26 27 /** 28 * A UiObject is a representation of a view. It is not in any way directly bound to a 29 * view as an object reference. A UiObject contains information to help it 30 * locate a matching view at runtime based on the {@link UiSelector} properties specified in 31 * its constructor. Once you create an instance of a UiObject, it can 32 * be reused for different views that match the selector criteria. 33 * @since API Level 16 34 * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the 35 * Android Testing Support Library. 36 */ 37 @Deprecated 38 public class UiObject { 39 private static final String LOG_TAG = UiObject.class.getSimpleName(); 40 /** 41 * @since API Level 16 42 * @deprecated use {@link Configurator#setWaitForSelectorTimeout(long)} 43 **/ 44 @Deprecated 45 protected static final long WAIT_FOR_SELECTOR_TIMEOUT = 10 * 1000; 46 /** 47 * @since API Level 16 48 **/ 49 protected static final long WAIT_FOR_SELECTOR_POLL = 1000; 50 // set a default timeout to 5.5s, since ANR threshold is 5s 51 /** 52 * @since API Level 16 53 **/ 54 protected static final long WAIT_FOR_WINDOW_TMEOUT = 5500; 55 /** 56 * @since API Level 16 57 **/ 58 protected static final int SWIPE_MARGIN_LIMIT = 5; 59 /** 60 * @since API Level 17 61 * @deprecated use {@link Configurator#setScrollAcknowledgmentTimeout(long)} 62 **/ 63 @Deprecated 64 protected static final long WAIT_FOR_EVENT_TMEOUT = 3 * 1000; 65 /** 66 * @since API Level 18 67 **/ 68 protected static final int FINGER_TOUCH_HALF_WIDTH = 20; 69 70 private final UiSelector mSelector; 71 72 private final Configurator mConfig = Configurator.getInstance(); 73 74 /** 75 * Constructs a UiObject to represent a view that matches the specified 76 * selector criteria. 77 * @param selector 78 * @since API Level 16 79 */ UiObject(UiSelector selector)80 public UiObject(UiSelector selector) { 81 mSelector = selector; 82 } 83 84 /** 85 * Debugging helper. A test can dump the properties of a selector as a string 86 * to its logs if needed. <code>getSelector().toString();</code> 87 * 88 * @return {@link UiSelector} 89 * @since API Level 16 90 */ getSelector()91 public final UiSelector getSelector() { 92 Tracer.trace(); 93 return new UiSelector(mSelector); 94 } 95 96 /** 97 * Retrieves the {@link QueryController} to translate a {@link UiSelector} selector 98 * into an {@link AccessibilityNodeInfo}. 99 * 100 * @return {@link QueryController} 101 */ getQueryController()102 QueryController getQueryController() { 103 return UiDevice.getInstance().getAutomatorBridge().getQueryController(); 104 } 105 106 /** 107 * Retrieves the {@link InteractionController} to perform finger actions such as tapping, 108 * swiping, or entering text. 109 * 110 * @return {@link InteractionController} 111 */ getInteractionController()112 InteractionController getInteractionController() { 113 return UiDevice.getInstance().getAutomatorBridge().getInteractionController(); 114 } 115 116 /** 117 * Creates a new UiObject for a child view that is under the present UiObject. 118 * 119 * @param selector for child view to match 120 * @return a new UiObject representing the child view 121 * @since API Level 16 122 */ getChild(UiSelector selector)123 public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException { 124 Tracer.trace(selector); 125 return new UiObject(getSelector().childSelector(selector)); 126 } 127 128 /** 129 * Creates a new UiObject for a sibling view or a child of the sibling view, 130 * relative to the present UiObject. 131 * 132 * @param selector for a sibling view or children of the sibling view 133 * @return a new UiObject representing the matched view 134 * @throws UiObjectNotFoundException 135 * @since API Level 16 136 */ getFromParent(UiSelector selector)137 public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException { 138 Tracer.trace(selector); 139 return new UiObject(getSelector().fromParent(selector)); 140 } 141 142 /** 143 * Counts the child views immediately under the present UiObject. 144 * 145 * @return the count of child views. 146 * @throws UiObjectNotFoundException 147 * @since API Level 16 148 */ getChildCount()149 public int getChildCount() throws UiObjectNotFoundException { 150 Tracer.trace(); 151 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 152 if(node == null) { 153 throw new UiObjectNotFoundException(getSelector().toString()); 154 } 155 return node.getChildCount(); 156 } 157 158 /** 159 * Finds a matching UI element in the accessibility hierarchy, by 160 * using the selector for this UiObject. 161 * 162 * @param timeout in milliseconds 163 * @return AccessibilityNodeInfo if found else null 164 * @since API Level 16 165 */ findAccessibilityNodeInfo(long timeout)166 protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) { 167 AccessibilityNodeInfo node = null; 168 long startMills = SystemClock.uptimeMillis(); 169 long currentMills = 0; 170 while (currentMills <= timeout) { 171 node = getQueryController().findAccessibilityNodeInfo(getSelector()); 172 if (node != null) { 173 break; 174 } else { 175 // does nothing if we're reentering another runWatchers() 176 UiDevice.getInstance().runWatchers(); 177 } 178 currentMills = SystemClock.uptimeMillis() - startMills; 179 if(timeout > 0) { 180 SystemClock.sleep(WAIT_FOR_SELECTOR_POLL); 181 } 182 } 183 return node; 184 } 185 186 /** 187 * Drags this object to a destination UiObject. 188 * The number of steps specified in your input parameter can influence the 189 * drag speed, and varying speeds may impact the results. Consider 190 * evaluating different speeds when using this method in your tests. 191 * 192 * @param destObj the destination UiObject. 193 * @param steps usually 40 steps. You can increase or decrease the steps to change the speed. 194 * @return true if successful 195 * @throws UiObjectNotFoundException 196 * @since API Level 18 197 */ dragTo(UiObject destObj, int steps)198 public boolean dragTo(UiObject destObj, int steps) throws UiObjectNotFoundException { 199 Rect srcRect = getVisibleBounds(); 200 Rect dstRect = destObj.getVisibleBounds(); 201 return getInteractionController().swipe(srcRect.centerX(), srcRect.centerY(), 202 dstRect.centerX(), dstRect.centerY(), steps, true); 203 } 204 205 /** 206 * Drags this object to arbitrary coordinates. 207 * The number of steps specified in your input parameter can influence the 208 * drag speed, and varying speeds may impact the results. Consider 209 * evaluating different speeds when using this method in your tests. 210 * 211 * @param destX the X-axis coordinate. 212 * @param destY the Y-axis coordinate. 213 * @param steps usually 40 steps. You can increase or decrease the steps to change the speed. 214 * @return true if successful 215 * @throws UiObjectNotFoundException 216 * @since API Level 18 217 */ dragTo(int destX, int destY, int steps)218 public boolean dragTo(int destX, int destY, int steps) throws UiObjectNotFoundException { 219 Rect srcRect = getVisibleBounds(); 220 return getInteractionController().swipe(srcRect.centerX(), srcRect.centerY(), destX, destY, 221 steps, true); 222 } 223 224 /** 225 * Performs the swipe up action on the UiObject. 226 * See also: 227 * <ul> 228 * <li>{@link UiScrollable#scrollToBeginning(int)}</li> 229 * <li>{@link UiScrollable#scrollToEnd(int)}</li> 230 * <li>{@link UiScrollable#scrollBackward()}</li> 231 * <li>{@link UiScrollable#scrollForward()}</li> 232 * </ul> 233 * 234 * @param steps indicates the number of injected move steps into the system. Steps are 235 * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete. 236 * @return true of successful 237 * @throws UiObjectNotFoundException 238 * @since API Level 16 239 */ swipeUp(int steps)240 public boolean swipeUp(int steps) throws UiObjectNotFoundException { 241 Tracer.trace(steps); 242 Rect rect = getVisibleBounds(); 243 if(rect.height() <= SWIPE_MARGIN_LIMIT * 2) 244 return false; // too small to swipe 245 return getInteractionController().swipe(rect.centerX(), 246 rect.bottom - SWIPE_MARGIN_LIMIT, rect.centerX(), rect.top + SWIPE_MARGIN_LIMIT, 247 steps); 248 } 249 250 /** 251 * Performs the swipe down action on the UiObject. 252 * The swipe gesture can be performed over any surface. The targeted 253 * UI element does not need to be scrollable. 254 * See also: 255 * <ul> 256 * <li>{@link UiScrollable#scrollToBeginning(int)}</li> 257 * <li>{@link UiScrollable#scrollToEnd(int)}</li> 258 * <li>{@link UiScrollable#scrollBackward()}</li> 259 * <li>{@link UiScrollable#scrollForward()}</li> 260 * </ul> 261 * 262 * @param steps indicates the number of injected move steps into the system. Steps are 263 * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete. 264 * @return true if successful 265 * @throws UiObjectNotFoundException 266 * @since API Level 16 267 */ swipeDown(int steps)268 public boolean swipeDown(int steps) throws UiObjectNotFoundException { 269 Tracer.trace(steps); 270 Rect rect = getVisibleBounds(); 271 if(rect.height() <= SWIPE_MARGIN_LIMIT * 2) 272 return false; // too small to swipe 273 return getInteractionController().swipe(rect.centerX(), 274 rect.top + SWIPE_MARGIN_LIMIT, rect.centerX(), 275 rect.bottom - SWIPE_MARGIN_LIMIT, steps); 276 } 277 278 /** 279 * Performs the swipe left action on the UiObject. 280 * The swipe gesture can be performed over any surface. The targeted 281 * UI element does not need to be scrollable. 282 * See also: 283 * <ul> 284 * <li>{@link UiScrollable#scrollToBeginning(int)}</li> 285 * <li>{@link UiScrollable#scrollToEnd(int)}</li> 286 * <li>{@link UiScrollable#scrollBackward()}</li> 287 * <li>{@link UiScrollable#scrollForward()}</li> 288 * </ul> 289 * 290 * @param steps indicates the number of injected move steps into the system. Steps are 291 * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete. 292 * @return true if successful 293 * @throws UiObjectNotFoundException 294 * @since API Level 16 295 */ swipeLeft(int steps)296 public boolean swipeLeft(int steps) throws UiObjectNotFoundException { 297 Tracer.trace(steps); 298 Rect rect = getVisibleBounds(); 299 if(rect.width() <= SWIPE_MARGIN_LIMIT * 2) 300 return false; // too small to swipe 301 return getInteractionController().swipe(rect.right - SWIPE_MARGIN_LIMIT, 302 rect.centerY(), rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), steps); 303 } 304 305 /** 306 * Performs the swipe right action on the UiObject. 307 * The swipe gesture can be performed over any surface. The targeted 308 * UI element does not need to be scrollable. 309 * See also: 310 * <ul> 311 * <li>{@link UiScrollable#scrollToBeginning(int)}</li> 312 * <li>{@link UiScrollable#scrollToEnd(int)}</li> 313 * <li>{@link UiScrollable#scrollBackward()}</li> 314 * <li>{@link UiScrollable#scrollForward()}</li> 315 * </ul> 316 * 317 * @param steps indicates the number of injected move steps into the system. Steps are 318 * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete. 319 * @return true if successful 320 * @throws UiObjectNotFoundException 321 * @since API Level 16 322 */ swipeRight(int steps)323 public boolean swipeRight(int steps) throws UiObjectNotFoundException { 324 Tracer.trace(steps); 325 Rect rect = getVisibleBounds(); 326 if(rect.width() <= SWIPE_MARGIN_LIMIT * 2) 327 return false; // too small to swipe 328 return getInteractionController().swipe(rect.left + SWIPE_MARGIN_LIMIT, 329 rect.centerY(), rect.right - SWIPE_MARGIN_LIMIT, rect.centerY(), steps); 330 } 331 332 /** 333 * Finds the visible bounds of a partially visible UI element 334 * 335 * @param node 336 * @return null if node is null, else a Rect containing visible bounds 337 */ getVisibleBounds(AccessibilityNodeInfo node)338 private Rect getVisibleBounds(AccessibilityNodeInfo node) { 339 if (node == null) { 340 return null; 341 } 342 343 // targeted node's bounds 344 int w = UiDevice.getInstance().getDisplayWidth(); 345 int h = UiDevice.getInstance().getDisplayHeight(); 346 Rect nodeRect = AccessibilityNodeInfoHelper.getVisibleBoundsInScreen(node, w, h); 347 348 // is the targeted node within a scrollable container? 349 AccessibilityNodeInfo scrollableParentNode = getScrollableParent(node); 350 if(scrollableParentNode == null) { 351 // nothing to adjust for so return the node's Rect as is 352 return nodeRect; 353 } 354 355 // Scrollable parent's visible bounds 356 Rect parentRect = AccessibilityNodeInfoHelper 357 .getVisibleBoundsInScreen(scrollableParentNode, w, h); 358 // adjust for partial clipping of targeted by parent node if required 359 if (nodeRect.intersect(parentRect)) { 360 return nodeRect; 361 } else { 362 // Node rect has no intersection with parent Rect 363 return new Rect(); 364 } 365 } 366 367 /** 368 * Walks up the layout hierarchy to find a scrollable parent. A scrollable parent 369 * indicates that this node might be in a container where it is partially 370 * visible due to scrolling. In this case, its clickable center might not be visible and 371 * the click coordinates should be adjusted. 372 * 373 * @param node 374 * @return The accessibility node info. 375 */ getScrollableParent(AccessibilityNodeInfo node)376 private AccessibilityNodeInfo getScrollableParent(AccessibilityNodeInfo node) { 377 AccessibilityNodeInfo parent = node; 378 while(parent != null) { 379 parent = parent.getParent(); 380 if (parent != null && parent.isScrollable()) { 381 return parent; 382 } 383 } 384 return null; 385 } 386 387 /** 388 * Performs a click at the center of the visible bounds of the UI element represented 389 * by this UiObject. 390 * 391 * @return true id successful else false 392 * @throws UiObjectNotFoundException 393 * @since API Level 16 394 */ click()395 public boolean click() throws UiObjectNotFoundException { 396 Tracer.trace(); 397 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 398 if(node == null) { 399 throw new UiObjectNotFoundException(getSelector().toString()); 400 } 401 Rect rect = getVisibleBounds(node); 402 return getInteractionController().clickAndSync(rect.centerX(), rect.centerY(), 403 mConfig.getActionAcknowledgmentTimeout()); 404 } 405 406 /** 407 * Waits for window transitions that would typically take longer than the 408 * usual default timeouts. 409 * See {@link #clickAndWaitForNewWindow(long)} 410 * 411 * @return true if the event was triggered, else false 412 * @throws UiObjectNotFoundException 413 * @since API Level 16 414 */ clickAndWaitForNewWindow()415 public boolean clickAndWaitForNewWindow() throws UiObjectNotFoundException { 416 Tracer.trace(); 417 return clickAndWaitForNewWindow(WAIT_FOR_WINDOW_TMEOUT); 418 } 419 420 /** 421 * Performs a click at the center of the visible bounds of the UI element represented 422 * by this UiObject and waits for window transitions. 423 * 424 * This method differ from {@link UiObject#click()} only in that this method waits for a 425 * a new window transition as a result of the click. Some examples of a window transition: 426 * <li>launching a new activity</li> 427 * <li>bringing up a pop-up menu</li> 428 * <li>bringing up a dialog</li> 429 * 430 * @param timeout timeout before giving up on waiting for a new window 431 * @return true if the event was triggered, else false 432 * @throws UiObjectNotFoundException 433 * @since API Level 16 434 */ clickAndWaitForNewWindow(long timeout)435 public boolean clickAndWaitForNewWindow(long timeout) throws UiObjectNotFoundException { 436 Tracer.trace(timeout); 437 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 438 if(node == null) { 439 throw new UiObjectNotFoundException(getSelector().toString()); 440 } 441 Rect rect = getVisibleBounds(node); 442 return getInteractionController().clickAndWaitForNewWindow(rect.centerX(), rect.centerY(), 443 mConfig.getActionAcknowledgmentTimeout()); 444 } 445 446 /** 447 * Clicks the top and left corner of the UI element 448 * 449 * @return true on success 450 * @throws UiObjectNotFoundException 451 * @since API Level 16 452 */ clickTopLeft()453 public boolean clickTopLeft() throws UiObjectNotFoundException { 454 Tracer.trace(); 455 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 456 if(node == null) { 457 throw new UiObjectNotFoundException(getSelector().toString()); 458 } 459 Rect rect = getVisibleBounds(node); 460 return getInteractionController().clickNoSync(rect.left + 5, rect.top + 5); 461 } 462 463 /** 464 * Long clicks bottom and right corner of the UI element 465 * 466 * @return true if operation was successful 467 * @throws UiObjectNotFoundException 468 * @since API Level 16 469 */ longClickBottomRight()470 public boolean longClickBottomRight() throws UiObjectNotFoundException { 471 Tracer.trace(); 472 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 473 if(node == null) { 474 throw new UiObjectNotFoundException(getSelector().toString()); 475 } 476 Rect rect = getVisibleBounds(node); 477 return getInteractionController().longTapNoSync(rect.right - 5, rect.bottom - 5); 478 } 479 480 /** 481 * Clicks the bottom and right corner of the UI element 482 * 483 * @return true on success 484 * @throws UiObjectNotFoundException 485 * @since API Level 16 486 */ clickBottomRight()487 public boolean clickBottomRight() throws UiObjectNotFoundException { 488 Tracer.trace(); 489 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 490 if(node == null) { 491 throw new UiObjectNotFoundException(getSelector().toString()); 492 } 493 Rect rect = getVisibleBounds(node); 494 return getInteractionController().clickNoSync(rect.right - 5, rect.bottom - 5); 495 } 496 497 /** 498 * Long clicks the center of the visible bounds of the UI element 499 * 500 * @return true if operation was successful 501 * @throws UiObjectNotFoundException 502 * @since API Level 16 503 */ longClick()504 public boolean longClick() throws UiObjectNotFoundException { 505 Tracer.trace(); 506 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 507 if(node == null) { 508 throw new UiObjectNotFoundException(getSelector().toString()); 509 } 510 Rect rect = getVisibleBounds(node); 511 return getInteractionController().longTapNoSync(rect.centerX(), rect.centerY()); 512 } 513 514 /** 515 * Long clicks on the top and left corner of the UI element 516 * 517 * @return true if operation was successful 518 * @throws UiObjectNotFoundException 519 * @since API Level 16 520 */ longClickTopLeft()521 public boolean longClickTopLeft() throws UiObjectNotFoundException { 522 Tracer.trace(); 523 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 524 if(node == null) { 525 throw new UiObjectNotFoundException(getSelector().toString()); 526 } 527 Rect rect = getVisibleBounds(node); 528 return getInteractionController().longTapNoSync(rect.left + 5, rect.top + 5); 529 } 530 531 /** 532 * Reads the <code>text</code> property of the UI element 533 * 534 * @return text value of the current node represented by this UiObject 535 * @throws UiObjectNotFoundException if no match could be found 536 * @since API Level 16 537 */ getText()538 public String getText() throws UiObjectNotFoundException { 539 Tracer.trace(); 540 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 541 if(node == null) { 542 throw new UiObjectNotFoundException(getSelector().toString()); 543 } 544 String retVal = safeStringReturn(node.getText()); 545 Log.d(LOG_TAG, String.format("getText() = %s", retVal)); 546 return retVal; 547 } 548 549 /** 550 * Retrieves the <code>className</code> property of the UI element. 551 * 552 * @return class name of the current node represented by this UiObject 553 * @throws UiObjectNotFoundException if no match was found 554 * @since API Level 18 555 */ getClassName()556 public String getClassName() throws UiObjectNotFoundException { 557 Tracer.trace(); 558 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 559 if(node == null) { 560 throw new UiObjectNotFoundException(getSelector().toString()); 561 } 562 String retVal = safeStringReturn(node.getClassName()); 563 Log.d(LOG_TAG, String.format("getClassName() = %s", retVal)); 564 return retVal; 565 } 566 567 /** 568 * Reads the <code>content_desc</code> property of the UI element 569 * 570 * @return value of node attribute "content_desc" 571 * @throws UiObjectNotFoundException 572 * @since API Level 16 573 */ getContentDescription()574 public String getContentDescription() throws UiObjectNotFoundException { 575 Tracer.trace(); 576 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 577 if(node == null) { 578 throw new UiObjectNotFoundException(getSelector().toString()); 579 } 580 return safeStringReturn(node.getContentDescription()); 581 } 582 583 /** 584 * Sets the text in an editable field, after clearing the field's content. 585 * 586 * The {@link UiSelector} selector of this object must reference a UI element that is editable. 587 * 588 * When you call this method, the method first simulates a {@link #click()} on 589 * editable field to set focus. The method then clears the field's contents 590 * and injects your specified text into the field. 591 * 592 * If you want to capture the original contents of the field, call {@link #getText()} first. 593 * You can then modify the text and use this method to update the field. 594 * 595 * @param text string to set 596 * @return true if operation is successful 597 * @throws UiObjectNotFoundException 598 * @since API Level 16 599 */ setText(String text)600 public boolean setText(String text) throws UiObjectNotFoundException { 601 Tracer.trace(text); 602 clearTextField(); 603 return getInteractionController().sendText(text); 604 } 605 606 /** 607 * Clears the existing text contents in an editable field. 608 * 609 * The {@link UiSelector} of this object must reference a UI element that is editable. 610 * 611 * When you call this method, the method first sets focus at the start edge of the field. 612 * The method then simulates a long-press to select the existing text, and deletes the 613 * selected text. 614 * 615 * If a "Select-All" option is displayed, the method will automatically attempt to use it 616 * to ensure full text selection. 617 * 618 * Note that it is possible that not all the text in the field is selected; for example, 619 * if the text contains separators such as spaces, slashes, at symbol etc. 620 * Also, not all editable fields support the long-press functionality. 621 * 622 * @throws UiObjectNotFoundException 623 * @since API Level 16 624 */ clearTextField()625 public void clearTextField() throws UiObjectNotFoundException { 626 Tracer.trace(); 627 // long click left + center 628 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 629 if(node == null) { 630 throw new UiObjectNotFoundException(getSelector().toString()); 631 } 632 Rect rect = getVisibleBounds(node); 633 getInteractionController().longTapNoSync(rect.left + 20, rect.centerY()); 634 // check if the edit menu is open 635 UiObject selectAll = new UiObject(new UiSelector().descriptionContains("Select all")); 636 if(selectAll.waitForExists(50)) 637 selectAll.click(); 638 // wait for the selection 639 SystemClock.sleep(250); 640 // delete it 641 getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0); 642 } 643 644 /** 645 * Check if the UI element's <code>checked</code> property is currently true 646 * 647 * @return true if it is else false 648 * @since API Level 16 649 */ isChecked()650 public boolean isChecked() throws UiObjectNotFoundException { 651 Tracer.trace(); 652 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 653 if(node == null) { 654 throw new UiObjectNotFoundException(getSelector().toString()); 655 } 656 return node.isChecked(); 657 } 658 659 /** 660 * Checks if the UI element's <code>selected</code> property is currently true. 661 * 662 * @return true if it is else false 663 * @throws UiObjectNotFoundException 664 * @since API Level 16 665 */ isSelected()666 public boolean isSelected() throws UiObjectNotFoundException { 667 Tracer.trace(); 668 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 669 if(node == null) { 670 throw new UiObjectNotFoundException(getSelector().toString()); 671 } 672 return node.isSelected(); 673 } 674 675 /** 676 * Checks if the UI element's <code>checkable</code> property is currently true. 677 * 678 * @return true if it is else false 679 * @throws UiObjectNotFoundException 680 * @since API Level 16 681 */ isCheckable()682 public boolean isCheckable() throws UiObjectNotFoundException { 683 Tracer.trace(); 684 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 685 if(node == null) { 686 throw new UiObjectNotFoundException(getSelector().toString()); 687 } 688 return node.isCheckable(); 689 } 690 691 /** 692 * Checks if the UI element's <code>enabled</code> property is currently true. 693 * 694 * @return true if it is else false 695 * @throws UiObjectNotFoundException 696 * @since API Level 16 697 */ isEnabled()698 public boolean isEnabled() throws UiObjectNotFoundException { 699 Tracer.trace(); 700 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 701 if(node == null) { 702 throw new UiObjectNotFoundException(getSelector().toString()); 703 } 704 return node.isEnabled(); 705 } 706 707 /** 708 * Checks if the UI element's <code>clickable</code> property is currently true. 709 * 710 * @return true if it is else false 711 * @throws UiObjectNotFoundException 712 * @since API Level 16 713 */ isClickable()714 public boolean isClickable() throws UiObjectNotFoundException { 715 Tracer.trace(); 716 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 717 if(node == null) { 718 throw new UiObjectNotFoundException(getSelector().toString()); 719 } 720 return node.isClickable(); 721 } 722 723 /** 724 * Check if the UI element's <code>focused</code> property is currently true 725 * 726 * @return true if it is else false 727 * @throws UiObjectNotFoundException 728 * @since API Level 16 729 */ isFocused()730 public boolean isFocused() throws UiObjectNotFoundException { 731 Tracer.trace(); 732 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 733 if(node == null) { 734 throw new UiObjectNotFoundException(getSelector().toString()); 735 } 736 return node.isFocused(); 737 } 738 739 /** 740 * Check if the UI element's <code>focusable</code> property is currently true. 741 * 742 * @return true if it is else false 743 * @throws UiObjectNotFoundException 744 * @since API Level 16 745 */ isFocusable()746 public boolean isFocusable() throws UiObjectNotFoundException { 747 Tracer.trace(); 748 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 749 if(node == null) { 750 throw new UiObjectNotFoundException(getSelector().toString()); 751 } 752 return node.isFocusable(); 753 } 754 755 /** 756 * Check if the view's <code>scrollable</code> property is currently true 757 * 758 * @return true if it is else false 759 * @throws UiObjectNotFoundException 760 * @since API Level 16 761 */ isScrollable()762 public boolean isScrollable() throws UiObjectNotFoundException { 763 Tracer.trace(); 764 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 765 if(node == null) { 766 throw new UiObjectNotFoundException(getSelector().toString()); 767 } 768 return node.isScrollable(); 769 } 770 771 /** 772 * Check if the view's <code>long-clickable</code> property is currently true 773 * 774 * @return true if it is else false 775 * @throws UiObjectNotFoundException 776 * @since API Level 16 777 */ isLongClickable()778 public boolean isLongClickable() throws UiObjectNotFoundException { 779 Tracer.trace(); 780 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 781 if(node == null) { 782 throw new UiObjectNotFoundException(getSelector().toString()); 783 } 784 return node.isLongClickable(); 785 } 786 787 /** 788 * Reads the view's <code>package</code> property 789 * 790 * @return true if it is else false 791 * @throws UiObjectNotFoundException 792 * @since API Level 16 793 */ getPackageName()794 public String getPackageName() throws UiObjectNotFoundException { 795 Tracer.trace(); 796 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 797 if(node == null) { 798 throw new UiObjectNotFoundException(getSelector().toString()); 799 } 800 return safeStringReturn(node.getPackageName()); 801 } 802 803 /** 804 * Returns the visible bounds of the view. 805 * 806 * If a portion of the view is visible, only the bounds of the visible portion are 807 * reported. 808 * 809 * @return Rect 810 * @throws UiObjectNotFoundException 811 * @see {@link #getBounds()} 812 * @since API Level 17 813 */ getVisibleBounds()814 public Rect getVisibleBounds() throws UiObjectNotFoundException { 815 Tracer.trace(); 816 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 817 if(node == null) { 818 throw new UiObjectNotFoundException(getSelector().toString()); 819 } 820 return getVisibleBounds(node); 821 } 822 823 /** 824 * Returns the view's <code>bounds</code> property. See {@link #getVisibleBounds()} 825 * 826 * @return Rect 827 * @throws UiObjectNotFoundException 828 * @since API Level 16 829 */ getBounds()830 public Rect getBounds() throws UiObjectNotFoundException { 831 Tracer.trace(); 832 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 833 if(node == null) { 834 throw new UiObjectNotFoundException(getSelector().toString()); 835 } 836 Rect nodeRect = new Rect(); 837 node.getBoundsInScreen(nodeRect); 838 839 return nodeRect; 840 } 841 842 /** 843 * Waits a specified length of time for a view to become visible. 844 * 845 * This method waits until the view becomes visible on the display, or 846 * until the timeout has elapsed. You can use this method in situations where 847 * the content that you want to select is not immediately displayed. 848 * 849 * @param timeout the amount of time to wait (in milliseconds) 850 * @return true if the view is displayed, else false if timeout elapsed while waiting 851 * @since API Level 16 852 */ waitForExists(long timeout)853 public boolean waitForExists(long timeout) { 854 Tracer.trace(timeout); 855 if(findAccessibilityNodeInfo(timeout) != null) { 856 return true; 857 } 858 return false; 859 } 860 861 /** 862 * Waits a specified length of time for a view to become undetectable. 863 * 864 * This method waits until a view is no longer matchable, or until the 865 * timeout has elapsed. 866 * 867 * A view becomes undetectable when the {@link UiSelector} of the object is 868 * unable to find a match because the element has either changed its state or is no 869 * longer displayed. 870 * 871 * You can use this method when attempting to wait for some long operation 872 * to compete, such as downloading a large file or connecting to a remote server. 873 * 874 * @param timeout time to wait (in milliseconds) 875 * @return true if the element is gone before timeout elapsed, else false if timeout elapsed 876 * but a matching element is still found. 877 * @since API Level 16 878 */ waitUntilGone(long timeout)879 public boolean waitUntilGone(long timeout) { 880 Tracer.trace(timeout); 881 long startMills = SystemClock.uptimeMillis(); 882 long currentMills = 0; 883 while (currentMills <= timeout) { 884 if(findAccessibilityNodeInfo(0) == null) 885 return true; 886 currentMills = SystemClock.uptimeMillis() - startMills; 887 if(timeout > 0) 888 SystemClock.sleep(WAIT_FOR_SELECTOR_POLL); 889 } 890 return false; 891 } 892 893 /** 894 * Check if view exists. 895 * 896 * This methods performs a {@link #waitForExists(long)} with zero timeout. This 897 * basically returns immediately whether the view represented by this UiObject 898 * exists or not. If you need to wait longer for this view, then see 899 * {@link #waitForExists(long)}. 900 * 901 * @return true if the view represented by this UiObject does exist 902 * @since API Level 16 903 */ exists()904 public boolean exists() { 905 Tracer.trace(); 906 return waitForExists(0); 907 } 908 safeStringReturn(CharSequence cs)909 private String safeStringReturn(CharSequence cs) { 910 if(cs == null) 911 return ""; 912 return cs.toString(); 913 } 914 915 /** 916 * Performs a two-pointer gesture, where each pointer moves diagonally 917 * opposite across the other, from the center out towards the edges of the 918 * this UiObject. 919 * @param percent percentage of the object's diagonal length for the pinch gesture 920 * @param steps the number of steps for the gesture. Steps are injected 921 * about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete. 922 * @return <code>true</code> if all touch events for this gesture are injected successfully, 923 * <code>false</code> otherwise 924 * @throws UiObjectNotFoundException 925 * @since API Level 18 926 */ pinchOut(int percent, int steps)927 public boolean pinchOut(int percent, int steps) throws UiObjectNotFoundException { 928 // make value between 1 and 100 929 percent = (percent < 0) ? 1 : (percent > 100) ? 100 : percent; 930 float percentage = percent / 100f; 931 932 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 933 if (node == null) { 934 throw new UiObjectNotFoundException(getSelector().toString()); 935 } 936 937 Rect rect = getVisibleBounds(node); 938 if (rect.width() <= FINGER_TOUCH_HALF_WIDTH * 2) 939 throw new IllegalStateException("Object width is too small for operation"); 940 941 // start from the same point at the center of the control 942 Point startPoint1 = new Point(rect.centerX() - FINGER_TOUCH_HALF_WIDTH, rect.centerY()); 943 Point startPoint2 = new Point(rect.centerX() + FINGER_TOUCH_HALF_WIDTH, rect.centerY()); 944 945 // End at the top-left and bottom-right corners of the control 946 Point endPoint1 = new Point(rect.centerX() - (int)((rect.width()/2) * percentage), 947 rect.centerY()); 948 Point endPoint2 = new Point(rect.centerX() + (int)((rect.width()/2) * percentage), 949 rect.centerY()); 950 951 return performTwoPointerGesture(startPoint1, startPoint2, endPoint1, endPoint2, steps); 952 } 953 954 /** 955 * Performs a two-pointer gesture, where each pointer moves diagonally 956 * toward the other, from the edges to the center of this UiObject . 957 * @param percent percentage of the object's diagonal length for the pinch gesture 958 * @param steps the number of steps for the gesture. Steps are injected 959 * about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete. 960 * @return <code>true</code> if all touch events for this gesture are injected successfully, 961 * <code>false</code> otherwise 962 * @throws UiObjectNotFoundException 963 * @since API Level 18 964 */ pinchIn(int percent, int steps)965 public boolean pinchIn(int percent, int steps) throws UiObjectNotFoundException { 966 // make value between 1 and 100 967 percent = (percent < 0) ? 0 : (percent > 100) ? 100 : percent; 968 float percentage = percent / 100f; 969 970 AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); 971 if (node == null) { 972 throw new UiObjectNotFoundException(getSelector().toString()); 973 } 974 975 Rect rect = getVisibleBounds(node); 976 if (rect.width() <= FINGER_TOUCH_HALF_WIDTH * 2) 977 throw new IllegalStateException("Object width is too small for operation"); 978 979 Point startPoint1 = new Point(rect.centerX() - (int)((rect.width()/2) * percentage), 980 rect.centerY()); 981 Point startPoint2 = new Point(rect.centerX() + (int)((rect.width()/2) * percentage), 982 rect.centerY()); 983 984 Point endPoint1 = new Point(rect.centerX() - FINGER_TOUCH_HALF_WIDTH, rect.centerY()); 985 Point endPoint2 = new Point(rect.centerX() + FINGER_TOUCH_HALF_WIDTH, rect.centerY()); 986 987 return performTwoPointerGesture(startPoint1, startPoint2, endPoint1, endPoint2, steps); 988 } 989 990 /** 991 * Generates a two-pointer gesture with arbitrary starting and ending points. 992 * 993 * @param startPoint1 start point of pointer 1 994 * @param startPoint2 start point of pointer 2 995 * @param endPoint1 end point of pointer 1 996 * @param endPoint2 end point of pointer 2 997 * @param steps the number of steps for the gesture. Steps are injected 998 * about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete. 999 * @return <code>true</code> if all touch events for this gesture are injected successfully, 1000 * <code>false</code> otherwise 1001 * @since API Level 18 1002 */ performTwoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1, Point endPoint2, int steps)1003 public boolean performTwoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1, 1004 Point endPoint2, int steps) { 1005 1006 // avoid a divide by zero 1007 if(steps == 0) 1008 steps = 1; 1009 1010 final float stepX1 = (endPoint1.x - startPoint1.x) / steps; 1011 final float stepY1 = (endPoint1.y - startPoint1.y) / steps; 1012 final float stepX2 = (endPoint2.x - startPoint2.x) / steps; 1013 final float stepY2 = (endPoint2.y - startPoint2.y) / steps; 1014 1015 int eventX1, eventY1, eventX2, eventY2; 1016 eventX1 = startPoint1.x; 1017 eventY1 = startPoint1.y; 1018 eventX2 = startPoint2.x; 1019 eventY2 = startPoint2.y; 1020 1021 // allocate for steps plus first down and last up 1022 PointerCoords[] points1 = new PointerCoords[steps + 2]; 1023 PointerCoords[] points2 = new PointerCoords[steps + 2]; 1024 1025 // Include the first and last touch downs in the arrays of steps 1026 for (int i = 0; i < steps + 1; i++) { 1027 PointerCoords p1 = new PointerCoords(); 1028 p1.x = eventX1; 1029 p1.y = eventY1; 1030 p1.pressure = 1; 1031 p1.size = 1; 1032 points1[i] = p1; 1033 1034 PointerCoords p2 = new PointerCoords(); 1035 p2.x = eventX2; 1036 p2.y = eventY2; 1037 p2.pressure = 1; 1038 p2.size = 1; 1039 points2[i] = p2; 1040 1041 eventX1 += stepX1; 1042 eventY1 += stepY1; 1043 eventX2 += stepX2; 1044 eventY2 += stepY2; 1045 } 1046 1047 // ending pointers coordinates 1048 PointerCoords p1 = new PointerCoords(); 1049 p1.x = endPoint1.x; 1050 p1.y = endPoint1.y; 1051 p1.pressure = 1; 1052 p1.size = 1; 1053 points1[steps + 1] = p1; 1054 1055 PointerCoords p2 = new PointerCoords(); 1056 p2.x = endPoint2.x; 1057 p2.y = endPoint2.y; 1058 p2.pressure = 1; 1059 p2.size = 1; 1060 points2[steps + 1] = p2; 1061 1062 return performMultiPointerGesture(points1, points2); 1063 } 1064 1065 /** 1066 * Performs a multi-touch gesture. You must specify touch coordinates for 1067 * at least 2 pointers. Each pointer must have all of its touch steps 1068 * defined in an array of {@link PointerCoords}. You can use this method to 1069 * specify complex gestures, like circles and irregular shapes, where each 1070 * pointer may take a different path. 1071 * 1072 * To create a single point on a pointer's touch path: 1073 * <code> 1074 * PointerCoords p = new PointerCoords(); 1075 * p.x = stepX; 1076 * p.y = stepY; 1077 * p.pressure = 1; 1078 * p.size = 1; 1079 * </code> 1080 * @param touches represents the pointers' paths. Each {@link PointerCoords} 1081 * array represents a different pointer. Each {@link PointerCoords} in an 1082 * array element represents a touch point on a pointer's path. 1083 * @return <code>true</code> if all touch events for this gesture are injected successfully, 1084 * <code>false</code> otherwise 1085 * @since API Level 18 1086 */ performMultiPointerGesture(PointerCoords[] ....touches)1087 public boolean performMultiPointerGesture(PointerCoords[] ...touches) { 1088 return getInteractionController().performMultiPointerGesture(touches); 1089 } 1090 } 1091