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