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