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