UiObject.java revision 6088c8f9e34e34a4958b1601ed7c1bb34c95da21
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 17package com.android.uiautomator.core; 18 19import android.graphics.Rect; 20import android.os.SystemClock; 21import android.util.Log; 22import android.view.KeyEvent; 23import android.view.accessibility.AccessibilityEvent; 24import android.view.accessibility.AccessibilityNodeInfo; 25 26/** 27 * A UiObject is a representation of a UI element. It is not in any way directly bound to a 28 * UI element as an object reference. A UiObject holds information to help it 29 * locate a matching UI element at runtime based on the {@link UiSelector} properties specified in 30 * its constructor. Since a UiObject is a representative for a UI element, it can 31 * be reused for different views with matching UI elements. 32 */ 33public class UiObject { 34 private static final String LOG_TAG = UiObject.class.getSimpleName(); 35 protected static final long WAIT_FOR_SELECTOR_TIMEOUT = 10 * 1000; 36 protected static final long WAIT_FOR_SELECTOR_POLL = 1000; 37 // set a default timeout to 5.5s, since ANR threshold is 5s 38 protected static final long WAIT_FOR_WINDOW_TMEOUT = 5500; 39 protected static final long WAIT_FOR_EVENT_TMEOUT = 3 * 1000; 40 protected static final int SWIPE_MARGIN_LIMIT = 5; 41 42 private final UiSelector mSelector; 43 private final UiAutomatorBridge mUiAutomationBridge; 44 45 /** 46 * Constructs a UiObject to represent a specific UI element matched by the specified 47 * {@link UiSelector} selector properties. 48 * 49 * @param selector 50 */ 51 public UiObject(UiSelector selector) { 52 mUiAutomationBridge = UiDevice.getInstance().getAutomatorBridge(); 53 mSelector = selector; 54 } 55 56 /** 57 * Debugging helper. A test can dump the properties of a selector as a string 58 * to its logs if needed. <code>getSelector().toString();</code> 59 * 60 * @return {@link UiSelector} 61 */ 62 public final UiSelector getSelector() { 63 return new UiSelector(mSelector); 64 } 65 66 /** 67 * Retrieves the {@link QueryController} to translate a {@link UiSelector} selector 68 * into an {@link AccessibilityNodeInfo}. 69 * 70 * @return {@link QueryController} 71 */ 72 QueryController getQueryController() { 73 return mUiAutomationBridge.getQueryController(); 74 } 75 76 /** 77 * Retrieves the {@link InteractionController} to perform finger actions such as tapping, 78 * swiping or entering text. 79 * 80 * @return {@link InteractionController} 81 */ 82 InteractionController getInteractionController() { 83 return mUiAutomationBridge.getInteractionController(); 84 } 85 86 /** 87 * Creates a new UiObject representing a child UI element of the element currently represented 88 * by this UiObject. 89 * 90 * @param selector for UI element to match 91 * @return a new UiObject representing the matched UI element 92 */ 93 public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException { 94 return new UiObject(getSelector().childSelector(selector)); 95 } 96 97 /** 98 * Creates a new UiObject representing a child UI element from the parent element currently 99 * represented by this object. Essentially this is starting the search from the parent 100 * element and can also be used to find sibling UI elements to the one currently represented 101 * by this UiObject. 102 * 103 * @param selector for the UI element to match 104 * @return a new UiObject representing the matched UI element 105 * @throws UiObjectNotFoundException 106 */ 107 public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException { 108 return new UiObject(getSelector().fromParent(selector)); 109 } 110 111 /** 112 * Counts the child UI elements immediately under the UI element currently represented by 113 * this UiObject. 114 * 115 * @return the count of child UI elements. 116 * @throws UiObjectNotFoundException 117 */ 118 public int getChildCount() throws UiObjectNotFoundException { 119 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 120 if(node == null) { 121 throw new UiObjectNotFoundException(getSelector().toString()); 122 } 123 return node.getChildCount(); 124 } 125 126 /** 127 * Uses the member UiSelector properties to find a matching UI element reported in 128 * the accessibility hierarchy. 129 * 130 * @param selector {@link UiSelector} 131 * @param timeout in milliseconds 132 * @return AccessibilityNodeInfo if found else null 133 */ 134 protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) { 135 AccessibilityNodeInfo node = null; 136 if(UiDevice.getInstance().isInWatcherContext()) { 137 // we will NOT run watchers or do any sort of polling if the 138 // reason we're here is because of a watcher is executing. Watchers 139 // will not have other watchers run for them so they should not block 140 // while they poll for items to become present. We disable polling for them. 141 node = getQueryController().findAccessibilityNodeInfo(getSelector()); 142 } else { 143 long startMills = SystemClock.uptimeMillis(); 144 long currentMills = 0; 145 while (currentMills <= timeout) { 146 node = getQueryController().findAccessibilityNodeInfo(getSelector()); 147 if (node != null) { 148 break; 149 } else { 150 UiDevice.getInstance().runWatchers(); 151 } 152 currentMills = SystemClock.uptimeMillis() - startMills; 153 if(timeout > 0) { 154 SystemClock.sleep(WAIT_FOR_SELECTOR_POLL); 155 } 156 } 157 } 158 return node; 159 } 160 161 /** 162 * Perform the action on the UI element that is represented by this UiObject. Also see 163 * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()}, 164 * {@link #scrollForward()}. 165 * 166 * @param steps indicates the number of injected move steps into the system. Steps are 167 * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete. 168 * @return true of successful 169 * @throws UiObjectNotFoundException 170 */ 171 public boolean swipeUp(int steps) throws UiObjectNotFoundException { 172 Rect rect = getVisibleBounds(); 173 if(rect.height() <= SWIPE_MARGIN_LIMIT * 2) 174 return false; // too small to swipe 175 return getInteractionController().swipe(rect.centerX(), 176 rect.bottom - SWIPE_MARGIN_LIMIT, rect.centerX(), rect.top + SWIPE_MARGIN_LIMIT, 177 steps); 178 } 179 180 /** 181 * Perform the action on the UI element that is represented by this object, Also see 182 * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()}, 183 * {@link #scrollForward()}. This method will perform the swipe gesture over any 184 * surface. The targeted UI element does not need to have the attribute 185 * <code>scrollable</code> set to <code>true</code> for this operation to be performed. 186 * 187 * @param steps indicates the number of injected move steps into the system. Steps are 188 * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete. 189 * @return true if successful 190 * @throws UiObjectNotFoundException 191 */ 192 public boolean swipeDown(int steps) throws UiObjectNotFoundException { 193 Rect rect = getVisibleBounds(); 194 if(rect.height() <= SWIPE_MARGIN_LIMIT * 2) 195 return false; // too small to swipe 196 return getInteractionController().swipe(rect.centerX(), 197 rect.top + SWIPE_MARGIN_LIMIT, rect.centerX(), 198 rect.bottom - SWIPE_MARGIN_LIMIT, steps); 199 } 200 201 /** 202 * Perform the action on the UI element that is represented by this object. Also see 203 * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()}, 204 * {@link #scrollForward()}. This method will perform the swipe gesture over any 205 * surface. The targeted UI element does not need to have the attribute 206 * <code>scrollable</code> set to <code>true</code> for this operation to be performed. 207 * 208 * @param steps indicates the number of injected move steps into the system. Steps are 209 * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete. 210 * @return true if successful 211 * @throws UiObjectNotFoundException 212 */ 213 public boolean swipeLeft(int steps) throws UiObjectNotFoundException { 214 Rect rect = getVisibleBounds(); 215 if(rect.width() <= SWIPE_MARGIN_LIMIT * 2) 216 return false; // too small to swipe 217 return getInteractionController().swipe(rect.right - SWIPE_MARGIN_LIMIT, 218 rect.centerY(), rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), steps); 219 } 220 221 /** 222 * Perform the action on the UI element that is represented by this object. Also see 223 * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()}, 224 * {@link #scrollForward()}. This method will perform the swipe gesture over any 225 * surface. The targeted UI element does not need to have the attribute 226 * <code>scrollable</code> set to <code>true</code> for this operation to be performed. 227 * 228 * @param steps indicates the number of injected move steps into the system. Steps are 229 * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete. 230 * @return true if successful 231 * @throws UiObjectNotFoundException 232 */ 233 public boolean swipeRight(int steps) throws UiObjectNotFoundException { 234 Rect rect = getVisibleBounds(); 235 if(rect.width() <= SWIPE_MARGIN_LIMIT * 2) 236 return false; // too small to swipe 237 return getInteractionController().swipe(rect.left + SWIPE_MARGIN_LIMIT, 238 rect.centerY(), rect.right - SWIPE_MARGIN_LIMIT, rect.centerY(), steps); 239 } 240 241 /** 242 * Finds the visible bounds of a partially visible UI element 243 * 244 * @param node 245 * @return null if node is null, else a Rect containing visible bounds 246 */ 247 private Rect getVisibleBounds(AccessibilityNodeInfo node) { 248 if (node == null) { 249 return null; 250 } 251 252 // targeted node's bounds 253 Rect nodeRect = AccessibilityNodeInfoHelper.getVisibleBoundsInScreen(node); 254 255 // is the targeted node within a scrollable container? 256 AccessibilityNodeInfo scrollableParentNode = getScrollableParent(node); 257 if(scrollableParentNode == null) { 258 // nothing to adjust for so return the node's Rect as is 259 return nodeRect; 260 } 261 262 // Scrollable parent's visible bounds 263 Rect parentRect = AccessibilityNodeInfoHelper 264 .getVisibleBoundsInScreen(scrollableParentNode); 265 // adjust for partial clipping of targeted by parent node if required 266 nodeRect.intersect(parentRect); 267 return nodeRect; 268 } 269 270 /** 271 * Walk the hierarchy up to find a scrollable parent. A scrollable parent 272 * indicates that this node may be in a content where it is partially 273 * visible due to scrolling. its clickable center maybe invisible and 274 * adjustments should be made to the click coordinates. 275 * 276 * @param node 277 * @return 278 */ 279 private AccessibilityNodeInfo getScrollableParent(AccessibilityNodeInfo node) { 280 AccessibilityNodeInfo parent = node; 281 while(parent != null) { 282 parent = parent.getParent(); 283 if (parent != null && parent.isScrollable()) { 284 return parent; 285 } 286 } 287 return null; 288 } 289 290 /** 291 * Performs a click at the center of the visible bounds of the UI element represented 292 * by this UiObject. 293 * 294 * @return true id successful else false 295 * @throws UiObjectNotFoundException 296 */ 297 public boolean click() throws UiObjectNotFoundException { 298 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 299 if(node == null) { 300 throw new UiObjectNotFoundException(getSelector().toString()); 301 } 302 Rect rect = getVisibleBounds(node); 303 return getInteractionController().clickAndWaitForEvent(rect.centerX(), rect.centerY(), 304 AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, WAIT_FOR_EVENT_TMEOUT); 305 } 306 307 /** 308 * See {@link #clickAndWaitForNewWindow(long)} 309 * This method is intended to reliably wait for window transitions that would typically take 310 * longer than the usual default timeouts. 311 * 312 * @return true if the event was triggered, else false 313 * @throws UiObjectNotFoundException 314 */ 315 public boolean clickAndWaitForNewWindow() throws UiObjectNotFoundException { 316 return clickAndWaitForNewWindow(WAIT_FOR_WINDOW_TMEOUT); 317 } 318 319 /** 320 * Performs a click at the center of the visible bounds of the UI element represented 321 * by this UiObject and waits for window transitions. 322 * 323 * This method differ from {@link UiObject#click()} only in that this method waits for a 324 * a new window transition as a result of the click. Some examples of a window transition: 325 * <li>launching a new activity</li> 326 * <li>bringing up a pop-up menu</li> 327 * <li>bringing up a dialog</li> 328 * 329 * @param timeout timeout before giving up on waiting for a new window 330 * @return true if the event was triggered, else false 331 * @throws UiObjectNotFoundException 332 */ 333 public boolean clickAndWaitForNewWindow(long timeout) throws UiObjectNotFoundException { 334 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 335 if(node == null) { 336 throw new UiObjectNotFoundException(getSelector().toString()); 337 } 338 Rect rect = getVisibleBounds(node); 339 return getInteractionController().clickAndWaitForNewWindow( 340 rect.centerX(), rect.centerY(), timeout); 341 } 342 343 /** 344 * Clicks the top and left corner of the UI element 345 * 346 * @return true on success 347 * @throws Exception 348 */ 349 public boolean clickTopLeft() throws UiObjectNotFoundException { 350 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 351 if(node == null) { 352 throw new UiObjectNotFoundException(getSelector().toString()); 353 } 354 Rect rect = getVisibleBounds(node); 355 return getInteractionController().click(rect.left + 5, rect.top + 5); 356 } 357 358 /** 359 * Long clicks bottom and right corner of the UI element 360 * 361 * @return true if operation was successful 362 * @throws UiObjectNotFoundException 363 */ 364 public boolean longClickBottomRight() throws UiObjectNotFoundException { 365 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 366 if(node == null) { 367 throw new UiObjectNotFoundException(getSelector().toString()); 368 } 369 Rect rect = getVisibleBounds(node); 370 return getInteractionController().longTap(rect.right - 5, rect.bottom - 5); 371 } 372 373 /** 374 * Clicks the bottom and right corner of the UI element 375 * 376 * @return true on success 377 * @throws Exception 378 */ 379 public boolean clickBottomRight() throws UiObjectNotFoundException { 380 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 381 if(node == null) { 382 throw new UiObjectNotFoundException(getSelector().toString()); 383 } 384 Rect rect = getVisibleBounds(node); 385 return getInteractionController().click(rect.right - 5, rect.bottom - 5); 386 } 387 388 /** 389 * Long clicks the center of the visible bounds of the UI element 390 * 391 * @return true if operation was successful 392 * @throws UiObjectNotFoundException 393 */ 394 public boolean longClick() throws UiObjectNotFoundException { 395 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 396 if(node == null) { 397 throw new UiObjectNotFoundException(getSelector().toString()); 398 } 399 Rect rect = getVisibleBounds(node); 400 return getInteractionController().longTap(rect.centerX(), rect.centerY()); 401 } 402 403 /** 404 * Long clicks on the top and left corner of the UI element 405 * 406 * @return true if operation was successful 407 * @throws UiObjectNotFoundException 408 */ 409 public boolean longClickTopLeft() throws UiObjectNotFoundException { 410 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 411 if(node == null) { 412 throw new UiObjectNotFoundException(getSelector().toString()); 413 } 414 Rect rect = getVisibleBounds(node); 415 return getInteractionController().longTap(rect.left + 5, rect.top + 5); 416 } 417 418 /** 419 * Reads the <code>text</code> property of the UI element 420 * 421 * @return text value of the current node represented by this UiObject 422 * @throws UiObjectNotFoundException if no match could be found 423 */ 424 public String getText() throws UiObjectNotFoundException { 425 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 426 if(node == null) { 427 throw new UiObjectNotFoundException(getSelector().toString()); 428 } 429 String retVal = safeStringReturn(node.getText()); 430 Log.d(LOG_TAG, String.format("getText() = %s", retVal)); 431 return retVal; 432 } 433 434 /** 435 * Reads the <code>content_desc</code> property of the UI element 436 * 437 * @return value of node attribute "content_desc" 438 * @throws UiObjectNotFoundException 439 */ 440 public String getContentDescription() throws UiObjectNotFoundException { 441 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 442 if(node == null) { 443 throw new UiObjectNotFoundException(getSelector().toString()); 444 } 445 return safeStringReturn(node.getContentDescription()); 446 } 447 448 /** 449 * Sets the text in an editable field, after clearing the field's content. 450 * 451 * The {@link UiSelector} selector of this object must reference a UI element that is editable. 452 * 453 * When you call this method, the method first simulates a {@link #click()} on 454 * editable field to set focus. The method then clears the field's contents 455 * and injects your specified text into the field. 456 * 457 * If you want to capture the original contents of the field, call {@link #getText()} first. 458 * You can then modify the text and use this method to update the field. 459 * 460 * @param text string to set 461 * @return true if operation is successful 462 * @throws UiObjectNotFoundException 463 */ 464 public boolean setText(String text) throws UiObjectNotFoundException { 465 clearTextField(); 466 return getInteractionController().sendText(text); 467 } 468 469 /** 470 * Clears the existing text contents in an editable field. 471 * 472 * The {@link UiSelector} of this object must reference a UI element that is editable. 473 * 474 * When you call this method, the method first sets focus at the start edge of the field. 475 * The method then simulates a long-press to select the existing text, and deletes the 476 * selected text. 477 * 478 * If a "Select-All" option is displayed, the method will automatically attempt to use it 479 * to ensure full text selection. 480 * 481 * Note that it is possible that not all the text in the field is selected; for example, 482 * if the text contains separators such as spaces, slashes, at symbol etc. 483 * Also, not all editable fields support the long-press functionality. 484 * 485 * @throws UiObjectNotFoundException 486 */ 487 public void clearTextField() throws UiObjectNotFoundException { 488 // long click left + center 489 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 490 if(node == null) { 491 throw new UiObjectNotFoundException(getSelector().toString()); 492 } 493 Rect rect = getVisibleBounds(node); 494 getInteractionController().longTap(rect.left + 20, rect.centerY()); 495 // check if the edit menu is open 496 UiObject selectAll = new UiObject(new UiSelector().descriptionContains("Select all")); 497 if(selectAll.waitForExists(50)) 498 selectAll.click(); 499 // wait for the selection 500 SystemClock.sleep(250); 501 // delete it 502 getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0); 503 } 504 505 /** 506 * Check if the UI element's <code>checked</code> property is currently true 507 * 508 * @return true if it is else false 509 */ 510 public boolean isChecked() throws UiObjectNotFoundException { 511 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 512 if(node == null) { 513 throw new UiObjectNotFoundException(getSelector().toString()); 514 } 515 return node.isChecked(); 516 } 517 518 /** 519 * Check if the UI element's <code>selected</code> property is currently true 520 * 521 * @return true if it is else false 522 * @throws UiObjectNotFoundException 523 */ 524 public boolean isSelected() throws UiObjectNotFoundException { 525 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 526 if(node == null) { 527 throw new UiObjectNotFoundException(getSelector().toString()); 528 } 529 return node.isSelected(); 530 } 531 532 /** 533 * Check if the UI element's <code>checkable</code> property is currently true 534 * 535 * @return true if it is else false 536 * @throws UiObjectNotFoundException 537 */ 538 public boolean isCheckable() throws UiObjectNotFoundException { 539 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 540 if(node == null) { 541 throw new UiObjectNotFoundException(getSelector().toString()); 542 } 543 return node.isCheckable(); 544 } 545 546 /** 547 * Check if the UI element's <code>enabled</code> property is currently true 548 * 549 * @return true if it is else false 550 * @throws UiObjectNotFoundException 551 */ 552 public boolean isEnabled() throws UiObjectNotFoundException { 553 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 554 if(node == null) { 555 throw new UiObjectNotFoundException(getSelector().toString()); 556 } 557 return node.isEnabled(); 558 } 559 560 /** 561 * Check if the UI element's <code>clickable</code> property is currently true 562 * 563 * @return true if it is else false 564 * @throws UiObjectNotFoundException 565 */ 566 public boolean isClickable() throws UiObjectNotFoundException { 567 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 568 if(node == null) { 569 throw new UiObjectNotFoundException(getSelector().toString()); 570 } 571 return node.isClickable(); 572 } 573 574 /** 575 * Check if the UI element's <code>focused</code> property is currently true 576 * 577 * @return true if it is else false 578 * @throws UiObjectNotFoundException 579 */ 580 public boolean isFocused() throws UiObjectNotFoundException { 581 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 582 if(node == null) { 583 throw new UiObjectNotFoundException(getSelector().toString()); 584 } 585 return node.isFocused(); 586 } 587 588 /** 589 * Check if the UI element's <code>focusable</code> property is currently true 590 * 591 * @return true if it is else false 592 * @throws UiObjectNotFoundException 593 */ 594 public boolean isFocusable() throws UiObjectNotFoundException { 595 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 596 if(node == null) { 597 throw new UiObjectNotFoundException(getSelector().toString()); 598 } 599 return node.isFocusable(); 600 } 601 602 /** 603 * Check if the UI element's <code>scrollable</code> property is currently true 604 * 605 * @return true if it is else false 606 * @throws UiObjectNotFoundException 607 */ 608 public boolean isScrollable() throws UiObjectNotFoundException { 609 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 610 if(node == null) { 611 throw new UiObjectNotFoundException(getSelector().toString()); 612 } 613 return node.isScrollable(); 614 } 615 616 /** 617 * Check if the UI element's <code>long-clickable</code> property is currently true 618 * 619 * @return true if it is else false 620 * @throws UiObjectNotFoundException 621 */ 622 public boolean isLongClickable() throws UiObjectNotFoundException { 623 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 624 if(node == null) { 625 throw new UiObjectNotFoundException(getSelector().toString()); 626 } 627 return node.isLongClickable(); 628 } 629 630 /** 631 * Reads the UI element's <code>package</code> property 632 * 633 * @return true if it is else false 634 * @throws UiObjectNotFoundException 635 */ 636 public String getPackageName() throws UiObjectNotFoundException { 637 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 638 if(node == null) { 639 throw new UiObjectNotFoundException(getSelector().toString()); 640 } 641 return safeStringReturn(node.getPackageName()); 642 } 643 644 /** 645 * Returns the visible bounds of the UI element. 646 * 647 * If a portion of the UI element is visible, only the bounds of the visible portion are 648 * reported. 649 * 650 * @return Rect 651 * @throws UiObjectNotFoundException 652 * @see {@link #getBound()} 653 */ 654 public Rect getVisibleBounds() throws UiObjectNotFoundException { 655 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 656 if(node == null) { 657 throw new UiObjectNotFoundException(getSelector().toString()); 658 } 659 return getVisibleBounds(node); 660 } 661 662 /** 663 * Returns the UI element's <code>bounds</code> property. See {@link #getVisibleBounds()} 664 * 665 * @return Rect 666 * @throws UiObjectNotFoundException 667 */ 668 public Rect getBounds() throws UiObjectNotFoundException { 669 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 670 if(node == null) { 671 throw new UiObjectNotFoundException(getSelector().toString()); 672 } 673 Rect nodeRect = new Rect(); 674 node.getBoundsInScreen(nodeRect); 675 676 return nodeRect; 677 } 678 679 /** 680 * Waits a specified length of time for a UI element to become visible. 681 * 682 * This method waits until the UI element becomes visible on the display, or 683 * until the timeout has elapsed. You can use this method in situations where 684 * the content that you want to select is not immediately displayed. 685 * 686 * @param timeout the amount of time to wait (in milliseconds) 687 * @return true if the UI element is displayed, else false if timeout elapsed while waiting 688 */ 689 public boolean waitForExists(long timeout) { 690 if(findAccessibilityNodeInfo(timeout) != null) { 691 return true; 692 } 693 return false; 694 } 695 696 /** 697 * Waits a specified length of time for a UI element to become undetectable. 698 * 699 * This method waits until a UI element is no longer matchable, or until the 700 * timeout has elapsed. 701 * 702 * A UI element becomes undetectable when the {@link UiSelector} of the object is 703 * unable to find a match because the element has either changed its state or is no 704 * longer displayed. 705 * 706 * You can use this method when attempting to wait for some long operation 707 * to compete, such as downloading a large file or connecting to a remote server. 708 * 709 * @param timeout time to wait (in milliseconds) 710 * @return true if the element is gone before timeout elapsed, else false if timeout elapsed 711 * but a matching element is still found. 712 */ 713 public boolean waitUntilGone(long timeout) { 714 long startMills = SystemClock.uptimeMillis(); 715 long currentMills = 0; 716 while (currentMills <= timeout) { 717 if(findAccessibilityNodeInfo(0) == null) 718 return true; 719 currentMills = SystemClock.uptimeMillis() - startMills; 720 if(timeout > 0) 721 SystemClock.sleep(WAIT_FOR_SELECTOR_POLL); 722 } 723 return false; 724 } 725 726 /** 727 * Check if UI element exists. 728 * 729 * This methods performs a {@link #waitForExists(long)} with zero timeout. This 730 * basically returns immediately whether the UI element represented by this UiObject 731 * exists or not. If you need to wait longer for this UI element, then see 732 * {@link #waitForExists(long)}. 733 * 734 * @return true if the UI element represented by this UiObject does exist 735 */ 736 public boolean exists() { 737 return waitForExists(0); 738 } 739 740 private String safeStringReturn(CharSequence cs) { 741 if(cs == null) 742 return ""; 743 return cs.toString(); 744 } 745} 746