UiObject.java revision 4ab790eccf6d5c27f542056b87d26d38f7caeeb3
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.AccessibilityNodeInfo; 24 25/** 26 * UiObject is designed to be a representation of displayed UI element. It is not in any way 27 * directly bound to a specific UI element. It holds information to find any displayed UI element 28 * that matches its selectors. This means it can be reused on any screen where a UI element 29 * exists to match its selector criteria. This help tests define a single UiObject say for 30 * an "OK" or tool-bar button and use it across many activities. 31 */ 32public class UiObject { 33 private static final String LOG_TAG = UiObject.class.getSimpleName(); 34 protected static final long WAIT_FOR_SELECTOR_TIMEOUT = 10 * 1000; 35 protected static final long WAIT_FOR_SELECTOR_POLL = 1000; 36 // set a default timeout to 5.5s, since ANR threshold is 5s 37 protected static final long WAIT_FOR_WINDOW_TMEOUT = 5500; 38 protected static final int SWIPE_MARGIN_LIMIT = 5; 39 40 protected UiSelector mSelector; 41 protected final UiDevice mDevice; 42 protected final UiAutomatorBridge mUiAutomationBridge; 43 44 /** 45 * Constructs a UiObject that references any UI element that may match the specified 46 * {@link UiSelector} selector. UiObject can be pre-constructed and reused across an 47 * application where applicable. A good example is a tool bar and its buttons. 48 * A toolbar may remain visible on the various views the application is displaying but 49 * may have different contextual buttons displayed for each. The same UiObject that 50 * describes the toolbar can be reused.<p/> 51 * It is a good idea in certain cases before using any operations of this UiObject is to 52 * call {@link #exists()} to verify if the UI element matching this object is actually 53 * visible on the screen. This way the test can gracefully handle this situation else 54 * a {@link UiObjectNotFoundException} will be thrown when using this object. 55 * @param selector 56 */ 57 public UiObject(UiSelector selector) { 58 mUiAutomationBridge = UiDevice.getInstance().getAutomatorBridge(); 59 mDevice = UiDevice.getInstance(); 60 mSelector = selector; 61 } 62 63 /** 64 * Helper for debugging. During testing a test can dump the selector of the object into 65 * its logs of needed. <code>getSelector().toString();</code> 66 * @return {@link UiSelector} 67 */ 68 public final UiSelector getSelector() { 69 return new UiSelector(mSelector); 70 } 71 72 /** 73 * Used in test operations to retrieve the {@link QueryController} to translate 74 * a {@link UiSelector} selector into an {@link AccessibilityNodeInfo}. 75 * @return {@link QueryController} 76 */ 77 protected QueryController getQueryController() { 78 return mUiAutomationBridge.getQueryController(); 79 } 80 81 /** 82 * Used in test operations to retrieve the {@link InteractionController} to perform 83 * finger actions such as tapping, swiping or entering text. 84 * @return {@link InteractionController} 85 */ 86 protected InteractionController getInteractionController() { 87 return mUiAutomationBridge.getInteractionController(); 88 } 89 90 /** 91 * Creates a new UiObject that points at a child UI element of the currently pointed 92 * to element by this object. UI element are considered layout elements as well as UI 93 * widgets. A layout element could have child widgets like buttons and text labels. 94 * @param selector 95 * @return a new UiObject with a new selector. It doesn't test if the object exists. 96 */ 97 public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException { 98 return new UiObject(getSelector().childSelector(selector)); 99 } 100 101 /** 102 * Creates a new UiObject that points at a child UI element of the parent of this object. 103 * Essentially this is starting the search from any one of the siblings UI element of this 104 * element. 105 * @param selector 106 * @return 107 * @throws UiObjectNotFoundException 108 */ 109 public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException { 110 return new UiObject(getSelector().fromParent(selector)); 111 } 112 113 /** 114 * Counts the child UI elements immediately under the UI element currently referenced by 115 * this UiObject. 116 * @return the count of child UI elements. 117 * @throws UiObjectNotFoundException 118 */ 119 public int getChildCount() throws UiObjectNotFoundException { 120 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 121 if(node == null) { 122 throw new UiObjectNotFoundException(getSelector().toString()); 123 } 124 return node.getChildCount(); 125 } 126 127 /** 128 * Helper method to allow for a specific content to become present on the 129 * screen before moving on to do other operations. 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 object. Also see 163 * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()}, 164 * {@link #scrollForward()}. This method will perform the swipe gesture over any 165 * surface. The targeted UI element does not need to have the attribute 166 * <code>scrollable</code> set to <code>true</code> for this operation to be performed. 167 * @param steps indicates the number of injected move steps into the system. More steps 168 * injected the smoother the motion and slower. 169 * @return 170 * @throws UiObjectNotFoundException 171 */ 172 public boolean swipeUp(int steps) throws UiObjectNotFoundException { 173 Rect rect = getBounds(); 174 if(rect.height() <= SWIPE_MARGIN_LIMIT * 2) 175 return false; // too small to swipe 176 return getInteractionController().swipe(rect.centerX(), 177 rect.bottom - SWIPE_MARGIN_LIMIT, rect.centerX(), rect.top + SWIPE_MARGIN_LIMIT, 178 steps); 179 } 180 181 /** 182 * Perform the action on the UI element that is represented by this object, Also see 183 * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()}, 184 * {@link #scrollForward()}. This method will perform the swipe gesture over any 185 * surface. The targeted UI element does not need to have the attribute 186 * <code>scrollable</code> set to <code>true</code> for this operation to be performed. 187 * @param steps indicates the number of injected move steps into the system. More steps 188 * injected the smoother the motion and slower. 189 * @return 190 * @throws UiObjectNotFoundException 191 */ 192 public boolean swipeDown(int steps) throws UiObjectNotFoundException { 193 Rect rect = getBounds(); 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 * @param steps indicates the number of injected move steps into the system. More steps 208 * injected the smoother the motion and slower. 209 * @return 210 * @throws UiObjectNotFoundException 211 */ 212 public boolean swipeLeft(int steps) throws UiObjectNotFoundException { 213 Rect rect = getBounds(); 214 if(rect.width() <= SWIPE_MARGIN_LIMIT * 2) 215 return false; // too small to swipe 216 return getInteractionController().swipe(rect.right - SWIPE_MARGIN_LIMIT, 217 rect.centerY(), rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), steps); 218 } 219 220 /** 221 * Perform the action on the UI element that is represented by this object. Also see 222 * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()}, 223 * {@link #scrollForward()}. This method will perform the swipe gesture over any 224 * surface. The targeted UI element does not need to have the attribute 225 * <code>scrollable</code> set to <code>true</code> for this operation to be performed. 226 * @param steps indicates the number of injected move steps into the system. More steps 227 * injected the smoother the motion and slower. 228 * @return 229 * @throws UiObjectNotFoundException 230 */ 231 public boolean swipeRight(int steps) throws UiObjectNotFoundException { 232 Rect rect = getBounds(); 233 if(rect.width() <= SWIPE_MARGIN_LIMIT * 2) 234 return false; // too small to swipe 235 return getInteractionController().swipe(rect.left + SWIPE_MARGIN_LIMIT, 236 rect.centerY(), rect.right - SWIPE_MARGIN_LIMIT, rect.centerY(), steps); 237 } 238 239 /** 240 * In rare situations, the node hierarchy returned from accessibility will 241 * return items that are slightly OFF the screen (list view contents). This method 242 * validate that the item is visible to avoid click operation failures. It will adjust 243 * the center of the click as much as possible to be within visible bounds to make 244 * the click successful. 245 * @param node 246 * @return the same AccessibilityNodeInfo passed in as argument 247 */ 248 private Rect getVisibleBounds(AccessibilityNodeInfo node) { 249 if (node == null) { 250 return null; 251 } 252 253 // targeted node's bounds 254 Rect nodeRect = new Rect(); 255 node.getBoundsInScreen(nodeRect); 256 257 // is the targeted node within a scrollable container? 258 AccessibilityNodeInfo scrollableParentNode = getScrollableParent(node); 259 if(scrollableParentNode == null) { 260 // nothing to adjust for so return the node's Rect as is 261 return nodeRect; 262 } 263 264 // Scrollable parent's visible bounds 265 Rect parentRect = new Rect(); 266 scrollableParentNode.getBoundsInScreen(parentRect); 267 // adjust for partial clipping of targeted by parent node if required 268 nodeRect.intersect(parentRect); 269 return nodeRect; 270 } 271 272 /** 273 * Walk the hierarchy up to find a scrollable parent. A scrollable parent indicates that 274 * this node may be in a content where it is partially visible due to scrolling. its 275 * clickable center maybe invisible and adjustments should be made to the click coordinates. 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 over the UI element this object represents. </p> 292 * Take note that the UI element directly represented by this UiObject may not have 293 * its attribute <code>clickable</code> set to <code>true</code> and yet still perform 294 * the click successfully. This is because all clicks are performed in the center of the 295 * targeted UI element and if this element is a child or a parent that wraps the clickable 296 * element the operation will still succeed. This is the reason this operation does not 297 * not validate the targeted UI element is clickable or not before operating. 298 * @return true id successful else false 299 * @throws UiObjectNotFoundException 300 */ 301 public boolean click() throws UiObjectNotFoundException { 302 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 303 if(node == null) { 304 throw new UiObjectNotFoundException(getSelector().toString()); 305 } 306 Rect rect = getVisibleBounds(node); 307 return getInteractionController().click(rect.centerX(), rect.centerY()); 308 } 309 310 /** 311 * 312 * Performs a click over the represented UI element, and expect window transition as 313 * a result.</p> 314 * 315 * This method is similar to the plain {@link UiObject#click()}, with an important difference 316 * that the method goes further to expect a window transition as a result of the tap. Some 317 * examples of a window transition: 318 * <li>launching a new activity</li> 319 * <li>bringing up a pop-up menu</li> 320 * <li>bringing up a dialog</li> 321 * This method is intended for reliably handling window transitions that would typically lasts 322 * longer than the usual preset timeouts. 323 * 324 * @return true if the event was triggered, else false 325 * @throws UiObjectNotFoundException 326 */ 327 public boolean clickAndWaitForNewWindow() throws UiObjectNotFoundException { 328 return clickAndWaitForNewWindow(WAIT_FOR_WINDOW_TMEOUT); 329 } 330 331 /** 332 * 333 * Performs a click over the represented UI element, and expect window transition as 334 * a result.</p> 335 * 336 * This method is similar to the plain {@link UiObject#click()}, with an important difference 337 * that the method goes further to expect a window transition as a result of the tap. Some 338 * examples of a window transition: 339 * <li>launching a new activity</li> 340 * <li>bringing up a pop-up menu</li> 341 * <li>bringing up a dialog</li> 342 * This method is intended for reliably handling window transitions that would typically lasts 343 * longer than the usual preset timeouts. 344 * 345 * @param timeout timeout before giving up on waiting for new window 346 * @return true if the event was triggered, else false 347 * @throws UiObjectNotFoundException 348 */ 349 public boolean clickAndWaitForNewWindow(long timeout) 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().clickAndWaitForNewWindow( 356 rect.centerX(), rect.centerY(), timeout); 357 } 358 359 /** 360 * Clicks the top and left corner of the UI element. 361 * @return true on success 362 * @throws Exception 363 */ 364 public boolean clickTopLeft() 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().click(rect.left + 5, rect.top + 5); 371 } 372 373 /** 374 * Long clicks a UI element pointed to by the {@link UiSelector} selector of this object at the 375 * bottom right corner. The duration of what will be considered as a long click is dynamically 376 * retrieved from the system and used in the operation. 377 * @return true if operation was successful 378 * @throws UiObjectNotFoundException 379 */ 380 public boolean longClickBottomRight() throws UiObjectNotFoundException { 381 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 382 if(node == null) { 383 throw new UiObjectNotFoundException(getSelector().toString()); 384 } 385 Rect rect = getVisibleBounds(node); 386 return getInteractionController().longTap(rect.right - 5, rect.bottom - 5); 387 } 388 389 /** 390 * Clicks the bottom and right corner of the UI element. 391 * @return true on success 392 * @throws Exception 393 */ 394 public boolean clickBottomRight() 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().click(rect.right - 5, rect.bottom - 5); 401 } 402 403 /** 404 * Long clicks a UI element pointed to by the {@link UiSelector} selector of this object. The 405 * duration of what will be considered as a long click is dynamically retrieved from the 406 * system and used in the operation. 407 * @return true if operation was successful 408 * @throws UiObjectNotFoundException 409 */ 410 public boolean longClick() throws UiObjectNotFoundException { 411 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 412 if(node == null) { 413 throw new UiObjectNotFoundException(getSelector().toString()); 414 } 415 Rect rect = getVisibleBounds(node); 416 return getInteractionController().longTap(rect.centerX(), rect.centerY()); 417 } 418 419 /** 420 * Long clicks a UI element pointed to by the {@link UiSelector} selector of this object at the 421 * top left corner. The duration of what will be considered as a long click is dynamically 422 * retrieved from the system and used in the operation. 423 * @return true if operation was successful 424 * @throws UiObjectNotFoundException 425 */ 426 public boolean longClickTopLeft() throws UiObjectNotFoundException { 427 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 428 if(node == null) { 429 throw new UiObjectNotFoundException(getSelector().toString()); 430 } 431 Rect rect = getVisibleBounds(node); 432 return getInteractionController().longTap(rect.left + 5, rect.top + 5); 433 } 434 435 /** 436 * This function can be used to return the UI element's displayed text. This applies to 437 * UI element that are displaying labels or edit fields. 438 * @return text value of the current node represented by this UiObject 439 * @throws UiObjectNotFoundException if no match could be found 440 */ 441 public String getText() throws UiObjectNotFoundException { 442 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 443 if(node == null) { 444 throw new UiObjectNotFoundException(getSelector().toString()); 445 } 446 String retVal = safeStringReturn(node.getText()); 447 Log.d(LOG_TAG, String.format("getText() = %s", retVal)); 448 return retVal; 449 } 450 451 /** 452 * Retrieves the content-description value set for the UI element. In Accessibility, the 453 * spoken text to speech is usually the <code>text</code> property of the UI element. If that 454 * is not present, then the content-description is spoken. Many UI element such as buttons on 455 * a toolbar may be too small to incorporate a visible text on their surfaces, so in such 456 * cases, these UI elements must have their content-description fields populated to describe 457 * them when accessibility is active. 458 * @return value of node attribute "content_desc" 459 * @throws UiObjectNotFoundException 460 */ 461 public String getContentDescription() throws UiObjectNotFoundException { 462 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 463 if(node == null) { 464 throw new UiObjectNotFoundException(getSelector().toString()); 465 } 466 return safeStringReturn(node.getContentDescription()); 467 } 468 469 /** 470 * First this function clears the existing text from the field. If this is not the intended 471 * behavior, do a {@link #getText()} first, modify the text and then use this function. 472 * The {@link UiSelector} selector of this object MUST be pointing directly at a UI element 473 * that can accept edits. The way this method works is by first performing a {@link #click()} 474 * on the edit field to set focus then it begins injecting the content of the text param 475 * into the system. Since the targeted field is in focus, the text contents should be 476 * inserted into the field.<p/> 477 * @param text 478 * @return true if operation is successful 479 * @throws UiObjectNotFoundException 480 */ 481 public boolean setText(String text) throws UiObjectNotFoundException { 482 clearTextField(); 483 return getInteractionController().sendText(text); 484 } 485 486 /** 487 * The object targeted must be an edit field capable of performing text insert. This 488 * method sets focus at the left edge of the field and long presses to select 489 * existing text. It will then follow that with delete press. Note: It is possible 490 * that not all the text is selected especially if the text contained separators 491 * such as spaces, slashes, at signs etc... The function will attempt to use the 492 * Select-All option if one is displayed to ensure full text selection. 493 * @throws UiObjectNotFoundException 494 */ 495 public void clearTextField() throws UiObjectNotFoundException { 496 // long click left + center 497 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 498 if(node == null) { 499 throw new UiObjectNotFoundException(getSelector().toString()); 500 } 501 Rect rect = getVisibleBounds(node); 502 getInteractionController().longTap(rect.left + 20, rect.centerY()); 503 // check if the edit menu is open 504 UiObject selectAll = new UiObject(new UiSelector().descriptionContains("Select all")); 505 if(selectAll.waitForExists(50)) 506 selectAll.click(); 507 // wait for the selection 508 SystemClock.sleep(250); 509 // delete it 510 getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0); 511 } 512 513 /** 514 * Check if item pointed to by this object's selector is currently checked. <p/> 515 * Take note that the {@link UiSelector} specified for this UiObjecy must be pointing 516 * directly at the element you wish to query for this attribute. 517 * @return true if it is else false 518 */ 519 public boolean isChecked() throws UiObjectNotFoundException { 520 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 521 if(node == null) { 522 throw new UiObjectNotFoundException(getSelector().toString()); 523 } 524 return node.isChecked(); 525 } 526 527 /** 528 * Check if item pointed to by this object's selector is currently selected.<p/> 529 * Take note that the {@link UiSelector} specified for this UiObjecy must be pointing 530 * directly at the element you wish to query for this attribute. 531 * @return true if it is else false 532 * @throws UiObjectNotFoundException 533 */ 534 public boolean isSelected() throws UiObjectNotFoundException { 535 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 536 if(node == null) { 537 throw new UiObjectNotFoundException(getSelector().toString()); 538 } 539 return node.isSelected(); 540 } 541 542 /** 543 * Check if item pointed to by this object's selector can be checked and unchecked. <p/> 544 * Take note that the {@link UiSelector} specified for this UiObjecy must be pointing 545 * directly at the element you wish to query for this attribute. 546 * @return true if it is else false 547 * @throws UiObjectNotFoundException 548 */ 549 public boolean isCheckable() throws UiObjectNotFoundException { 550 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 551 if(node == null) { 552 throw new UiObjectNotFoundException(getSelector().toString()); 553 } 554 return node.isCheckable(); 555 } 556 557 /** 558 * Check if item pointed to by this object's selector is currently not grayed out. <p/> 559 * Take note that the {@link UiSelector} specified for this UiObjecy must be pointing 560 * directly at the element you wish to query for this attribute. 561 * @return true if it is else false 562 * @throws UiObjectNotFoundException 563 */ 564 public boolean isEnabled() throws UiObjectNotFoundException { 565 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 566 if(node == null) { 567 throw new UiObjectNotFoundException(getSelector().toString()); 568 } 569 return node.isEnabled(); 570 } 571 572 /** 573 * Check if item pointed to by this object's selector responds to clicks <p/> 574 * Take note that the {@link UiSelector} specified for this UiObjecy must be pointing 575 * directly at the element you wish to query for this attribute. 576 * @return true if it is else false 577 * @throws UiObjectNotFoundException 578 */ 579 public boolean isClickable() throws UiObjectNotFoundException { 580 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 581 if(node == null) { 582 throw new UiObjectNotFoundException(getSelector().toString()); 583 } 584 return node.isClickable(); 585 } 586 587 /** 588 * Check if item pointed to by this object's selector is currently focused. 589 * Objects with focus will receive key stroke events when key events 590 * are fired 591 * @return true if it is else false 592 * @throws UiObjectNotFoundException 593 */ 594 public boolean isFocused() throws UiObjectNotFoundException { 595 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 596 if(node == null) { 597 throw new UiObjectNotFoundException(getSelector().toString()); 598 } 599 return node.isFocused(); 600 } 601 602 /** 603 * Check if item pointed to by this object's selector is capable of receiving focus. <p/> 604 * Take note that the {@link UiSelector} selector specified for this UiObjecy must be pointing 605 * directly at the element you wish to query for this attribute. 606 * @return true if it is else false 607 * @throws UiObjectNotFoundException 608 */ 609 public boolean isFocusable() throws UiObjectNotFoundException { 610 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 611 if(node == null) { 612 throw new UiObjectNotFoundException(getSelector().toString()); 613 } 614 return node.isFocusable(); 615 } 616 617 /** 618 * Check if item pointed to by this object's selector can be scrolled.<p/> 619 * Take note that the {@link UiSelector} selector specified for this UiObjecy must be pointing 620 * directly at the element you wish to query for this attribute. 621 * @return true if it is else false 622 * @throws UiObjectNotFoundException 623 */ 624 public boolean isScrollable() throws UiObjectNotFoundException { 625 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 626 if(node == null) { 627 throw new UiObjectNotFoundException(getSelector().toString()); 628 } 629 return node.isScrollable(); 630 } 631 632 /** 633 * Check if item pointed to by this object's selector responds to long clicks.<p/> 634 * Take note that the {@link UiSelector} selector specified for this UiObjecy must be pointing 635 * directly at the element you wish to query for this attribute. 636 * @return true if it is else false 637 * @throws UiObjectNotFoundException 638 */ 639 public boolean isLongClickable() throws UiObjectNotFoundException { 640 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 641 if(node == null) { 642 throw new UiObjectNotFoundException(getSelector().toString()); 643 } 644 return node.isLongClickable(); 645 } 646 647 /** 648 * This method retrieves the package name of the currently displayed content on the screen. 649 * This can be helpful when verifying that the expected package is on the screen before 650 * proceeding with further test operations. 651 * @return String package name 652 * @throws UiObjectNotFoundException 653 */ 654 public String getPackageName() throws UiObjectNotFoundException { 655 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 656 if(node == null) { 657 throw new UiObjectNotFoundException(getSelector().toString()); 658 } 659 return safeStringReturn(node.getPackageName()); 660 } 661 662 /** 663 * Reports the absolute visible screen bounds of the object. If a portion of the UI element 664 * is visible, only the bounds of the visible portion of the UI element are reported. This 665 * becomes important when using bounds to calculate exact coordinates for tapping the element. 666 * @return Rect 667 * @throws UiObjectNotFoundException 668 */ 669 public Rect getBounds() throws UiObjectNotFoundException { 670 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 671 if(node == null) { 672 throw new UiObjectNotFoundException(getSelector().toString()); 673 } 674 return getVisibleBounds(node); 675 } 676 677 /** 678 * This method will wait for a UI element to become visible on the display. It 679 * can be used for situations where the content to be selected is not yet displayed 680 * and the time it will be present is unknown. 681 * @param timeout 682 * @return true if the UI element exists else false for timeout while waiting 683 */ 684 public boolean waitForExists(long timeout) { 685 if(findAccessibilityNodeInfo(timeout) != null) { 686 return true; 687 } 688 return false; 689 } 690 691 /** 692 * Helper to wait for a specified object to no longer be detectable. This can be 693 * useful when having to wait for a progress dialog to finish. 694 * @param timeout 695 * @return true if gone before timeout else false for still present at timeout 696 */ 697 public boolean waitUntilGone(long timeout) { 698 long startMills = SystemClock.uptimeMillis(); 699 long currentMills = 0; 700 while (currentMills <= timeout) { 701 if(findAccessibilityNodeInfo(0) == null) 702 return true; 703 currentMills = SystemClock.uptimeMillis() - startMills; 704 if(timeout > 0) 705 SystemClock.sleep(WAIT_FOR_SELECTOR_POLL); 706 } 707 return false; 708 } 709 710 /** 711 * This methods performs a {@link #waitForExists(long)} with zero timeout. This 712 * basically returns immediately whether the UI element represented by this UiObject 713 * exists or not. If you need to wait longer for this UI element, then see 714 * {@link #waitForExists(long)}. 715 * @return true if the UI element represented by this UiObject does exist 716 */ 717 public boolean exists() { 718 return waitForExists(0); 719 } 720 721 private String safeStringReturn(CharSequence cs) { 722 if(cs == null) 723 return ""; 724 return cs.toString(); 725 } 726} 727