AccessibilityNodeInfo.java revision c9c9a48e7bafae63cb35a9aa69255e80aba83988
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.graphics.Rect; 20import android.os.Bundle; 21import android.os.Parcel; 22import android.os.Parcelable; 23import android.util.SparseLongArray; 24import android.view.View; 25 26import java.util.Collections; 27import 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 */ 57public 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 369 private int mBooleanProperties; 370 private final Rect mBoundsInParent = new Rect(); 371 private final Rect mBoundsInScreen = new Rect(); 372 373 private CharSequence mPackageName; 374 private CharSequence mClassName; 375 private CharSequence mText; 376 private CharSequence mContentDescription; 377 378 private final SparseLongArray mChildNodeIds = new SparseLongArray(); 379 private int mActions; 380 381 private int mMovementGranularities; 382 383 private int mConnectionId = UNDEFINED; 384 385 /** 386 * Hide constructor from clients. 387 */ 388 private AccessibilityNodeInfo() { 389 /* do nothing */ 390 } 391 392 /** 393 * Sets the source. 394 * <p> 395 * <strong>Note:</strong> Cannot be called from an 396 * {@link android.accessibilityservice.AccessibilityService}. 397 * This class is made immutable before being delivered to an AccessibilityService. 398 * </p> 399 * 400 * @param source The info source. 401 */ 402 public void setSource(View source) { 403 setSource(source, UNDEFINED); 404 } 405 406 /** 407 * Sets the source to be a virtual descendant of the given <code>root</code>. 408 * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root 409 * is set as the source. 410 * <p> 411 * A virtual descendant is an imaginary View that is reported as a part of the view 412 * hierarchy for accessibility purposes. This enables custom views that draw complex 413 * content to report themselves as a tree of virtual views, thus conveying their 414 * logical structure. 415 * </p> 416 * <p> 417 * <strong>Note:</strong> Cannot be called from an 418 * {@link android.accessibilityservice.AccessibilityService}. 419 * This class is made immutable before being delivered to an AccessibilityService. 420 * </p> 421 * 422 * @param root The root of the virtual subtree. 423 * @param virtualDescendantId The id of the virtual descendant. 424 */ 425 public void setSource(View root, int virtualDescendantId) { 426 enforceNotSealed(); 427 mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED; 428 final int rootAccessibilityViewId = 429 (root != null) ? root.getAccessibilityViewId() : UNDEFINED; 430 mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 431 } 432 433 /** 434 * Find the view that has the specified focus type. The search starts from 435 * the view represented by this node info. 436 * 437 * @param focus The focus to find. One of {@link #FOCUS_INPUT} or 438 * {@link #FOCUS_ACCESSIBILITY}. 439 * @return The node info of the focused view or null. 440 * 441 * @see #FOCUS_INPUT 442 * @see #FOCUS_ACCESSIBILITY 443 */ 444 public AccessibilityNodeInfo findFocus(int focus) { 445 enforceSealed(); 446 enforceValidFocusType(focus); 447 if (!canPerformRequestOverConnection(mSourceNodeId)) { 448 return null; 449 } 450 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId, 451 mSourceNodeId, focus); 452 } 453 454 /** 455 * Searches for the nearest view in the specified direction that can take 456 * the input focus. 457 * 458 * @param direction The direction. Can be one of: 459 * {@link View#FOCUS_DOWN}, 460 * {@link View#FOCUS_UP}, 461 * {@link View#FOCUS_LEFT}, 462 * {@link View#FOCUS_RIGHT}, 463 * {@link View#FOCUS_FORWARD}, 464 * {@link View#FOCUS_BACKWARD}. 465 * 466 * @return The node info for the view that can take accessibility focus. 467 */ 468 public AccessibilityNodeInfo focusSearch(int direction) { 469 enforceSealed(); 470 enforceValidFocusDirection(direction); 471 if (!canPerformRequestOverConnection(mSourceNodeId)) { 472 return null; 473 } 474 return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId, 475 mSourceNodeId, direction); 476 } 477 478 /** 479 * Gets the id of the window from which the info comes from. 480 * 481 * @return The window id. 482 */ 483 public int getWindowId() { 484 return mWindowId; 485 } 486 487 /** 488 * @return The ids of the children. 489 * 490 * @hide 491 */ 492 public SparseLongArray getChildNodeIds() { 493 return mChildNodeIds; 494 } 495 496 /** 497 * Gets the number of children. 498 * 499 * @return The child count. 500 */ 501 public int getChildCount() { 502 return mChildNodeIds.size(); 503 } 504 505 /** 506 * Get the child at given index. 507 * <p> 508 * <strong>Note:</strong> It is a client responsibility to recycle the 509 * received info by calling {@link AccessibilityNodeInfo#recycle()} 510 * to avoid creating of multiple instances. 511 * </p> 512 * 513 * @param index The child index. 514 * @return The child node. 515 * 516 * @throws IllegalStateException If called outside of an AccessibilityService. 517 * 518 */ 519 public AccessibilityNodeInfo getChild(int index) { 520 enforceSealed(); 521 if (!canPerformRequestOverConnection(mSourceNodeId)) { 522 return null; 523 } 524 final long childId = mChildNodeIds.get(index); 525 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 526 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId, 527 childId, FLAG_PREFETCH_DESCENDANTS); 528 } 529 530 /** 531 * Adds a child. 532 * <p> 533 * <strong>Note:</strong> Cannot be called from an 534 * {@link android.accessibilityservice.AccessibilityService}. 535 * This class is made immutable before being delivered to an AccessibilityService. 536 * </p> 537 * 538 * @param child The child. 539 * 540 * @throws IllegalStateException If called from an AccessibilityService. 541 */ 542 public void addChild(View child) { 543 addChild(child, UNDEFINED); 544 } 545 546 /** 547 * Adds a virtual child which is a descendant of the given <code>root</code>. 548 * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root 549 * is added as a child. 550 * <p> 551 * A virtual descendant is an imaginary View that is reported as a part of the view 552 * hierarchy for accessibility purposes. This enables custom views that draw complex 553 * content to report them selves as a tree of virtual views, thus conveying their 554 * logical structure. 555 * </p> 556 * 557 * @param root The root of the virtual subtree. 558 * @param virtualDescendantId The id of the virtual child. 559 */ 560 public void addChild(View root, int virtualDescendantId) { 561 enforceNotSealed(); 562 final int index = mChildNodeIds.size(); 563 final int rootAccessibilityViewId = 564 (root != null) ? root.getAccessibilityViewId() : UNDEFINED; 565 final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 566 mChildNodeIds.put(index, childNodeId); 567 } 568 569 /** 570 * Gets the actions that can be performed on the node. 571 * 572 * @return The bit mask of with actions. 573 * 574 * @see AccessibilityNodeInfo#ACTION_FOCUS 575 * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS 576 * @see AccessibilityNodeInfo#ACTION_SELECT 577 * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION 578 * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS 579 * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS 580 * @see AccessibilityNodeInfo#ACTION_CLICK 581 * @see AccessibilityNodeInfo#ACTION_LONG_CLICK 582 * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY 583 * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY 584 * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT 585 * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT 586 * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD 587 * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD 588 */ 589 public int getActions() { 590 return mActions; 591 } 592 593 /** 594 * Adds an action that can be performed on the node. 595 * <p> 596 * <strong>Note:</strong> Cannot be called from an 597 * {@link android.accessibilityservice.AccessibilityService}. 598 * This class is made immutable before being delivered to an AccessibilityService. 599 * </p> 600 * 601 * @param action The action. 602 * 603 * @throws IllegalStateException If called from an AccessibilityService. 604 */ 605 public void addAction(int action) { 606 enforceNotSealed(); 607 mActions |= action; 608 } 609 610 /** 611 * Sets the movement granularities for traversing the text of this node. 612 * <p> 613 * <strong>Note:</strong> Cannot be called from an 614 * {@link android.accessibilityservice.AccessibilityService}. 615 * This class is made immutable before being delivered to an AccessibilityService. 616 * </p> 617 * 618 * @param granularities The bit mask with granularities. 619 * 620 * @throws IllegalStateException If called from an AccessibilityService. 621 */ 622 public void setMovementGranularities(int granularities) { 623 enforceNotSealed(); 624 mMovementGranularities = granularities; 625 } 626 627 /** 628 * Gets the movement granularities for traversing the text of this node. 629 * 630 * @return The bit mask with granularities. 631 */ 632 public int getMovementGranularities() { 633 return mMovementGranularities; 634 } 635 636 /** 637 * Performs an action on the node. 638 * <p> 639 * <strong>Note:</strong> An action can be performed only if the request is made 640 * from an {@link android.accessibilityservice.AccessibilityService}. 641 * </p> 642 * 643 * @param action The action to perform. 644 * @return True if the action was performed. 645 * 646 * @throws IllegalStateException If called outside of an AccessibilityService. 647 */ 648 public boolean performAction(int action) { 649 enforceSealed(); 650 if (!canPerformRequestOverConnection(mSourceNodeId)) { 651 return false; 652 } 653 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 654 return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, 655 action, null); 656 } 657 658 /** 659 * Performs an action on the node. 660 * <p> 661 * <strong>Note:</strong> An action can be performed only if the request is made 662 * from an {@link android.accessibilityservice.AccessibilityService}. 663 * </p> 664 * 665 * @param action The action to perform. 666 * @param arguments A bundle with additional arguments. 667 * @return True if the action was performed. 668 * 669 * @throws IllegalStateException If called outside of an AccessibilityService. 670 */ 671 public boolean performAction(int action, Bundle arguments) { 672 enforceSealed(); 673 if (!canPerformRequestOverConnection(mSourceNodeId)) { 674 return false; 675 } 676 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 677 return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, 678 action, arguments); 679 } 680 681 /** 682 * Finds {@link AccessibilityNodeInfo}s by text. The match is case 683 * insensitive containment. The search is relative to this info i.e. 684 * this info is the root of the traversed tree. 685 * 686 * <p> 687 * <strong>Note:</strong> It is a client responsibility to recycle the 688 * received info by calling {@link AccessibilityNodeInfo#recycle()} 689 * to avoid creating of multiple instances. 690 * </p> 691 * 692 * @param text The searched text. 693 * @return A list of node info. 694 */ 695 public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) { 696 enforceSealed(); 697 if (!canPerformRequestOverConnection(mSourceNodeId)) { 698 return Collections.emptyList(); 699 } 700 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 701 return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId, 702 text); 703 } 704 705 /** 706 * Gets the parent. 707 * <p> 708 * <strong>Note:</strong> It is a client responsibility to recycle the 709 * received info by calling {@link AccessibilityNodeInfo#recycle()} 710 * to avoid creating of multiple instances. 711 * </p> 712 * 713 * @return The parent. 714 */ 715 public AccessibilityNodeInfo getParent() { 716 enforceSealed(); 717 if (!canPerformRequestOverConnection(mParentNodeId)) { 718 return null; 719 } 720 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 721 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, 722 mWindowId, mParentNodeId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); 723 } 724 725 /** 726 * @return The parent node id. 727 * 728 * @hide 729 */ 730 public long getParentNodeId() { 731 return mParentNodeId; 732 } 733 734 /** 735 * Sets the parent. 736 * <p> 737 * <strong>Note:</strong> Cannot be called from an 738 * {@link android.accessibilityservice.AccessibilityService}. 739 * This class is made immutable before being delivered to an AccessibilityService. 740 * </p> 741 * 742 * @param parent The parent. 743 * 744 * @throws IllegalStateException If called from an AccessibilityService. 745 */ 746 public void setParent(View parent) { 747 setParent(parent, UNDEFINED); 748 } 749 750 /** 751 * Sets the parent to be a virtual descendant of the given <code>root</code>. 752 * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root 753 * is set as the parent. 754 * <p> 755 * A virtual descendant is an imaginary View that is reported as a part of the view 756 * hierarchy for accessibility purposes. This enables custom views that draw complex 757 * content to report them selves as a tree of virtual views, thus conveying their 758 * logical structure. 759 * </p> 760 * <p> 761 * <strong>Note:</strong> Cannot be called from an 762 * {@link android.accessibilityservice.AccessibilityService}. 763 * This class is made immutable before being delivered to an AccessibilityService. 764 * </p> 765 * 766 * @param root The root of the virtual subtree. 767 * @param virtualDescendantId The id of the virtual descendant. 768 */ 769 public void setParent(View root, int virtualDescendantId) { 770 enforceNotSealed(); 771 final int rootAccessibilityViewId = 772 (root != null) ? root.getAccessibilityViewId() : UNDEFINED; 773 mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 774 } 775 776 /** 777 * Gets the node bounds in parent coordinates. 778 * 779 * @param outBounds The output node bounds. 780 */ 781 public void getBoundsInParent(Rect outBounds) { 782 outBounds.set(mBoundsInParent.left, mBoundsInParent.top, 783 mBoundsInParent.right, mBoundsInParent.bottom); 784 } 785 786 /** 787 * Sets the node bounds in parent coordinates. 788 * <p> 789 * <strong>Note:</strong> Cannot be called from an 790 * {@link android.accessibilityservice.AccessibilityService}. 791 * This class is made immutable before being delivered to an AccessibilityService. 792 * </p> 793 * 794 * @param bounds The node bounds. 795 * 796 * @throws IllegalStateException If called from an AccessibilityService. 797 */ 798 public void setBoundsInParent(Rect bounds) { 799 enforceNotSealed(); 800 mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom); 801 } 802 803 /** 804 * Gets the node bounds in screen coordinates. 805 * 806 * @param outBounds The output node bounds. 807 */ 808 public void getBoundsInScreen(Rect outBounds) { 809 outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top, 810 mBoundsInScreen.right, mBoundsInScreen.bottom); 811 } 812 813 /** 814 * Sets the node bounds in screen coordinates. 815 * <p> 816 * <strong>Note:</strong> Cannot be called from an 817 * {@link android.accessibilityservice.AccessibilityService}. 818 * This class is made immutable before being delivered to an AccessibilityService. 819 * </p> 820 * 821 * @param bounds The node bounds. 822 * 823 * @throws IllegalStateException If called from an AccessibilityService. 824 */ 825 public void setBoundsInScreen(Rect bounds) { 826 enforceNotSealed(); 827 mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom); 828 } 829 830 /** 831 * Gets whether this node is checkable. 832 * 833 * @return True if the node is checkable. 834 */ 835 public boolean isCheckable() { 836 return getBooleanProperty(PROPERTY_CHECKABLE); 837 } 838 839 /** 840 * Sets whether this node is checkable. 841 * <p> 842 * <strong>Note:</strong> Cannot be called from an 843 * {@link android.accessibilityservice.AccessibilityService}. 844 * This class is made immutable before being delivered to an AccessibilityService. 845 * </p> 846 * 847 * @param checkable True if the node is checkable. 848 * 849 * @throws IllegalStateException If called from an AccessibilityService. 850 */ 851 public void setCheckable(boolean checkable) { 852 setBooleanProperty(PROPERTY_CHECKABLE, checkable); 853 } 854 855 /** 856 * Gets whether this node is checked. 857 * 858 * @return True if the node is checked. 859 */ 860 public boolean isChecked() { 861 return getBooleanProperty(PROPERTY_CHECKED); 862 } 863 864 /** 865 * Sets whether this node is checked. 866 * <p> 867 * <strong>Note:</strong> Cannot be called from an 868 * {@link android.accessibilityservice.AccessibilityService}. 869 * This class is made immutable before being delivered to an AccessibilityService. 870 * </p> 871 * 872 * @param checked True if the node is checked. 873 * 874 * @throws IllegalStateException If called from an AccessibilityService. 875 */ 876 public void setChecked(boolean checked) { 877 setBooleanProperty(PROPERTY_CHECKED, checked); 878 } 879 880 /** 881 * Gets whether this node is focusable. 882 * 883 * @return True if the node is focusable. 884 */ 885 public boolean isFocusable() { 886 return getBooleanProperty(PROPERTY_FOCUSABLE); 887 } 888 889 /** 890 * Sets whether this node is focusable. 891 * <p> 892 * <strong>Note:</strong> Cannot be called from an 893 * {@link android.accessibilityservice.AccessibilityService}. 894 * This class is made immutable before being delivered to an AccessibilityService. 895 * </p> 896 * 897 * @param focusable True if the node is focusable. 898 * 899 * @throws IllegalStateException If called from an AccessibilityService. 900 */ 901 public void setFocusable(boolean focusable) { 902 setBooleanProperty(PROPERTY_FOCUSABLE, focusable); 903 } 904 905 /** 906 * Gets whether this node is focused. 907 * 908 * @return True if the node is focused. 909 */ 910 public boolean isFocused() { 911 return getBooleanProperty(PROPERTY_FOCUSED); 912 } 913 914 /** 915 * Sets whether this node is focused. 916 * <p> 917 * <strong>Note:</strong> Cannot be called from an 918 * {@link android.accessibilityservice.AccessibilityService}. 919 * This class is made immutable before being delivered to an AccessibilityService. 920 * </p> 921 * 922 * @param focused True if the node is focused. 923 * 924 * @throws IllegalStateException If called from an AccessibilityService. 925 */ 926 public void setFocused(boolean focused) { 927 setBooleanProperty(PROPERTY_FOCUSED, focused); 928 } 929 930 /** 931 * Sets whether this node is visible to the user. 932 * 933 * @return Whether the node is visible to the user. 934 */ 935 public boolean isVisibleToUser() { 936 return getBooleanProperty(PROPERTY_VISIBLE_TO_USER); 937 } 938 939 /** 940 * Sets whether this node is visible to the user. 941 * <p> 942 * <strong>Note:</strong> Cannot be called from an 943 * {@link android.accessibilityservice.AccessibilityService}. 944 * This class is made immutable before being delivered to an AccessibilityService. 945 * </p> 946 * 947 * @param visibleToUser Whether the node is visible to the user. 948 * 949 * @throws IllegalStateException If called from an AccessibilityService. 950 */ 951 public void setVisibleToUser(boolean visibleToUser) { 952 setBooleanProperty(PROPERTY_VISIBLE_TO_USER, visibleToUser); 953 } 954 955 /** 956 * Gets whether this node is accessibility focused. 957 * 958 * @return True if the node is accessibility focused. 959 */ 960 public boolean isAccessibilityFocused() { 961 return getBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED); 962 } 963 964 /** 965 * Sets whether this node is accessibility focused. 966 * <p> 967 * <strong>Note:</strong> Cannot be called from an 968 * {@link android.accessibilityservice.AccessibilityService}. 969 * This class is made immutable before being delivered to an AccessibilityService. 970 * </p> 971 * 972 * @param focused True if the node is accessibility focused. 973 * 974 * @throws IllegalStateException If called from an AccessibilityService. 975 */ 976 public void setAccessibilityFocused(boolean focused) { 977 setBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED, focused); 978 } 979 980 /** 981 * Gets whether this node is selected. 982 * 983 * @return True if the node is selected. 984 */ 985 public boolean isSelected() { 986 return getBooleanProperty(PROPERTY_SELECTED); 987 } 988 989 /** 990 * Sets whether this node is selected. 991 * <p> 992 * <strong>Note:</strong> Cannot be called from an 993 * {@link android.accessibilityservice.AccessibilityService}. 994 * This class is made immutable before being delivered to an AccessibilityService. 995 * </p> 996 * 997 * @param selected True if the node is selected. 998 * 999 * @throws IllegalStateException If called from an AccessibilityService. 1000 */ 1001 public void setSelected(boolean selected) { 1002 setBooleanProperty(PROPERTY_SELECTED, selected); 1003 } 1004 1005 /** 1006 * Gets whether this node is clickable. 1007 * 1008 * @return True if the node is clickable. 1009 */ 1010 public boolean isClickable() { 1011 return getBooleanProperty(PROPERTY_CLICKABLE); 1012 } 1013 1014 /** 1015 * Sets whether this node is clickable. 1016 * <p> 1017 * <strong>Note:</strong> Cannot be called from an 1018 * {@link android.accessibilityservice.AccessibilityService}. 1019 * This class is made immutable before being delivered to an AccessibilityService. 1020 * </p> 1021 * 1022 * @param clickable True if the node is clickable. 1023 * 1024 * @throws IllegalStateException If called from an AccessibilityService. 1025 */ 1026 public void setClickable(boolean clickable) { 1027 setBooleanProperty(PROPERTY_CLICKABLE, clickable); 1028 } 1029 1030 /** 1031 * Gets whether this node is long clickable. 1032 * 1033 * @return True if the node is long clickable. 1034 */ 1035 public boolean isLongClickable() { 1036 return getBooleanProperty(PROPERTY_LONG_CLICKABLE); 1037 } 1038 1039 /** 1040 * Sets whether this node is long clickable. 1041 * <p> 1042 * <strong>Note:</strong> Cannot be called from an 1043 * {@link android.accessibilityservice.AccessibilityService}. 1044 * This class is made immutable before being delivered to an AccessibilityService. 1045 * </p> 1046 * 1047 * @param longClickable True if the node is long clickable. 1048 * 1049 * @throws IllegalStateException If called from an AccessibilityService. 1050 */ 1051 public void setLongClickable(boolean longClickable) { 1052 setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable); 1053 } 1054 1055 /** 1056 * Gets whether this node is enabled. 1057 * 1058 * @return True if the node is enabled. 1059 */ 1060 public boolean isEnabled() { 1061 return getBooleanProperty(PROPERTY_ENABLED); 1062 } 1063 1064 /** 1065 * Sets whether this node is enabled. 1066 * <p> 1067 * <strong>Note:</strong> Cannot be called from an 1068 * {@link android.accessibilityservice.AccessibilityService}. 1069 * This class is made immutable before being delivered to an AccessibilityService. 1070 * </p> 1071 * 1072 * @param enabled True if the node is enabled. 1073 * 1074 * @throws IllegalStateException If called from an AccessibilityService. 1075 */ 1076 public void setEnabled(boolean enabled) { 1077 setBooleanProperty(PROPERTY_ENABLED, enabled); 1078 } 1079 1080 /** 1081 * Gets whether this node is a password. 1082 * 1083 * @return True if the node is a password. 1084 */ 1085 public boolean isPassword() { 1086 return getBooleanProperty(PROPERTY_PASSWORD); 1087 } 1088 1089 /** 1090 * Sets whether this node is a password. 1091 * <p> 1092 * <strong>Note:</strong> Cannot be called from an 1093 * {@link android.accessibilityservice.AccessibilityService}. 1094 * This class is made immutable before being delivered to an AccessibilityService. 1095 * </p> 1096 * 1097 * @param password True if the node is a password. 1098 * 1099 * @throws IllegalStateException If called from an AccessibilityService. 1100 */ 1101 public void setPassword(boolean password) { 1102 setBooleanProperty(PROPERTY_PASSWORD, password); 1103 } 1104 1105 /** 1106 * Gets if the node is scrollable. 1107 * 1108 * @return True if the node is scrollable, false otherwise. 1109 */ 1110 public boolean isScrollable() { 1111 return getBooleanProperty(PROPERTY_SCROLLABLE); 1112 } 1113 1114 /** 1115 * Sets if the node is scrollable. 1116 * <p> 1117 * <strong>Note:</strong> Cannot be called from an 1118 * {@link android.accessibilityservice.AccessibilityService}. 1119 * This class is made immutable before being delivered to an AccessibilityService. 1120 * </p> 1121 * 1122 * @param scrollable True if the node is scrollable, false otherwise. 1123 * 1124 * @throws IllegalStateException If called from an AccessibilityService. 1125 */ 1126 public void setScrollable(boolean scrollable) { 1127 enforceNotSealed(); 1128 setBooleanProperty(PROPERTY_SCROLLABLE, scrollable); 1129 } 1130 1131 /** 1132 * Gets the package this node comes from. 1133 * 1134 * @return The package name. 1135 */ 1136 public CharSequence getPackageName() { 1137 return mPackageName; 1138 } 1139 1140 /** 1141 * Sets the package this node comes from. 1142 * <p> 1143 * <strong>Note:</strong> Cannot be called from an 1144 * {@link android.accessibilityservice.AccessibilityService}. 1145 * This class is made immutable before being delivered to an AccessibilityService. 1146 * </p> 1147 * 1148 * @param packageName The package name. 1149 * 1150 * @throws IllegalStateException If called from an AccessibilityService. 1151 */ 1152 public void setPackageName(CharSequence packageName) { 1153 enforceNotSealed(); 1154 mPackageName = packageName; 1155 } 1156 1157 /** 1158 * Gets the class this node comes from. 1159 * 1160 * @return The class name. 1161 */ 1162 public CharSequence getClassName() { 1163 return mClassName; 1164 } 1165 1166 /** 1167 * Sets the class this node comes from. 1168 * <p> 1169 * <strong>Note:</strong> Cannot be called from an 1170 * {@link android.accessibilityservice.AccessibilityService}. 1171 * This class is made immutable before being delivered to an AccessibilityService. 1172 * </p> 1173 * 1174 * @param className The class name. 1175 * 1176 * @throws IllegalStateException If called from an AccessibilityService. 1177 */ 1178 public void setClassName(CharSequence className) { 1179 enforceNotSealed(); 1180 mClassName = className; 1181 } 1182 1183 /** 1184 * Gets the text of this node. 1185 * 1186 * @return The text. 1187 */ 1188 public CharSequence getText() { 1189 return mText; 1190 } 1191 1192 /** 1193 * Sets the text of this node. 1194 * <p> 1195 * <strong>Note:</strong> Cannot be called from an 1196 * {@link android.accessibilityservice.AccessibilityService}. 1197 * This class is made immutable before being delivered to an AccessibilityService. 1198 * </p> 1199 * 1200 * @param text The text. 1201 * 1202 * @throws IllegalStateException If called from an AccessibilityService. 1203 */ 1204 public void setText(CharSequence text) { 1205 enforceNotSealed(); 1206 mText = text; 1207 } 1208 1209 /** 1210 * Gets the content description of this node. 1211 * 1212 * @return The content description. 1213 */ 1214 public CharSequence getContentDescription() { 1215 return mContentDescription; 1216 } 1217 1218 /** 1219 * Sets the content description of this node. 1220 * <p> 1221 * <strong>Note:</strong> Cannot be called from an 1222 * {@link android.accessibilityservice.AccessibilityService}. 1223 * This class is made immutable before being delivered to an AccessibilityService. 1224 * </p> 1225 * 1226 * @param contentDescription The content description. 1227 * 1228 * @throws IllegalStateException If called from an AccessibilityService. 1229 */ 1230 public void setContentDescription(CharSequence contentDescription) { 1231 enforceNotSealed(); 1232 mContentDescription = contentDescription; 1233 } 1234 1235 /** 1236 * Gets the value of a boolean property. 1237 * 1238 * @param property The property. 1239 * @return The value. 1240 */ 1241 private boolean getBooleanProperty(int property) { 1242 return (mBooleanProperties & property) != 0; 1243 } 1244 1245 /** 1246 * Sets a boolean property. 1247 * 1248 * @param property The property. 1249 * @param value The value. 1250 * 1251 * @throws IllegalStateException If called from an AccessibilityService. 1252 */ 1253 private void setBooleanProperty(int property, boolean value) { 1254 enforceNotSealed(); 1255 if (value) { 1256 mBooleanProperties |= property; 1257 } else { 1258 mBooleanProperties &= ~property; 1259 } 1260 } 1261 1262 /** 1263 * Sets the unique id of the IAccessibilityServiceConnection over which 1264 * this instance can send requests to the system. 1265 * 1266 * @param connectionId The connection id. 1267 * 1268 * @hide 1269 */ 1270 public void setConnectionId(int connectionId) { 1271 enforceNotSealed(); 1272 mConnectionId = connectionId; 1273 } 1274 1275 /** 1276 * {@inheritDoc} 1277 */ 1278 public int describeContents() { 1279 return 0; 1280 } 1281 1282 /** 1283 * Gets the id of the source node. 1284 * 1285 * @return The id. 1286 * 1287 * @hide 1288 */ 1289 public long getSourceNodeId() { 1290 return mSourceNodeId; 1291 } 1292 1293 /** 1294 * Sets if this instance is sealed. 1295 * 1296 * @param sealed Whether is sealed. 1297 * 1298 * @hide 1299 */ 1300 public void setSealed(boolean sealed) { 1301 mSealed = sealed; 1302 } 1303 1304 /** 1305 * Gets if this instance is sealed. 1306 * 1307 * @return Whether is sealed. 1308 * 1309 * @hide 1310 */ 1311 public boolean isSealed() { 1312 return mSealed; 1313 } 1314 1315 /** 1316 * Enforces that this instance is sealed. 1317 * 1318 * @throws IllegalStateException If this instance is not sealed. 1319 * 1320 * @hide 1321 */ 1322 protected void enforceSealed() { 1323 if (!isSealed()) { 1324 throw new IllegalStateException("Cannot perform this " 1325 + "action on a not sealed instance."); 1326 } 1327 } 1328 1329 private void enforceValidFocusDirection(int direction) { 1330 switch (direction) { 1331 case View.FOCUS_DOWN: 1332 case View.FOCUS_UP: 1333 case View.FOCUS_LEFT: 1334 case View.FOCUS_RIGHT: 1335 case View.FOCUS_FORWARD: 1336 case View.FOCUS_BACKWARD: 1337 return; 1338 default: 1339 throw new IllegalArgumentException("Unknown direction: " + direction); 1340 } 1341 } 1342 1343 private void enforceValidFocusType(int focusType) { 1344 switch (focusType) { 1345 case FOCUS_INPUT: 1346 case FOCUS_ACCESSIBILITY: 1347 return; 1348 default: 1349 throw new IllegalArgumentException("Unknown focus type: " + focusType); 1350 } 1351 } 1352 1353 /** 1354 * Enforces that this instance is not sealed. 1355 * 1356 * @throws IllegalStateException If this instance is sealed. 1357 * 1358 * @hide 1359 */ 1360 protected void enforceNotSealed() { 1361 if (isSealed()) { 1362 throw new IllegalStateException("Cannot perform this " 1363 + "action on a sealed instance."); 1364 } 1365 } 1366 1367 /** 1368 * Returns a cached instance if such is available otherwise a new one 1369 * and sets the source. 1370 * 1371 * @param source The source view. 1372 * @return An instance. 1373 * 1374 * @see #setSource(View) 1375 */ 1376 public static AccessibilityNodeInfo obtain(View source) { 1377 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 1378 info.setSource(source); 1379 return info; 1380 } 1381 1382 /** 1383 * Returns a cached instance if such is available otherwise a new one 1384 * and sets the source. 1385 * 1386 * @param root The root of the virtual subtree. 1387 * @param virtualDescendantId The id of the virtual descendant. 1388 * @return An instance. 1389 * 1390 * @see #setSource(View, int) 1391 */ 1392 public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) { 1393 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 1394 info.setSource(root, virtualDescendantId); 1395 return info; 1396 } 1397 1398 /** 1399 * Returns a cached instance if such is available otherwise a new one. 1400 * 1401 * @return An instance. 1402 */ 1403 public static AccessibilityNodeInfo obtain() { 1404 synchronized (sPoolLock) { 1405 if (sPool != null) { 1406 AccessibilityNodeInfo info = sPool; 1407 sPool = sPool.mNext; 1408 sPoolSize--; 1409 info.mNext = null; 1410 info.mIsInPool = false; 1411 return info; 1412 } 1413 return new AccessibilityNodeInfo(); 1414 } 1415 } 1416 1417 /** 1418 * Returns a cached instance if such is available or a new one is 1419 * create. The returned instance is initialized from the given 1420 * <code>info</code>. 1421 * 1422 * @param info The other info. 1423 * @return An instance. 1424 */ 1425 public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) { 1426 AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain(); 1427 infoClone.init(info); 1428 return infoClone; 1429 } 1430 1431 /** 1432 * Return an instance back to be reused. 1433 * <p> 1434 * <strong>Note:</strong> You must not touch the object after calling this function. 1435 * 1436 * @throws IllegalStateException If the info is already recycled. 1437 */ 1438 public void recycle() { 1439 if (mIsInPool) { 1440 throw new IllegalStateException("Info already recycled!"); 1441 } 1442 clear(); 1443 synchronized (sPoolLock) { 1444 if (sPoolSize <= MAX_POOL_SIZE) { 1445 mNext = sPool; 1446 sPool = this; 1447 mIsInPool = true; 1448 sPoolSize++; 1449 } 1450 } 1451 } 1452 1453 /** 1454 * {@inheritDoc} 1455 * <p> 1456 * <strong>Note:</strong> After the instance is written to a parcel it 1457 * is recycled. You must not touch the object after calling this function. 1458 * </p> 1459 */ 1460 public void writeToParcel(Parcel parcel, int flags) { 1461 parcel.writeInt(isSealed() ? 1 : 0); 1462 parcel.writeLong(mSourceNodeId); 1463 parcel.writeInt(mWindowId); 1464 parcel.writeLong(mParentNodeId); 1465 parcel.writeInt(mConnectionId); 1466 1467 SparseLongArray childIds = mChildNodeIds; 1468 final int childIdsSize = childIds.size(); 1469 parcel.writeInt(childIdsSize); 1470 for (int i = 0; i < childIdsSize; i++) { 1471 parcel.writeLong(childIds.valueAt(i)); 1472 } 1473 1474 parcel.writeInt(mBoundsInParent.top); 1475 parcel.writeInt(mBoundsInParent.bottom); 1476 parcel.writeInt(mBoundsInParent.left); 1477 parcel.writeInt(mBoundsInParent.right); 1478 1479 parcel.writeInt(mBoundsInScreen.top); 1480 parcel.writeInt(mBoundsInScreen.bottom); 1481 parcel.writeInt(mBoundsInScreen.left); 1482 parcel.writeInt(mBoundsInScreen.right); 1483 1484 parcel.writeInt(mActions); 1485 1486 parcel.writeInt(mMovementGranularities); 1487 1488 parcel.writeInt(mBooleanProperties); 1489 1490 parcel.writeCharSequence(mPackageName); 1491 parcel.writeCharSequence(mClassName); 1492 parcel.writeCharSequence(mText); 1493 parcel.writeCharSequence(mContentDescription); 1494 1495 // Since instances of this class are fetched via synchronous i.e. blocking 1496 // calls in IPCs we always recycle as soon as the instance is marshaled. 1497 recycle(); 1498 } 1499 1500 /** 1501 * Initializes this instance from another one. 1502 * 1503 * @param other The other instance. 1504 */ 1505 @SuppressWarnings("unchecked") 1506 private void init(AccessibilityNodeInfo other) { 1507 mSealed = other.mSealed; 1508 mSourceNodeId = other.mSourceNodeId; 1509 mParentNodeId = other.mParentNodeId; 1510 mWindowId = other.mWindowId; 1511 mConnectionId = other.mConnectionId; 1512 mBoundsInParent.set(other.mBoundsInParent); 1513 mBoundsInScreen.set(other.mBoundsInScreen); 1514 mPackageName = other.mPackageName; 1515 mClassName = other.mClassName; 1516 mText = other.mText; 1517 mContentDescription = other.mContentDescription; 1518 mActions= other.mActions; 1519 mBooleanProperties = other.mBooleanProperties; 1520 mMovementGranularities = other.mMovementGranularities; 1521 final int otherChildIdCount = other.mChildNodeIds.size(); 1522 for (int i = 0; i < otherChildIdCount; i++) { 1523 mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i)); 1524 } 1525 } 1526 1527 /** 1528 * Creates a new instance from a {@link Parcel}. 1529 * 1530 * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}. 1531 */ 1532 private void initFromParcel(Parcel parcel) { 1533 mSealed = (parcel.readInt() == 1); 1534 mSourceNodeId = parcel.readLong(); 1535 mWindowId = parcel.readInt(); 1536 mParentNodeId = parcel.readLong(); 1537 mConnectionId = parcel.readInt(); 1538 1539 SparseLongArray childIds = mChildNodeIds; 1540 final int childrenSize = parcel.readInt(); 1541 for (int i = 0; i < childrenSize; i++) { 1542 final long childId = parcel.readLong(); 1543 childIds.put(i, childId); 1544 } 1545 1546 mBoundsInParent.top = parcel.readInt(); 1547 mBoundsInParent.bottom = parcel.readInt(); 1548 mBoundsInParent.left = parcel.readInt(); 1549 mBoundsInParent.right = parcel.readInt(); 1550 1551 mBoundsInScreen.top = parcel.readInt(); 1552 mBoundsInScreen.bottom = parcel.readInt(); 1553 mBoundsInScreen.left = parcel.readInt(); 1554 mBoundsInScreen.right = parcel.readInt(); 1555 1556 mActions = parcel.readInt(); 1557 1558 mMovementGranularities = parcel.readInt(); 1559 1560 mBooleanProperties = parcel.readInt(); 1561 1562 mPackageName = parcel.readCharSequence(); 1563 mClassName = parcel.readCharSequence(); 1564 mText = parcel.readCharSequence(); 1565 mContentDescription = parcel.readCharSequence(); 1566 } 1567 1568 /** 1569 * Clears the state of this instance. 1570 */ 1571 private void clear() { 1572 mSealed = false; 1573 mSourceNodeId = ROOT_NODE_ID; 1574 mParentNodeId = ROOT_NODE_ID; 1575 mWindowId = UNDEFINED; 1576 mConnectionId = UNDEFINED; 1577 mMovementGranularities = 0; 1578 mChildNodeIds.clear(); 1579 mBoundsInParent.set(0, 0, 0, 0); 1580 mBoundsInScreen.set(0, 0, 0, 0); 1581 mBooleanProperties = 0; 1582 mPackageName = null; 1583 mClassName = null; 1584 mText = null; 1585 mContentDescription = null; 1586 mActions = 0; 1587 } 1588 1589 /** 1590 * Gets the human readable action symbolic name. 1591 * 1592 * @param action The action. 1593 * @return The symbolic name. 1594 */ 1595 private static String getActionSymbolicName(int action) { 1596 switch (action) { 1597 case ACTION_FOCUS: 1598 return "ACTION_FOCUS"; 1599 case ACTION_CLEAR_FOCUS: 1600 return "ACTION_CLEAR_FOCUS"; 1601 case ACTION_SELECT: 1602 return "ACTION_SELECT"; 1603 case ACTION_CLEAR_SELECTION: 1604 return "ACTION_CLEAR_SELECTION"; 1605 case ACTION_CLICK: 1606 return "ACTION_CLICK"; 1607 case ACTION_LONG_CLICK: 1608 return "ACTION_LONG_CLICK"; 1609 case ACTION_ACCESSIBILITY_FOCUS: 1610 return "ACTION_ACCESSIBILITY_FOCUS"; 1611 case ACTION_CLEAR_ACCESSIBILITY_FOCUS: 1612 return "ACTION_CLEAR_ACCESSIBILITY_FOCUS"; 1613 case ACTION_NEXT_AT_MOVEMENT_GRANULARITY: 1614 return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY"; 1615 case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: 1616 return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY"; 1617 case ACTION_NEXT_HTML_ELEMENT: 1618 return "ACTION_NEXT_HTML_ELEMENT"; 1619 case ACTION_PREVIOUS_HTML_ELEMENT: 1620 return "ACTION_PREVIOUS_HTML_ELEMENT"; 1621 case ACTION_SCROLL_FORWARD: 1622 return "ACTION_SCROLL_FORWARD"; 1623 case ACTION_SCROLL_BACKWARD: 1624 return "ACTION_SCROLL_BACKWARD"; 1625 default: 1626 throw new IllegalArgumentException("Unknown action: " + action); 1627 } 1628 } 1629 1630 /** 1631 * Gets the human readable movement granularity symbolic name. 1632 * 1633 * @param granularity The granularity. 1634 * @return The symbolic name. 1635 */ 1636 private static String getMovementGranularitySymbolicName(int granularity) { 1637 switch (granularity) { 1638 case MOVEMENT_GRANULARITY_CHARACTER: 1639 return "MOVEMENT_GRANULARITY_CHARACTER"; 1640 case MOVEMENT_GRANULARITY_WORD: 1641 return "MOVEMENT_GRANULARITY_WORD"; 1642 case MOVEMENT_GRANULARITY_LINE: 1643 return "MOVEMENT_GRANULARITY_LINE"; 1644 case MOVEMENT_GRANULARITY_PARAGRAPH: 1645 return "MOVEMENT_GRANULARITY_PARAGRAPH"; 1646 case MOVEMENT_GRANULARITY_PAGE: 1647 return "MOVEMENT_GRANULARITY_PAGE"; 1648 default: 1649 throw new IllegalArgumentException("Unknown movement granularity: " + granularity); 1650 } 1651 } 1652 1653 private boolean canPerformRequestOverConnection(long accessibilityNodeId) { 1654 return (mWindowId != UNDEFINED 1655 && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED 1656 && mConnectionId != UNDEFINED); 1657 } 1658 1659 @Override 1660 public boolean equals(Object object) { 1661 if (this == object) { 1662 return true; 1663 } 1664 if (object == null) { 1665 return false; 1666 } 1667 if (getClass() != object.getClass()) { 1668 return false; 1669 } 1670 AccessibilityNodeInfo other = (AccessibilityNodeInfo) object; 1671 if (mSourceNodeId != other.mSourceNodeId) { 1672 return false; 1673 } 1674 if (mWindowId != other.mWindowId) { 1675 return false; 1676 } 1677 return true; 1678 } 1679 1680 @Override 1681 public int hashCode() { 1682 final int prime = 31; 1683 int result = 1; 1684 result = prime * result + getAccessibilityViewId(mSourceNodeId); 1685 result = prime * result + getVirtualDescendantId(mSourceNodeId); 1686 result = prime * result + mWindowId; 1687 return result; 1688 } 1689 1690 @Override 1691 public String toString() { 1692 StringBuilder builder = new StringBuilder(); 1693 builder.append(super.toString()); 1694 1695 if (DEBUG) { 1696 builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId)); 1697 builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId)); 1698 builder.append("; mParentNodeId: " + mParentNodeId); 1699 1700 int granularities = mMovementGranularities; 1701 builder.append("; MovementGranularities: ["); 1702 while (granularities != 0) { 1703 final int granularity = 1 << Integer.numberOfTrailingZeros(granularities); 1704 granularities &= ~granularity; 1705 builder.append(getMovementGranularitySymbolicName(granularity)); 1706 if (granularities != 0) { 1707 builder.append(", "); 1708 } 1709 } 1710 builder.append("]"); 1711 1712 SparseLongArray childIds = mChildNodeIds; 1713 builder.append("; childAccessibilityIds: ["); 1714 for (int i = 0, count = childIds.size(); i < count; i++) { 1715 builder.append(childIds.valueAt(i)); 1716 if (i < count - 1) { 1717 builder.append(", "); 1718 } 1719 } 1720 builder.append("]"); 1721 } 1722 1723 builder.append("; boundsInParent: " + mBoundsInParent); 1724 builder.append("; boundsInScreen: " + mBoundsInScreen); 1725 1726 builder.append("; packageName: ").append(mPackageName); 1727 builder.append("; className: ").append(mClassName); 1728 builder.append("; text: ").append(mText); 1729 builder.append("; contentDescription: ").append(mContentDescription); 1730 1731 builder.append("; checkable: ").append(isCheckable()); 1732 builder.append("; checked: ").append(isChecked()); 1733 builder.append("; focusable: ").append(isFocusable()); 1734 builder.append("; focused: ").append(isFocused()); 1735 builder.append("; selected: ").append(isSelected()); 1736 builder.append("; clickable: ").append(isClickable()); 1737 builder.append("; longClickable: ").append(isLongClickable()); 1738 builder.append("; enabled: ").append(isEnabled()); 1739 builder.append("; password: ").append(isPassword()); 1740 builder.append("; scrollable: " + isScrollable()); 1741 1742 builder.append("; ["); 1743 for (int actionBits = mActions; actionBits != 0;) { 1744 final int action = 1 << Integer.numberOfTrailingZeros(actionBits); 1745 actionBits &= ~action; 1746 builder.append(getActionSymbolicName(action)); 1747 if (actionBits != 0) { 1748 builder.append(", "); 1749 } 1750 } 1751 builder.append("]"); 1752 1753 return builder.toString(); 1754 } 1755 1756 /** 1757 * @see Parcelable.Creator 1758 */ 1759 public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR = 1760 new Parcelable.Creator<AccessibilityNodeInfo>() { 1761 public AccessibilityNodeInfo createFromParcel(Parcel parcel) { 1762 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 1763 info.initFromParcel(parcel); 1764 return info; 1765 } 1766 1767 public AccessibilityNodeInfo[] newArray(int size) { 1768 return new AccessibilityNodeInfo[size]; 1769 } 1770 }; 1771} 1772