AccessibilityNodeInfo.java revision 14ff996ce82734100ba3faedbc80c4783eebea9d
1/* 2 * Copyright (C) 2011 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 android.view.accessibility; 18 19import android.accessibilityservice.AccessibilityServiceInfo; 20import android.graphics.Rect; 21import android.os.Bundle; 22import android.os.Parcel; 23import android.os.Parcelable; 24import android.util.Pools.SynchronizedPool; 25import android.util.SparseLongArray; 26import android.view.View; 27 28import java.util.Collections; 29import java.util.List; 30 31/** 32 * This class represents a node of the window content as well as actions that 33 * can be requested from its source. From the point of view of an 34 * {@link android.accessibilityservice.AccessibilityService} a window content is 35 * presented as tree of accessibility node info which may or may not map one-to-one 36 * to the view hierarchy. In other words, a custom view is free to report itself as 37 * a tree of accessibility node info. 38 * </p> 39 * <p> 40 * Once an accessibility node info is delivered to an accessibility service it is 41 * made immutable and calling a state mutation method generates an error. 42 * </p> 43 * <p> 44 * Please refer to {@link android.accessibilityservice.AccessibilityService} for 45 * details about how to obtain a handle to window content as a tree of accessibility 46 * node info as well as familiarizing with the security model. 47 * </p> 48 * <div class="special reference"> 49 * <h3>Developer Guides</h3> 50 * <p>For more information about making applications accessible, read the 51 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> 52 * developer guide.</p> 53 * </div> 54 * 55 * @see android.accessibilityservice.AccessibilityService 56 * @see AccessibilityEvent 57 * @see AccessibilityManager 58 */ 59public class AccessibilityNodeInfo implements Parcelable { 60 61 private static final boolean DEBUG = false; 62 63 /** @hide */ 64 public static final int UNDEFINED = -1; 65 66 /** @hide */ 67 public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED, UNDEFINED); 68 69 /** @hide */ 70 public static final int ACTIVE_WINDOW_ID = UNDEFINED; 71 72 /** @hide */ 73 public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001; 74 75 /** @hide */ 76 public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002; 77 78 /** @hide */ 79 public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004; 80 81 /** @hide */ 82 public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008; 83 84 /** @hide */ 85 public static final int FLAG_REPORT_VIEW_IDS = 0x00000010; 86 87 // Actions. 88 89 /** 90 * Action that gives input focus to the node. 91 */ 92 public static final int ACTION_FOCUS = 0x00000001; 93 94 /** 95 * Action that clears input focus of the node. 96 */ 97 public static final int ACTION_CLEAR_FOCUS = 0x00000002; 98 99 /** 100 * Action that selects the node. 101 */ 102 public static final int ACTION_SELECT = 0x00000004; 103 104 /** 105 * Action that unselects the node. 106 */ 107 public static final int ACTION_CLEAR_SELECTION = 0x00000008; 108 109 /** 110 * Action that clicks on the node info. 111 */ 112 public static final int ACTION_CLICK = 0x00000010; 113 114 /** 115 * Action that long clicks on the node. 116 */ 117 public static final int ACTION_LONG_CLICK = 0x00000020; 118 119 /** 120 * Action that gives accessibility focus to the node. 121 */ 122 public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040; 123 124 /** 125 * Action that clears accessibility focus of the node. 126 */ 127 public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080; 128 129 /** 130 * Action that requests to go to the next entity in this node's text 131 * at a given movement granularity. For example, move to the next character, 132 * word, etc. 133 * <p> 134 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br> 135 * <strong>Example:</strong> 136 * <code><pre><p> 137 * Bundle arguments = new Bundle(); 138 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, 139 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); 140 * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments); 141 * </code></pre></p> 142 * </p> 143 * 144 * @see #setMovementGranularities(int) 145 * @see #getMovementGranularities() 146 * 147 * @see #MOVEMENT_GRANULARITY_CHARACTER 148 * @see #MOVEMENT_GRANULARITY_WORD 149 * @see #MOVEMENT_GRANULARITY_LINE 150 * @see #MOVEMENT_GRANULARITY_PARAGRAPH 151 * @see #MOVEMENT_GRANULARITY_PAGE 152 */ 153 public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100; 154 155 /** 156 * Action that requests to go to the previous entity in this node's text 157 * at a given movement granularity. For example, move to the next character, 158 * word, etc. 159 * <p> 160 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br> 161 * <strong>Example:</strong> 162 * <code><pre><p> 163 * Bundle arguments = new Bundle(); 164 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, 165 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); 166 * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, 167 * arguments); 168 * </code></pre></p> 169 * </p> 170 * 171 * @see #setMovementGranularities(int) 172 * @see #getMovementGranularities() 173 * 174 * @see #MOVEMENT_GRANULARITY_CHARACTER 175 * @see #MOVEMENT_GRANULARITY_WORD 176 * @see #MOVEMENT_GRANULARITY_LINE 177 * @see #MOVEMENT_GRANULARITY_PARAGRAPH 178 * @see #MOVEMENT_GRANULARITY_PAGE 179 */ 180 public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200; 181 182 /** 183 * Action to move to the next HTML element of a given type. For example, move 184 * to the BUTTON, INPUT, TABLE, etc. 185 * <p> 186 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> 187 * <strong>Example:</strong> 188 * <code><pre><p> 189 * Bundle arguments = new Bundle(); 190 * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); 191 * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments); 192 * </code></pre></p> 193 * </p> 194 */ 195 public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400; 196 197 /** 198 * Action to move to the previous HTML element of a given type. For example, move 199 * to the BUTTON, INPUT, TABLE, etc. 200 * <p> 201 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> 202 * <strong>Example:</strong> 203 * <code><pre><p> 204 * Bundle arguments = new Bundle(); 205 * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); 206 * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments); 207 * </code></pre></p> 208 * </p> 209 */ 210 public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800; 211 212 /** 213 * Action to scroll the node content forward. 214 */ 215 public static final int ACTION_SCROLL_FORWARD = 0x00001000; 216 217 /** 218 * Action to scroll the node content backward. 219 */ 220 public static final int ACTION_SCROLL_BACKWARD = 0x00002000; 221 222 /** 223 * Argument for which movement granularity to be used when traversing the node text. 224 * <p> 225 * <strong>Type:</strong> int<br> 226 * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY}, 227 * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY} 228 * </p> 229 */ 230 public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = 231 "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT"; 232 233 /** 234 * Argument for which HTML element to get moving to the next/previous HTML element. 235 * <p> 236 * <strong>Type:</strong> String<br> 237 * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT}, 238 * {@link #ACTION_PREVIOUS_HTML_ELEMENT} 239 * </p> 240 */ 241 public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = 242 "ACTION_ARGUMENT_HTML_ELEMENT_STRING"; 243 244 /** 245 * The input focus. 246 */ 247 public static final int FOCUS_INPUT = 1; 248 249 /** 250 * The accessibility focus. 251 */ 252 public static final int FOCUS_ACCESSIBILITY = 2; 253 254 // Movement granularities 255 256 /** 257 * Movement granularity bit for traversing the text of a node by character. 258 */ 259 public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001; 260 261 /** 262 * Movement granularity bit for traversing the text of a node by word. 263 */ 264 public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002; 265 266 /** 267 * Movement granularity bit for traversing the text of a node by line. 268 */ 269 public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004; 270 271 /** 272 * Movement granularity bit for traversing the text of a node by paragraph. 273 */ 274 public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008; 275 276 /** 277 * Movement granularity bit for traversing the text of a node by page. 278 */ 279 public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010; 280 281 // Boolean attributes. 282 283 private static final int PROPERTY_CHECKABLE = 0x00000001; 284 285 private static final int PROPERTY_CHECKED = 0x00000002; 286 287 private static final int PROPERTY_FOCUSABLE = 0x00000004; 288 289 private static final int PROPERTY_FOCUSED = 0x00000008; 290 291 private static final int PROPERTY_SELECTED = 0x00000010; 292 293 private static final int PROPERTY_CLICKABLE = 0x00000020; 294 295 private static final int PROPERTY_LONG_CLICKABLE = 0x00000040; 296 297 private static final int PROPERTY_ENABLED = 0x00000080; 298 299 private static final int PROPERTY_PASSWORD = 0x00000100; 300 301 private static final int PROPERTY_SCROLLABLE = 0x00000200; 302 303 private static final int PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400; 304 305 private static final int PROPERTY_VISIBLE_TO_USER = 0x00000800; 306 307 /** 308 * Bits that provide the id of a virtual descendant of a view. 309 */ 310 private static final long VIRTUAL_DESCENDANT_ID_MASK = 0xffffffff00000000L; 311 312 /** 313 * Bit shift of {@link #VIRTUAL_DESCENDANT_ID_MASK} to get to the id for a 314 * virtual descendant of a view. Such a descendant does not exist in the view 315 * hierarchy and is only reported via the accessibility APIs. 316 */ 317 private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32; 318 319 /** 320 * Gets the accessibility view id which identifies a View in the view three. 321 * 322 * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}. 323 * @return The accessibility view id part of the node id. 324 * 325 * @hide 326 */ 327 public static int getAccessibilityViewId(long accessibilityNodeId) { 328 return (int) accessibilityNodeId; 329 } 330 331 /** 332 * Gets the virtual descendant id which identifies an imaginary view in a 333 * containing View. 334 * 335 * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}. 336 * @return The virtual view id part of the node id. 337 * 338 * @hide 339 */ 340 public static int getVirtualDescendantId(long accessibilityNodeId) { 341 return (int) ((accessibilityNodeId & VIRTUAL_DESCENDANT_ID_MASK) 342 >> VIRTUAL_DESCENDANT_ID_SHIFT); 343 } 344 345 /** 346 * Makes a node id by shifting the <code>virtualDescendantId</code> 347 * by {@link #VIRTUAL_DESCENDANT_ID_SHIFT} and taking 348 * the bitwise or with the <code>accessibilityViewId</code>. 349 * 350 * @param accessibilityViewId A View accessibility id. 351 * @param virtualDescendantId A virtual descendant id. 352 * @return The node id. 353 * 354 * @hide 355 */ 356 public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) { 357 return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId; 358 } 359 360 // Housekeeping. 361 private static final int MAX_POOL_SIZE = 50; 362 private static final SynchronizedPool<AccessibilityNodeInfo> sPool = 363 new SynchronizedPool<AccessibilityNodeInfo>(MAX_POOL_SIZE); 364 365 private boolean mSealed; 366 367 // Data. 368 private int mWindowId = UNDEFINED; 369 private long mSourceNodeId = ROOT_NODE_ID; 370 private long mParentNodeId = ROOT_NODE_ID; 371 private long mLabelForId = ROOT_NODE_ID; 372 private long mLabeledById = ROOT_NODE_ID; 373 374 private int mBooleanProperties; 375 private final Rect mBoundsInParent = new Rect(); 376 private final Rect mBoundsInScreen = new Rect(); 377 378 private CharSequence mPackageName; 379 private CharSequence mClassName; 380 private CharSequence mText; 381 private CharSequence mContentDescription; 382 private CharSequence mViewId; 383 384 private final SparseLongArray mChildNodeIds = new SparseLongArray(); 385 private int mActions; 386 387 private int mMovementGranularities; 388 389 private int mConnectionId = UNDEFINED; 390 391 /** 392 * Hide constructor from clients. 393 */ 394 private AccessibilityNodeInfo() { 395 /* do nothing */ 396 } 397 398 /** 399 * Sets the source. 400 * <p> 401 * <strong>Note:</strong> Cannot be called from an 402 * {@link android.accessibilityservice.AccessibilityService}. 403 * This class is made immutable before being delivered to an AccessibilityService. 404 * </p> 405 * 406 * @param source The info source. 407 */ 408 public void setSource(View source) { 409 setSource(source, UNDEFINED); 410 } 411 412 /** 413 * Sets the source to be a virtual descendant of the given <code>root</code>. 414 * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root 415 * is set as the source. 416 * <p> 417 * A virtual descendant is an imaginary View that is reported as a part of the view 418 * hierarchy for accessibility purposes. This enables custom views that draw complex 419 * content to report themselves as a tree of virtual views, thus conveying their 420 * logical structure. 421 * </p> 422 * <p> 423 * <strong>Note:</strong> Cannot be called from an 424 * {@link android.accessibilityservice.AccessibilityService}. 425 * This class is made immutable before being delivered to an AccessibilityService. 426 * </p> 427 * 428 * @param root The root of the virtual subtree. 429 * @param virtualDescendantId The id of the virtual descendant. 430 */ 431 public void setSource(View root, int virtualDescendantId) { 432 enforceNotSealed(); 433 mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED; 434 final int rootAccessibilityViewId = 435 (root != null) ? root.getAccessibilityViewId() : UNDEFINED; 436 mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 437 } 438 439 /** 440 * Find the view that has the specified focus type. The search starts from 441 * the view represented by this node info. 442 * 443 * @param focus The focus to find. One of {@link #FOCUS_INPUT} or 444 * {@link #FOCUS_ACCESSIBILITY}. 445 * @return The node info of the focused view or null. 446 * 447 * @see #FOCUS_INPUT 448 * @see #FOCUS_ACCESSIBILITY 449 */ 450 public AccessibilityNodeInfo findFocus(int focus) { 451 enforceSealed(); 452 enforceValidFocusType(focus); 453 if (!canPerformRequestOverConnection(mSourceNodeId)) { 454 return null; 455 } 456 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId, 457 mSourceNodeId, focus); 458 } 459 460 /** 461 * Searches for the nearest view in the specified direction that can take 462 * the input focus. 463 * 464 * @param direction The direction. Can be one of: 465 * {@link View#FOCUS_DOWN}, 466 * {@link View#FOCUS_UP}, 467 * {@link View#FOCUS_LEFT}, 468 * {@link View#FOCUS_RIGHT}, 469 * {@link View#FOCUS_FORWARD}, 470 * {@link View#FOCUS_BACKWARD}. 471 * 472 * @return The node info for the view that can take accessibility focus. 473 */ 474 public AccessibilityNodeInfo focusSearch(int direction) { 475 enforceSealed(); 476 enforceValidFocusDirection(direction); 477 if (!canPerformRequestOverConnection(mSourceNodeId)) { 478 return null; 479 } 480 return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId, 481 mSourceNodeId, direction); 482 } 483 484 /** 485 * Gets the id of the window from which the info comes from. 486 * 487 * @return The window id. 488 */ 489 public int getWindowId() { 490 return mWindowId; 491 } 492 493 /** 494 * Refreshes this info with the latest state of the view it represents. 495 * <p> 496 * <strong>Note:</strong> If this method returns false this info is obsolete 497 * since it represents a view that is no longer in the view tree and should 498 * be recycled. 499 * </p> 500 * @return Whether the refresh succeeded. 501 */ 502 public boolean refresh() { 503 enforceSealed(); 504 if (!canPerformRequestOverConnection(mSourceNodeId)) { 505 return false; 506 } 507 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 508 AccessibilityNodeInfo refreshedInfo = client.findAccessibilityNodeInfoByAccessibilityId( 509 mConnectionId, mWindowId, mSourceNodeId, 0); 510 if (refreshedInfo == null) { 511 return false; 512 } 513 init(refreshedInfo); 514 refreshedInfo.recycle(); 515 return true; 516 } 517 518 /** 519 * @return The ids of the children. 520 * 521 * @hide 522 */ 523 public SparseLongArray getChildNodeIds() { 524 return mChildNodeIds; 525 } 526 527 /** 528 * Gets the number of children. 529 * 530 * @return The child count. 531 */ 532 public int getChildCount() { 533 return mChildNodeIds.size(); 534 } 535 536 /** 537 * Get the child at given index. 538 * <p> 539 * <strong>Note:</strong> It is a client responsibility to recycle the 540 * received info by calling {@link AccessibilityNodeInfo#recycle()} 541 * to avoid creating of multiple instances. 542 * </p> 543 * 544 * @param index The child index. 545 * @return The child node. 546 * 547 * @throws IllegalStateException If called outside of an AccessibilityService. 548 * 549 */ 550 public AccessibilityNodeInfo getChild(int index) { 551 enforceSealed(); 552 if (!canPerformRequestOverConnection(mSourceNodeId)) { 553 return null; 554 } 555 final long childId = mChildNodeIds.get(index); 556 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 557 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId, 558 childId, FLAG_PREFETCH_DESCENDANTS); 559 } 560 561 /** 562 * Adds a child. 563 * <p> 564 * <strong>Note:</strong> Cannot be called from an 565 * {@link android.accessibilityservice.AccessibilityService}. 566 * This class is made immutable before being delivered to an AccessibilityService. 567 * </p> 568 * 569 * @param child The child. 570 * 571 * @throws IllegalStateException If called from an AccessibilityService. 572 */ 573 public void addChild(View child) { 574 addChild(child, UNDEFINED); 575 } 576 577 /** 578 * Adds a virtual child which is a descendant of the given <code>root</code>. 579 * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root 580 * is added as a child. 581 * <p> 582 * A virtual descendant is an imaginary View that is reported as a part of the view 583 * hierarchy for accessibility purposes. This enables custom views that draw complex 584 * content to report them selves as a tree of virtual views, thus conveying their 585 * logical structure. 586 * </p> 587 * 588 * @param root The root of the virtual subtree. 589 * @param virtualDescendantId The id of the virtual child. 590 */ 591 public void addChild(View root, int virtualDescendantId) { 592 enforceNotSealed(); 593 final int index = mChildNodeIds.size(); 594 final int rootAccessibilityViewId = 595 (root != null) ? root.getAccessibilityViewId() : UNDEFINED; 596 final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 597 mChildNodeIds.put(index, childNodeId); 598 } 599 600 /** 601 * Gets the actions that can be performed on the node. 602 * 603 * @return The bit mask of with actions. 604 * 605 * @see AccessibilityNodeInfo#ACTION_FOCUS 606 * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS 607 * @see AccessibilityNodeInfo#ACTION_SELECT 608 * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION 609 * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS 610 * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS 611 * @see AccessibilityNodeInfo#ACTION_CLICK 612 * @see AccessibilityNodeInfo#ACTION_LONG_CLICK 613 * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY 614 * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY 615 * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT 616 * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT 617 * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD 618 * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD 619 */ 620 public int getActions() { 621 return mActions; 622 } 623 624 /** 625 * Adds an action that can be performed on the node. 626 * <p> 627 * <strong>Note:</strong> Cannot be called from an 628 * {@link android.accessibilityservice.AccessibilityService}. 629 * This class is made immutable before being delivered to an AccessibilityService. 630 * </p> 631 * 632 * @param action The action. 633 * 634 * @throws IllegalStateException If called from an AccessibilityService. 635 */ 636 public void addAction(int action) { 637 enforceNotSealed(); 638 mActions |= action; 639 } 640 641 /** 642 * Sets the movement granularities for traversing the text of this node. 643 * <p> 644 * <strong>Note:</strong> Cannot be called from an 645 * {@link android.accessibilityservice.AccessibilityService}. 646 * This class is made immutable before being delivered to an AccessibilityService. 647 * </p> 648 * 649 * @param granularities The bit mask with granularities. 650 * 651 * @throws IllegalStateException If called from an AccessibilityService. 652 */ 653 public void setMovementGranularities(int granularities) { 654 enforceNotSealed(); 655 mMovementGranularities = granularities; 656 } 657 658 /** 659 * Gets the movement granularities for traversing the text of this node. 660 * 661 * @return The bit mask with granularities. 662 */ 663 public int getMovementGranularities() { 664 return mMovementGranularities; 665 } 666 667 /** 668 * Performs an action on the node. 669 * <p> 670 * <strong>Note:</strong> An action can be performed only if the request is made 671 * from an {@link android.accessibilityservice.AccessibilityService}. 672 * </p> 673 * 674 * @param action The action to perform. 675 * @return True if the action was performed. 676 * 677 * @throws IllegalStateException If called outside of an AccessibilityService. 678 */ 679 public boolean performAction(int action) { 680 enforceSealed(); 681 if (!canPerformRequestOverConnection(mSourceNodeId)) { 682 return false; 683 } 684 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 685 return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, 686 action, null); 687 } 688 689 /** 690 * Performs an action on the node. 691 * <p> 692 * <strong>Note:</strong> An action can be performed only if the request is made 693 * from an {@link android.accessibilityservice.AccessibilityService}. 694 * </p> 695 * 696 * @param action The action to perform. 697 * @param arguments A bundle with additional arguments. 698 * @return True if the action was performed. 699 * 700 * @throws IllegalStateException If called outside of an AccessibilityService. 701 */ 702 public boolean performAction(int action, Bundle arguments) { 703 enforceSealed(); 704 if (!canPerformRequestOverConnection(mSourceNodeId)) { 705 return false; 706 } 707 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 708 return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, 709 action, arguments); 710 } 711 712 /** 713 * Finds {@link AccessibilityNodeInfo}s by text. The match is case 714 * insensitive containment. The search is relative to this info i.e. 715 * this info is the root of the traversed tree. 716 * 717 * <p> 718 * <strong>Note:</strong> It is a client responsibility to recycle the 719 * received info by calling {@link AccessibilityNodeInfo#recycle()} 720 * to avoid creating of multiple instances. 721 * </p> 722 * 723 * @param text The searched text. 724 * @return A list of node info. 725 */ 726 public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) { 727 enforceSealed(); 728 if (!canPerformRequestOverConnection(mSourceNodeId)) { 729 return Collections.emptyList(); 730 } 731 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 732 return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId, 733 text); 734 } 735 736 /** 737 * Finds {@link AccessibilityNodeInfo}s by the fully qualified view id's resource 738 * name where a fully qualified id is of the from "package:id/id_resource_name". 739 * For example, if the target application's package is "foo.bar" and the id 740 * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz". 741 * 742 * <p> 743 * <strong>Note:</strong> It is a client responsibility to recycle the 744 * received info by calling {@link AccessibilityNodeInfo#recycle()} 745 * to avoid creating of multiple instances. 746 * </p> 747 * <p> 748 * <strong>Note:</strong> The primary usage of this API is for UI test automation 749 * and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo} 750 * the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} 751 * flag when configuring his {@link android.accessibilityservice.AccessibilityService}. 752 * </p> 753 * 754 * @param viewId The fully qualified resource name of the view id to find. 755 * @return A list of node info. 756 */ 757 public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) { 758 enforceSealed(); 759 if (!canPerformRequestOverConnection(mSourceNodeId)) { 760 return Collections.emptyList(); 761 } 762 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 763 return client.findAccessibilityNodeInfosByViewId(mConnectionId, mWindowId, mSourceNodeId, 764 viewId); 765 } 766 767 /** 768 * Gets the parent. 769 * <p> 770 * <strong>Note:</strong> It is a client responsibility to recycle the 771 * received info by calling {@link AccessibilityNodeInfo#recycle()} 772 * to avoid creating of multiple instances. 773 * </p> 774 * 775 * @return The parent. 776 */ 777 public AccessibilityNodeInfo getParent() { 778 enforceSealed(); 779 if (!canPerformRequestOverConnection(mParentNodeId)) { 780 return null; 781 } 782 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 783 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, 784 mWindowId, mParentNodeId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); 785 } 786 787 /** 788 * @return The parent node id. 789 * 790 * @hide 791 */ 792 public long getParentNodeId() { 793 return mParentNodeId; 794 } 795 796 /** 797 * Sets the parent. 798 * <p> 799 * <strong>Note:</strong> Cannot be called from an 800 * {@link android.accessibilityservice.AccessibilityService}. 801 * This class is made immutable before being delivered to an AccessibilityService. 802 * </p> 803 * 804 * @param parent The parent. 805 * 806 * @throws IllegalStateException If called from an AccessibilityService. 807 */ 808 public void setParent(View parent) { 809 setParent(parent, UNDEFINED); 810 } 811 812 /** 813 * Sets the parent to be a virtual descendant of the given <code>root</code>. 814 * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root 815 * is set as the parent. 816 * <p> 817 * A virtual descendant is an imaginary View that is reported as a part of the view 818 * hierarchy for accessibility purposes. This enables custom views that draw complex 819 * content to report them selves as a tree of virtual views, thus conveying their 820 * logical structure. 821 * </p> 822 * <p> 823 * <strong>Note:</strong> Cannot be called from an 824 * {@link android.accessibilityservice.AccessibilityService}. 825 * This class is made immutable before being delivered to an AccessibilityService. 826 * </p> 827 * 828 * @param root The root of the virtual subtree. 829 * @param virtualDescendantId The id of the virtual descendant. 830 */ 831 public void setParent(View root, int virtualDescendantId) { 832 enforceNotSealed(); 833 final int rootAccessibilityViewId = 834 (root != null) ? root.getAccessibilityViewId() : UNDEFINED; 835 mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 836 } 837 838 /** 839 * Gets the node bounds in parent coordinates. 840 * 841 * @param outBounds The output node bounds. 842 */ 843 public void getBoundsInParent(Rect outBounds) { 844 outBounds.set(mBoundsInParent.left, mBoundsInParent.top, 845 mBoundsInParent.right, mBoundsInParent.bottom); 846 } 847 848 /** 849 * Sets the node bounds in parent coordinates. 850 * <p> 851 * <strong>Note:</strong> Cannot be called from an 852 * {@link android.accessibilityservice.AccessibilityService}. 853 * This class is made immutable before being delivered to an AccessibilityService. 854 * </p> 855 * 856 * @param bounds The node bounds. 857 * 858 * @throws IllegalStateException If called from an AccessibilityService. 859 */ 860 public void setBoundsInParent(Rect bounds) { 861 enforceNotSealed(); 862 mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom); 863 } 864 865 /** 866 * Gets the node bounds in screen coordinates. 867 * 868 * @param outBounds The output node bounds. 869 */ 870 public void getBoundsInScreen(Rect outBounds) { 871 outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top, 872 mBoundsInScreen.right, mBoundsInScreen.bottom); 873 } 874 875 /** 876 * Sets the node bounds in screen coordinates. 877 * <p> 878 * <strong>Note:</strong> Cannot be called from an 879 * {@link android.accessibilityservice.AccessibilityService}. 880 * This class is made immutable before being delivered to an AccessibilityService. 881 * </p> 882 * 883 * @param bounds The node bounds. 884 * 885 * @throws IllegalStateException If called from an AccessibilityService. 886 */ 887 public void setBoundsInScreen(Rect bounds) { 888 enforceNotSealed(); 889 mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom); 890 } 891 892 /** 893 * Gets whether this node is checkable. 894 * 895 * @return True if the node is checkable. 896 */ 897 public boolean isCheckable() { 898 return getBooleanProperty(PROPERTY_CHECKABLE); 899 } 900 901 /** 902 * Sets whether this node is checkable. 903 * <p> 904 * <strong>Note:</strong> Cannot be called from an 905 * {@link android.accessibilityservice.AccessibilityService}. 906 * This class is made immutable before being delivered to an AccessibilityService. 907 * </p> 908 * 909 * @param checkable True if the node is checkable. 910 * 911 * @throws IllegalStateException If called from an AccessibilityService. 912 */ 913 public void setCheckable(boolean checkable) { 914 setBooleanProperty(PROPERTY_CHECKABLE, checkable); 915 } 916 917 /** 918 * Gets whether this node is checked. 919 * 920 * @return True if the node is checked. 921 */ 922 public boolean isChecked() { 923 return getBooleanProperty(PROPERTY_CHECKED); 924 } 925 926 /** 927 * Sets whether this node is checked. 928 * <p> 929 * <strong>Note:</strong> Cannot be called from an 930 * {@link android.accessibilityservice.AccessibilityService}. 931 * This class is made immutable before being delivered to an AccessibilityService. 932 * </p> 933 * 934 * @param checked True if the node is checked. 935 * 936 * @throws IllegalStateException If called from an AccessibilityService. 937 */ 938 public void setChecked(boolean checked) { 939 setBooleanProperty(PROPERTY_CHECKED, checked); 940 } 941 942 /** 943 * Gets whether this node is focusable. 944 * 945 * @return True if the node is focusable. 946 */ 947 public boolean isFocusable() { 948 return getBooleanProperty(PROPERTY_FOCUSABLE); 949 } 950 951 /** 952 * Sets whether this node is focusable. 953 * <p> 954 * <strong>Note:</strong> Cannot be called from an 955 * {@link android.accessibilityservice.AccessibilityService}. 956 * This class is made immutable before being delivered to an AccessibilityService. 957 * </p> 958 * 959 * @param focusable True if the node is focusable. 960 * 961 * @throws IllegalStateException If called from an AccessibilityService. 962 */ 963 public void setFocusable(boolean focusable) { 964 setBooleanProperty(PROPERTY_FOCUSABLE, focusable); 965 } 966 967 /** 968 * Gets whether this node is focused. 969 * 970 * @return True if the node is focused. 971 */ 972 public boolean isFocused() { 973 return getBooleanProperty(PROPERTY_FOCUSED); 974 } 975 976 /** 977 * Sets whether this node is focused. 978 * <p> 979 * <strong>Note:</strong> Cannot be called from an 980 * {@link android.accessibilityservice.AccessibilityService}. 981 * This class is made immutable before being delivered to an AccessibilityService. 982 * </p> 983 * 984 * @param focused True if the node is focused. 985 * 986 * @throws IllegalStateException If called from an AccessibilityService. 987 */ 988 public void setFocused(boolean focused) { 989 setBooleanProperty(PROPERTY_FOCUSED, focused); 990 } 991 992 /** 993 * Sets whether this node is visible to the user. 994 * 995 * @return Whether the node is visible to the user. 996 */ 997 public boolean isVisibleToUser() { 998 return getBooleanProperty(PROPERTY_VISIBLE_TO_USER); 999 } 1000 1001 /** 1002 * Sets whether this node is visible to the user. 1003 * <p> 1004 * <strong>Note:</strong> Cannot be called from an 1005 * {@link android.accessibilityservice.AccessibilityService}. 1006 * This class is made immutable before being delivered to an AccessibilityService. 1007 * </p> 1008 * 1009 * @param visibleToUser Whether the node is visible to the user. 1010 * 1011 * @throws IllegalStateException If called from an AccessibilityService. 1012 */ 1013 public void setVisibleToUser(boolean visibleToUser) { 1014 setBooleanProperty(PROPERTY_VISIBLE_TO_USER, visibleToUser); 1015 } 1016 1017 /** 1018 * Gets whether this node is accessibility focused. 1019 * 1020 * @return True if the node is accessibility focused. 1021 */ 1022 public boolean isAccessibilityFocused() { 1023 return getBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED); 1024 } 1025 1026 /** 1027 * Sets whether this node is accessibility focused. 1028 * <p> 1029 * <strong>Note:</strong> Cannot be called from an 1030 * {@link android.accessibilityservice.AccessibilityService}. 1031 * This class is made immutable before being delivered to an AccessibilityService. 1032 * </p> 1033 * 1034 * @param focused True if the node is accessibility focused. 1035 * 1036 * @throws IllegalStateException If called from an AccessibilityService. 1037 */ 1038 public void setAccessibilityFocused(boolean focused) { 1039 setBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED, focused); 1040 } 1041 1042 /** 1043 * Gets whether this node is selected. 1044 * 1045 * @return True if the node is selected. 1046 */ 1047 public boolean isSelected() { 1048 return getBooleanProperty(PROPERTY_SELECTED); 1049 } 1050 1051 /** 1052 * Sets whether this node is selected. 1053 * <p> 1054 * <strong>Note:</strong> Cannot be called from an 1055 * {@link android.accessibilityservice.AccessibilityService}. 1056 * This class is made immutable before being delivered to an AccessibilityService. 1057 * </p> 1058 * 1059 * @param selected True if the node is selected. 1060 * 1061 * @throws IllegalStateException If called from an AccessibilityService. 1062 */ 1063 public void setSelected(boolean selected) { 1064 setBooleanProperty(PROPERTY_SELECTED, selected); 1065 } 1066 1067 /** 1068 * Gets whether this node is clickable. 1069 * 1070 * @return True if the node is clickable. 1071 */ 1072 public boolean isClickable() { 1073 return getBooleanProperty(PROPERTY_CLICKABLE); 1074 } 1075 1076 /** 1077 * Sets whether this node is clickable. 1078 * <p> 1079 * <strong>Note:</strong> Cannot be called from an 1080 * {@link android.accessibilityservice.AccessibilityService}. 1081 * This class is made immutable before being delivered to an AccessibilityService. 1082 * </p> 1083 * 1084 * @param clickable True if the node is clickable. 1085 * 1086 * @throws IllegalStateException If called from an AccessibilityService. 1087 */ 1088 public void setClickable(boolean clickable) { 1089 setBooleanProperty(PROPERTY_CLICKABLE, clickable); 1090 } 1091 1092 /** 1093 * Gets whether this node is long clickable. 1094 * 1095 * @return True if the node is long clickable. 1096 */ 1097 public boolean isLongClickable() { 1098 return getBooleanProperty(PROPERTY_LONG_CLICKABLE); 1099 } 1100 1101 /** 1102 * Sets whether this node is long clickable. 1103 * <p> 1104 * <strong>Note:</strong> Cannot be called from an 1105 * {@link android.accessibilityservice.AccessibilityService}. 1106 * This class is made immutable before being delivered to an AccessibilityService. 1107 * </p> 1108 * 1109 * @param longClickable True if the node is long clickable. 1110 * 1111 * @throws IllegalStateException If called from an AccessibilityService. 1112 */ 1113 public void setLongClickable(boolean longClickable) { 1114 setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable); 1115 } 1116 1117 /** 1118 * Gets whether this node is enabled. 1119 * 1120 * @return True if the node is enabled. 1121 */ 1122 public boolean isEnabled() { 1123 return getBooleanProperty(PROPERTY_ENABLED); 1124 } 1125 1126 /** 1127 * Sets whether this node is enabled. 1128 * <p> 1129 * <strong>Note:</strong> Cannot be called from an 1130 * {@link android.accessibilityservice.AccessibilityService}. 1131 * This class is made immutable before being delivered to an AccessibilityService. 1132 * </p> 1133 * 1134 * @param enabled True if the node is enabled. 1135 * 1136 * @throws IllegalStateException If called from an AccessibilityService. 1137 */ 1138 public void setEnabled(boolean enabled) { 1139 setBooleanProperty(PROPERTY_ENABLED, enabled); 1140 } 1141 1142 /** 1143 * Gets whether this node is a password. 1144 * 1145 * @return True if the node is a password. 1146 */ 1147 public boolean isPassword() { 1148 return getBooleanProperty(PROPERTY_PASSWORD); 1149 } 1150 1151 /** 1152 * Sets whether this node is a password. 1153 * <p> 1154 * <strong>Note:</strong> Cannot be called from an 1155 * {@link android.accessibilityservice.AccessibilityService}. 1156 * This class is made immutable before being delivered to an AccessibilityService. 1157 * </p> 1158 * 1159 * @param password True if the node is a password. 1160 * 1161 * @throws IllegalStateException If called from an AccessibilityService. 1162 */ 1163 public void setPassword(boolean password) { 1164 setBooleanProperty(PROPERTY_PASSWORD, password); 1165 } 1166 1167 /** 1168 * Gets if the node is scrollable. 1169 * 1170 * @return True if the node is scrollable, false otherwise. 1171 */ 1172 public boolean isScrollable() { 1173 return getBooleanProperty(PROPERTY_SCROLLABLE); 1174 } 1175 1176 /** 1177 * Sets if the node is scrollable. 1178 * <p> 1179 * <strong>Note:</strong> Cannot be called from an 1180 * {@link android.accessibilityservice.AccessibilityService}. 1181 * This class is made immutable before being delivered to an AccessibilityService. 1182 * </p> 1183 * 1184 * @param scrollable True if the node is scrollable, false otherwise. 1185 * 1186 * @throws IllegalStateException If called from an AccessibilityService. 1187 */ 1188 public void setScrollable(boolean scrollable) { 1189 enforceNotSealed(); 1190 setBooleanProperty(PROPERTY_SCROLLABLE, scrollable); 1191 } 1192 1193 /** 1194 * Gets the package this node comes from. 1195 * 1196 * @return The package name. 1197 */ 1198 public CharSequence getPackageName() { 1199 return mPackageName; 1200 } 1201 1202 /** 1203 * Sets the package this node comes from. 1204 * <p> 1205 * <strong>Note:</strong> Cannot be called from an 1206 * {@link android.accessibilityservice.AccessibilityService}. 1207 * This class is made immutable before being delivered to an AccessibilityService. 1208 * </p> 1209 * 1210 * @param packageName The package name. 1211 * 1212 * @throws IllegalStateException If called from an AccessibilityService. 1213 */ 1214 public void setPackageName(CharSequence packageName) { 1215 enforceNotSealed(); 1216 mPackageName = packageName; 1217 } 1218 1219 /** 1220 * Gets the class this node comes from. 1221 * 1222 * @return The class name. 1223 */ 1224 public CharSequence getClassName() { 1225 return mClassName; 1226 } 1227 1228 /** 1229 * Sets the class this node comes from. 1230 * <p> 1231 * <strong>Note:</strong> Cannot be called from an 1232 * {@link android.accessibilityservice.AccessibilityService}. 1233 * This class is made immutable before being delivered to an AccessibilityService. 1234 * </p> 1235 * 1236 * @param className The class name. 1237 * 1238 * @throws IllegalStateException If called from an AccessibilityService. 1239 */ 1240 public void setClassName(CharSequence className) { 1241 enforceNotSealed(); 1242 mClassName = className; 1243 } 1244 1245 /** 1246 * Gets the text of this node. 1247 * 1248 * @return The text. 1249 */ 1250 public CharSequence getText() { 1251 return mText; 1252 } 1253 1254 /** 1255 * Sets the text of this node. 1256 * <p> 1257 * <strong>Note:</strong> Cannot be called from an 1258 * {@link android.accessibilityservice.AccessibilityService}. 1259 * This class is made immutable before being delivered to an AccessibilityService. 1260 * </p> 1261 * 1262 * @param text The text. 1263 * 1264 * @throws IllegalStateException If called from an AccessibilityService. 1265 */ 1266 public void setText(CharSequence text) { 1267 enforceNotSealed(); 1268 mText = text; 1269 } 1270 1271 /** 1272 * Gets the content description of this node. 1273 * 1274 * @return The content description. 1275 */ 1276 public CharSequence getContentDescription() { 1277 return mContentDescription; 1278 } 1279 1280 /** 1281 * Sets the content description of this node. 1282 * <p> 1283 * <strong>Note:</strong> Cannot be called from an 1284 * {@link android.accessibilityservice.AccessibilityService}. 1285 * This class is made immutable before being delivered to an AccessibilityService. 1286 * </p> 1287 * 1288 * @param contentDescription The content description. 1289 * 1290 * @throws IllegalStateException If called from an AccessibilityService. 1291 */ 1292 public void setContentDescription(CharSequence contentDescription) { 1293 enforceNotSealed(); 1294 mContentDescription = contentDescription; 1295 } 1296 1297 /** 1298 * Sets the view for which the view represented by this info serves as a 1299 * label for accessibility purposes. 1300 * 1301 * @param labeled The view for which this info serves as a label. 1302 */ 1303 public void setLabelFor(View labeled) { 1304 setLabelFor(labeled, UNDEFINED); 1305 } 1306 1307 /** 1308 * Sets the view for which the view represented by this info serves as a 1309 * label for accessibility purposes. If <code>virtualDescendantId</code> 1310 * is {@link View#NO_ID} the root is set as the labeled. 1311 * <p> 1312 * A virtual descendant is an imaginary View that is reported as a part of the view 1313 * hierarchy for accessibility purposes. This enables custom views that draw complex 1314 * content to report themselves as a tree of virtual views, thus conveying their 1315 * logical structure. 1316 * </p> 1317 * <p> 1318 * <strong>Note:</strong> Cannot be called from an 1319 * {@link android.accessibilityservice.AccessibilityService}. 1320 * This class is made immutable before being delivered to an AccessibilityService. 1321 * </p> 1322 * 1323 * @param root The root whose virtual descendant serves as a label. 1324 * @param virtualDescendantId The id of the virtual descendant. 1325 */ 1326 public void setLabelFor(View root, int virtualDescendantId) { 1327 enforceNotSealed(); 1328 final int rootAccessibilityViewId = (root != null) 1329 ? root.getAccessibilityViewId() : UNDEFINED; 1330 mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 1331 } 1332 1333 /** 1334 * Gets the node info for which the view represented by this info serves as 1335 * a label for accessibility purposes. 1336 * <p> 1337 * <strong>Note:</strong> It is a client responsibility to recycle the 1338 * received info by calling {@link AccessibilityNodeInfo#recycle()} 1339 * to avoid creating of multiple instances. 1340 * </p> 1341 * 1342 * @return The labeled info. 1343 */ 1344 public AccessibilityNodeInfo getLabelFor() { 1345 enforceSealed(); 1346 if (!canPerformRequestOverConnection(mLabelForId)) { 1347 return null; 1348 } 1349 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 1350 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, 1351 mWindowId, mLabelForId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); 1352 } 1353 1354 /** 1355 * Sets the view which serves as the label of the view represented by 1356 * this info for accessibility purposes. 1357 * 1358 * @param label The view that labels this node's source. 1359 */ 1360 public void setLabeledBy(View label) { 1361 setLabeledBy(label, UNDEFINED); 1362 } 1363 1364 /** 1365 * Sets the view which serves as the label of the view represented by 1366 * this info for accessibility purposes. If <code>virtualDescendantId</code> 1367 * is {@link View#NO_ID} the root is set as the label. 1368 * <p> 1369 * A virtual descendant is an imaginary View that is reported as a part of the view 1370 * hierarchy for accessibility purposes. This enables custom views that draw complex 1371 * content to report themselves as a tree of virtual views, thus conveying their 1372 * logical structure. 1373 * </p> 1374 * <p> 1375 * <strong>Note:</strong> Cannot be called from an 1376 * {@link android.accessibilityservice.AccessibilityService}. 1377 * This class is made immutable before being delivered to an AccessibilityService. 1378 * </p> 1379 * 1380 * @param root The root whose virtual descendant labels this node's source. 1381 * @param virtualDescendantId The id of the virtual descendant. 1382 */ 1383 public void setLabeledBy(View root, int virtualDescendantId) { 1384 enforceNotSealed(); 1385 final int rootAccessibilityViewId = (root != null) 1386 ? root.getAccessibilityViewId() : UNDEFINED; 1387 mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 1388 } 1389 1390 /** 1391 * Gets the node info which serves as the label of the view represented by 1392 * this info for accessibility purposes. 1393 * <p> 1394 * <strong>Note:</strong> It is a client responsibility to recycle the 1395 * received info by calling {@link AccessibilityNodeInfo#recycle()} 1396 * to avoid creating of multiple instances. 1397 * </p> 1398 * 1399 * @return The label. 1400 */ 1401 public AccessibilityNodeInfo getLabeledBy() { 1402 enforceSealed(); 1403 if (!canPerformRequestOverConnection(mLabeledById)) { 1404 return null; 1405 } 1406 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 1407 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, 1408 mWindowId, mLabeledById, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); 1409 } 1410 1411 /** 1412 * Sets the fully qualified resource name of the source view's id. 1413 * 1414 * <p> 1415 * <strong>Note:</strong> Cannot be called from an 1416 * {@link android.accessibilityservice.AccessibilityService}. 1417 * This class is made immutable before being delivered to an AccessibilityService. 1418 * </p> 1419 * 1420 * @param viewId The id resource name. 1421 */ 1422 public void setViewId(CharSequence viewId) { 1423 enforceNotSealed(); 1424 mViewId = viewId; 1425 } 1426 1427 /** 1428 * Gets the fully qualified resource name of the source view's id. 1429 * 1430 * <p> 1431 * <strong>Note:</strong> The primary usage of this API is for UI test automation 1432 * and in order to report the source view id of an {@link AccessibilityNodeInfo} the 1433 * client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} 1434 * flag when configuring his {@link android.accessibilityservice.AccessibilityService}. 1435 * </p> 1436 1437 * @return The id resource name. 1438 */ 1439 public CharSequence getViewId() { 1440 return mViewId; 1441 } 1442 1443 /** 1444 * Gets the value of a boolean property. 1445 * 1446 * @param property The property. 1447 * @return The value. 1448 */ 1449 private boolean getBooleanProperty(int property) { 1450 return (mBooleanProperties & property) != 0; 1451 } 1452 1453 /** 1454 * Sets a boolean property. 1455 * 1456 * @param property The property. 1457 * @param value The value. 1458 * 1459 * @throws IllegalStateException If called from an AccessibilityService. 1460 */ 1461 private void setBooleanProperty(int property, boolean value) { 1462 enforceNotSealed(); 1463 if (value) { 1464 mBooleanProperties |= property; 1465 } else { 1466 mBooleanProperties &= ~property; 1467 } 1468 } 1469 1470 /** 1471 * Sets the unique id of the IAccessibilityServiceConnection over which 1472 * this instance can send requests to the system. 1473 * 1474 * @param connectionId The connection id. 1475 * 1476 * @hide 1477 */ 1478 public void setConnectionId(int connectionId) { 1479 enforceNotSealed(); 1480 mConnectionId = connectionId; 1481 } 1482 1483 /** 1484 * {@inheritDoc} 1485 */ 1486 public int describeContents() { 1487 return 0; 1488 } 1489 1490 /** 1491 * Gets the id of the source node. 1492 * 1493 * @return The id. 1494 * 1495 * @hide 1496 */ 1497 public long getSourceNodeId() { 1498 return mSourceNodeId; 1499 } 1500 1501 /** 1502 * Sets if this instance is sealed. 1503 * 1504 * @param sealed Whether is sealed. 1505 * 1506 * @hide 1507 */ 1508 public void setSealed(boolean sealed) { 1509 mSealed = sealed; 1510 } 1511 1512 /** 1513 * Gets if this instance is sealed. 1514 * 1515 * @return Whether is sealed. 1516 * 1517 * @hide 1518 */ 1519 public boolean isSealed() { 1520 return mSealed; 1521 } 1522 1523 /** 1524 * Enforces that this instance is sealed. 1525 * 1526 * @throws IllegalStateException If this instance is not sealed. 1527 * 1528 * @hide 1529 */ 1530 protected void enforceSealed() { 1531 if (!isSealed()) { 1532 throw new IllegalStateException("Cannot perform this " 1533 + "action on a not sealed instance."); 1534 } 1535 } 1536 1537 private void enforceValidFocusDirection(int direction) { 1538 switch (direction) { 1539 case View.FOCUS_DOWN: 1540 case View.FOCUS_UP: 1541 case View.FOCUS_LEFT: 1542 case View.FOCUS_RIGHT: 1543 case View.FOCUS_FORWARD: 1544 case View.FOCUS_BACKWARD: 1545 return; 1546 default: 1547 throw new IllegalArgumentException("Unknown direction: " + direction); 1548 } 1549 } 1550 1551 private void enforceValidFocusType(int focusType) { 1552 switch (focusType) { 1553 case FOCUS_INPUT: 1554 case FOCUS_ACCESSIBILITY: 1555 return; 1556 default: 1557 throw new IllegalArgumentException("Unknown focus type: " + focusType); 1558 } 1559 } 1560 1561 /** 1562 * Enforces that this instance is not sealed. 1563 * 1564 * @throws IllegalStateException If this instance is sealed. 1565 * 1566 * @hide 1567 */ 1568 protected void enforceNotSealed() { 1569 if (isSealed()) { 1570 throw new IllegalStateException("Cannot perform this " 1571 + "action on a sealed instance."); 1572 } 1573 } 1574 1575 /** 1576 * Returns a cached instance if such is available otherwise a new one 1577 * and sets the source. 1578 * 1579 * @param source The source view. 1580 * @return An instance. 1581 * 1582 * @see #setSource(View) 1583 */ 1584 public static AccessibilityNodeInfo obtain(View source) { 1585 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 1586 info.setSource(source); 1587 return info; 1588 } 1589 1590 /** 1591 * Returns a cached instance if such is available otherwise a new one 1592 * and sets the source. 1593 * 1594 * @param root The root of the virtual subtree. 1595 * @param virtualDescendantId The id of the virtual descendant. 1596 * @return An instance. 1597 * 1598 * @see #setSource(View, int) 1599 */ 1600 public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) { 1601 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 1602 info.setSource(root, virtualDescendantId); 1603 return info; 1604 } 1605 1606 /** 1607 * Returns a cached instance if such is available otherwise a new one. 1608 * 1609 * @return An instance. 1610 */ 1611 public static AccessibilityNodeInfo obtain() { 1612 AccessibilityNodeInfo info = sPool.acquire(); 1613 return (info != null) ? info : new AccessibilityNodeInfo(); 1614 } 1615 1616 /** 1617 * Returns a cached instance if such is available or a new one is 1618 * create. The returned instance is initialized from the given 1619 * <code>info</code>. 1620 * 1621 * @param info The other info. 1622 * @return An instance. 1623 */ 1624 public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) { 1625 AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain(); 1626 infoClone.init(info); 1627 return infoClone; 1628 } 1629 1630 /** 1631 * Return an instance back to be reused. 1632 * <p> 1633 * <strong>Note:</strong> You must not touch the object after calling this function. 1634 * 1635 * @throws IllegalStateException If the info is already recycled. 1636 */ 1637 public void recycle() { 1638 clear(); 1639 sPool.release(this); 1640 } 1641 1642 /** 1643 * {@inheritDoc} 1644 * <p> 1645 * <strong>Note:</strong> After the instance is written to a parcel it 1646 * is recycled. You must not touch the object after calling this function. 1647 * </p> 1648 */ 1649 public void writeToParcel(Parcel parcel, int flags) { 1650 parcel.writeInt(isSealed() ? 1 : 0); 1651 parcel.writeLong(mSourceNodeId); 1652 parcel.writeInt(mWindowId); 1653 parcel.writeLong(mParentNodeId); 1654 parcel.writeLong(mLabelForId); 1655 parcel.writeLong(mLabeledById); 1656 parcel.writeInt(mConnectionId); 1657 1658 SparseLongArray childIds = mChildNodeIds; 1659 final int childIdsSize = childIds.size(); 1660 parcel.writeInt(childIdsSize); 1661 for (int i = 0; i < childIdsSize; i++) { 1662 parcel.writeLong(childIds.valueAt(i)); 1663 } 1664 1665 parcel.writeInt(mBoundsInParent.top); 1666 parcel.writeInt(mBoundsInParent.bottom); 1667 parcel.writeInt(mBoundsInParent.left); 1668 parcel.writeInt(mBoundsInParent.right); 1669 1670 parcel.writeInt(mBoundsInScreen.top); 1671 parcel.writeInt(mBoundsInScreen.bottom); 1672 parcel.writeInt(mBoundsInScreen.left); 1673 parcel.writeInt(mBoundsInScreen.right); 1674 1675 parcel.writeInt(mActions); 1676 1677 parcel.writeInt(mMovementGranularities); 1678 1679 parcel.writeInt(mBooleanProperties); 1680 1681 parcel.writeCharSequence(mPackageName); 1682 parcel.writeCharSequence(mClassName); 1683 parcel.writeCharSequence(mText); 1684 parcel.writeCharSequence(mContentDescription); 1685 parcel.writeCharSequence(mViewId); 1686 1687 // Since instances of this class are fetched via synchronous i.e. blocking 1688 // calls in IPCs we always recycle as soon as the instance is marshaled. 1689 recycle(); 1690 } 1691 1692 /** 1693 * Initializes this instance from another one. 1694 * 1695 * @param other The other instance. 1696 */ 1697 private void init(AccessibilityNodeInfo other) { 1698 mSealed = other.mSealed; 1699 mSourceNodeId = other.mSourceNodeId; 1700 mParentNodeId = other.mParentNodeId; 1701 mLabelForId = other.mLabelForId; 1702 mLabeledById = other.mLabeledById; 1703 mWindowId = other.mWindowId; 1704 mConnectionId = other.mConnectionId; 1705 mBoundsInParent.set(other.mBoundsInParent); 1706 mBoundsInScreen.set(other.mBoundsInScreen); 1707 mPackageName = other.mPackageName; 1708 mClassName = other.mClassName; 1709 mText = other.mText; 1710 mContentDescription = other.mContentDescription; 1711 mViewId = other.mViewId; 1712 mActions= other.mActions; 1713 mBooleanProperties = other.mBooleanProperties; 1714 mMovementGranularities = other.mMovementGranularities; 1715 final int otherChildIdCount = other.mChildNodeIds.size(); 1716 for (int i = 0; i < otherChildIdCount; i++) { 1717 mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i)); 1718 } 1719 } 1720 1721 /** 1722 * Creates a new instance from a {@link Parcel}. 1723 * 1724 * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}. 1725 */ 1726 private void initFromParcel(Parcel parcel) { 1727 mSealed = (parcel.readInt() == 1); 1728 mSourceNodeId = parcel.readLong(); 1729 mWindowId = parcel.readInt(); 1730 mParentNodeId = parcel.readLong(); 1731 mLabelForId = parcel.readLong(); 1732 mLabeledById = parcel.readLong(); 1733 mConnectionId = parcel.readInt(); 1734 1735 SparseLongArray childIds = mChildNodeIds; 1736 final int childrenSize = parcel.readInt(); 1737 for (int i = 0; i < childrenSize; i++) { 1738 final long childId = parcel.readLong(); 1739 childIds.put(i, childId); 1740 } 1741 1742 mBoundsInParent.top = parcel.readInt(); 1743 mBoundsInParent.bottom = parcel.readInt(); 1744 mBoundsInParent.left = parcel.readInt(); 1745 mBoundsInParent.right = parcel.readInt(); 1746 1747 mBoundsInScreen.top = parcel.readInt(); 1748 mBoundsInScreen.bottom = parcel.readInt(); 1749 mBoundsInScreen.left = parcel.readInt(); 1750 mBoundsInScreen.right = parcel.readInt(); 1751 1752 mActions = parcel.readInt(); 1753 1754 mMovementGranularities = parcel.readInt(); 1755 1756 mBooleanProperties = parcel.readInt(); 1757 1758 mPackageName = parcel.readCharSequence(); 1759 mClassName = parcel.readCharSequence(); 1760 mText = parcel.readCharSequence(); 1761 mContentDescription = parcel.readCharSequence(); 1762 mViewId = parcel.readCharSequence(); 1763 } 1764 1765 /** 1766 * Clears the state of this instance. 1767 */ 1768 private void clear() { 1769 mSealed = false; 1770 mSourceNodeId = ROOT_NODE_ID; 1771 mParentNodeId = ROOT_NODE_ID; 1772 mLabelForId = ROOT_NODE_ID; 1773 mLabeledById = ROOT_NODE_ID; 1774 mWindowId = UNDEFINED; 1775 mConnectionId = UNDEFINED; 1776 mMovementGranularities = 0; 1777 mChildNodeIds.clear(); 1778 mBoundsInParent.set(0, 0, 0, 0); 1779 mBoundsInScreen.set(0, 0, 0, 0); 1780 mBooleanProperties = 0; 1781 mPackageName = null; 1782 mClassName = null; 1783 mText = null; 1784 mContentDescription = null; 1785 mViewId = null; 1786 mActions = 0; 1787 } 1788 1789 /** 1790 * Gets the human readable action symbolic name. 1791 * 1792 * @param action The action. 1793 * @return The symbolic name. 1794 */ 1795 private static String getActionSymbolicName(int action) { 1796 switch (action) { 1797 case ACTION_FOCUS: 1798 return "ACTION_FOCUS"; 1799 case ACTION_CLEAR_FOCUS: 1800 return "ACTION_CLEAR_FOCUS"; 1801 case ACTION_SELECT: 1802 return "ACTION_SELECT"; 1803 case ACTION_CLEAR_SELECTION: 1804 return "ACTION_CLEAR_SELECTION"; 1805 case ACTION_CLICK: 1806 return "ACTION_CLICK"; 1807 case ACTION_LONG_CLICK: 1808 return "ACTION_LONG_CLICK"; 1809 case ACTION_ACCESSIBILITY_FOCUS: 1810 return "ACTION_ACCESSIBILITY_FOCUS"; 1811 case ACTION_CLEAR_ACCESSIBILITY_FOCUS: 1812 return "ACTION_CLEAR_ACCESSIBILITY_FOCUS"; 1813 case ACTION_NEXT_AT_MOVEMENT_GRANULARITY: 1814 return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY"; 1815 case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: 1816 return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY"; 1817 case ACTION_NEXT_HTML_ELEMENT: 1818 return "ACTION_NEXT_HTML_ELEMENT"; 1819 case ACTION_PREVIOUS_HTML_ELEMENT: 1820 return "ACTION_PREVIOUS_HTML_ELEMENT"; 1821 case ACTION_SCROLL_FORWARD: 1822 return "ACTION_SCROLL_FORWARD"; 1823 case ACTION_SCROLL_BACKWARD: 1824 return "ACTION_SCROLL_BACKWARD"; 1825 default: 1826 throw new IllegalArgumentException("Unknown action: " + action); 1827 } 1828 } 1829 1830 /** 1831 * Gets the human readable movement granularity symbolic name. 1832 * 1833 * @param granularity The granularity. 1834 * @return The symbolic name. 1835 */ 1836 private static String getMovementGranularitySymbolicName(int granularity) { 1837 switch (granularity) { 1838 case MOVEMENT_GRANULARITY_CHARACTER: 1839 return "MOVEMENT_GRANULARITY_CHARACTER"; 1840 case MOVEMENT_GRANULARITY_WORD: 1841 return "MOVEMENT_GRANULARITY_WORD"; 1842 case MOVEMENT_GRANULARITY_LINE: 1843 return "MOVEMENT_GRANULARITY_LINE"; 1844 case MOVEMENT_GRANULARITY_PARAGRAPH: 1845 return "MOVEMENT_GRANULARITY_PARAGRAPH"; 1846 case MOVEMENT_GRANULARITY_PAGE: 1847 return "MOVEMENT_GRANULARITY_PAGE"; 1848 default: 1849 throw new IllegalArgumentException("Unknown movement granularity: " + granularity); 1850 } 1851 } 1852 1853 private boolean canPerformRequestOverConnection(long accessibilityNodeId) { 1854 return (mWindowId != UNDEFINED 1855 && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED 1856 && mConnectionId != UNDEFINED); 1857 } 1858 1859 @Override 1860 public boolean equals(Object object) { 1861 if (this == object) { 1862 return true; 1863 } 1864 if (object == null) { 1865 return false; 1866 } 1867 if (getClass() != object.getClass()) { 1868 return false; 1869 } 1870 AccessibilityNodeInfo other = (AccessibilityNodeInfo) object; 1871 if (mSourceNodeId != other.mSourceNodeId) { 1872 return false; 1873 } 1874 if (mWindowId != other.mWindowId) { 1875 return false; 1876 } 1877 return true; 1878 } 1879 1880 @Override 1881 public int hashCode() { 1882 final int prime = 31; 1883 int result = 1; 1884 result = prime * result + getAccessibilityViewId(mSourceNodeId); 1885 result = prime * result + getVirtualDescendantId(mSourceNodeId); 1886 result = prime * result + mWindowId; 1887 return result; 1888 } 1889 1890 @Override 1891 public String toString() { 1892 StringBuilder builder = new StringBuilder(); 1893 builder.append(super.toString()); 1894 1895 if (DEBUG) { 1896 builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId)); 1897 builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId)); 1898 builder.append("; mParentNodeId: " + mParentNodeId); 1899 1900 int granularities = mMovementGranularities; 1901 builder.append("; MovementGranularities: ["); 1902 while (granularities != 0) { 1903 final int granularity = 1 << Integer.numberOfTrailingZeros(granularities); 1904 granularities &= ~granularity; 1905 builder.append(getMovementGranularitySymbolicName(granularity)); 1906 if (granularities != 0) { 1907 builder.append(", "); 1908 } 1909 } 1910 builder.append("]"); 1911 1912 SparseLongArray childIds = mChildNodeIds; 1913 builder.append("; childAccessibilityIds: ["); 1914 for (int i = 0, count = childIds.size(); i < count; i++) { 1915 builder.append(childIds.valueAt(i)); 1916 if (i < count - 1) { 1917 builder.append(", "); 1918 } 1919 } 1920 builder.append("]"); 1921 } 1922 1923 builder.append("; boundsInParent: " + mBoundsInParent); 1924 builder.append("; boundsInScreen: " + mBoundsInScreen); 1925 1926 builder.append("; packageName: ").append(mPackageName); 1927 builder.append("; className: ").append(mClassName); 1928 builder.append("; text: ").append(mText); 1929 builder.append("; contentDescription: ").append(mContentDescription); 1930 builder.append("; viewId: ").append(mViewId); 1931 1932 builder.append("; checkable: ").append(isCheckable()); 1933 builder.append("; checked: ").append(isChecked()); 1934 builder.append("; focusable: ").append(isFocusable()); 1935 builder.append("; focused: ").append(isFocused()); 1936 builder.append("; selected: ").append(isSelected()); 1937 builder.append("; clickable: ").append(isClickable()); 1938 builder.append("; longClickable: ").append(isLongClickable()); 1939 builder.append("; enabled: ").append(isEnabled()); 1940 builder.append("; password: ").append(isPassword()); 1941 builder.append("; scrollable: " + isScrollable()); 1942 1943 builder.append("; ["); 1944 for (int actionBits = mActions; actionBits != 0;) { 1945 final int action = 1 << Integer.numberOfTrailingZeros(actionBits); 1946 actionBits &= ~action; 1947 builder.append(getActionSymbolicName(action)); 1948 if (actionBits != 0) { 1949 builder.append(", "); 1950 } 1951 } 1952 builder.append("]"); 1953 1954 return builder.toString(); 1955 } 1956 1957 /** 1958 * @see Parcelable.Creator 1959 */ 1960 public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR = 1961 new Parcelable.Creator<AccessibilityNodeInfo>() { 1962 public AccessibilityNodeInfo createFromParcel(Parcel parcel) { 1963 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 1964 info.initFromParcel(parcel); 1965 return info; 1966 } 1967 1968 public AccessibilityNodeInfo[] newArray(int size) { 1969 return new AccessibilityNodeInfo[size]; 1970 } 1971 }; 1972} 1973