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