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