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