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 17 package android.view.accessibility; 18 19 import android.accessibilityservice.AccessibilityServiceInfo; 20 import android.graphics.Rect; 21 import android.os.Bundle; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 import android.util.Pools.SynchronizedPool; 25 import android.util.SparseLongArray; 26 import android.view.View; 27 28 import java.util.Collections; 29 import java.util.List; 30 31 /** 32 * This class represents a node of the window content as well as actions that 33 * can be requested from its source. From the point of view of an 34 * {@link android.accessibilityservice.AccessibilityService} a window content is 35 * presented as tree of accessibility node info which may or may not map one-to-one 36 * to the view hierarchy. In other words, a custom view is free to report itself as 37 * a tree of accessibility node info. 38 * </p> 39 * <p> 40 * Once an accessibility node info is delivered to an accessibility service it is 41 * made immutable and calling a state mutation method generates an error. 42 * </p> 43 * <p> 44 * Please refer to {@link android.accessibilityservice.AccessibilityService} for 45 * details about how to obtain a handle to window content as a tree of accessibility 46 * node info as well as familiarizing with the security model. 47 * </p> 48 * <div class="special reference"> 49 * <h3>Developer Guides</h3> 50 * <p>For more information about making applications accessible, read the 51 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> 52 * developer guide.</p> 53 * </div> 54 * 55 * @see android.accessibilityservice.AccessibilityService 56 * @see AccessibilityEvent 57 * @see AccessibilityManager 58 */ 59 public class AccessibilityNodeInfo implements Parcelable { 60 61 private static final boolean DEBUG = false; 62 63 /** @hide */ 64 public static final int UNDEFINED = -1; 65 66 /** @hide */ 67 public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED, UNDEFINED); 68 69 /** @hide */ 70 public static final int ACTIVE_WINDOW_ID = UNDEFINED; 71 72 /** @hide */ 73 public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001; 74 75 /** @hide */ 76 public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002; 77 78 /** @hide */ 79 public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004; 80 81 /** @hide */ 82 public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008; 83 84 /** @hide */ 85 public static final int FLAG_REPORT_VIEW_IDS = 0x00000010; 86 87 // Actions. 88 89 /** 90 * Action that gives input focus to the node. 91 */ 92 public static final int ACTION_FOCUS = 0x00000001; 93 94 /** 95 * Action that clears input focus of the node. 96 */ 97 public static final int ACTION_CLEAR_FOCUS = 0x00000002; 98 99 /** 100 * Action that selects the node. 101 */ 102 public static final int ACTION_SELECT = 0x00000004; 103 104 /** 105 * Action that unselects the node. 106 */ 107 public static final int ACTION_CLEAR_SELECTION = 0x00000008; 108 109 /** 110 * Action that clicks on the node info. 111 */ 112 public static final int ACTION_CLICK = 0x00000010; 113 114 /** 115 * Action that long clicks on the node. 116 */ 117 public static final int ACTION_LONG_CLICK = 0x00000020; 118 119 /** 120 * Action that gives accessibility focus to the node. 121 */ 122 public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040; 123 124 /** 125 * Action that clears accessibility focus of the node. 126 */ 127 public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080; 128 129 /** 130 * Action that requests to go to the next entity in this node's text 131 * at a given movement granularity. For example, move to the next character, 132 * word, etc. 133 * <p> 134 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<, 135 * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br> 136 * <strong>Example:</strong> Move to the previous character and do not extend selection. 137 * <code><pre><p> 138 * Bundle arguments = new Bundle(); 139 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, 140 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); 141 * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, 142 * false); 143 * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments); 144 * </code></pre></p> 145 * </p> 146 * 147 * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT 148 * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 149 * 150 * @see #setMovementGranularities(int) 151 * @see #getMovementGranularities() 152 * 153 * @see #MOVEMENT_GRANULARITY_CHARACTER 154 * @see #MOVEMENT_GRANULARITY_WORD 155 * @see #MOVEMENT_GRANULARITY_LINE 156 * @see #MOVEMENT_GRANULARITY_PARAGRAPH 157 * @see #MOVEMENT_GRANULARITY_PAGE 158 */ 159 public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100; 160 161 /** 162 * Action that requests to go to the previous entity in this node's text 163 * at a given movement granularity. For example, move to the next character, 164 * word, etc. 165 * <p> 166 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<, 167 * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br> 168 * <strong>Example:</strong> Move to the next character and do not extend selection. 169 * <code><pre><p> 170 * Bundle arguments = new Bundle(); 171 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, 172 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); 173 * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, 174 * false); 175 * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, 176 * arguments); 177 * </code></pre></p> 178 * </p> 179 * 180 * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT 181 * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 182 * 183 * @see #setMovementGranularities(int) 184 * @see #getMovementGranularities() 185 * 186 * @see #MOVEMENT_GRANULARITY_CHARACTER 187 * @see #MOVEMENT_GRANULARITY_WORD 188 * @see #MOVEMENT_GRANULARITY_LINE 189 * @see #MOVEMENT_GRANULARITY_PARAGRAPH 190 * @see #MOVEMENT_GRANULARITY_PAGE 191 */ 192 public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200; 193 194 /** 195 * Action to move to the next HTML element of a given type. For example, move 196 * to the BUTTON, INPUT, TABLE, etc. 197 * <p> 198 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> 199 * <strong>Example:</strong> 200 * <code><pre><p> 201 * Bundle arguments = new Bundle(); 202 * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); 203 * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments); 204 * </code></pre></p> 205 * </p> 206 */ 207 public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400; 208 209 /** 210 * Action to move to the previous HTML element of a given type. For example, move 211 * to the BUTTON, INPUT, TABLE, etc. 212 * <p> 213 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> 214 * <strong>Example:</strong> 215 * <code><pre><p> 216 * Bundle arguments = new Bundle(); 217 * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); 218 * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments); 219 * </code></pre></p> 220 * </p> 221 */ 222 public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800; 223 224 /** 225 * Action to scroll the node content forward. 226 */ 227 public static final int ACTION_SCROLL_FORWARD = 0x00001000; 228 229 /** 230 * Action to scroll the node content backward. 231 */ 232 public static final int ACTION_SCROLL_BACKWARD = 0x00002000; 233 234 /** 235 * Action to copy the current selection to the clipboard. 236 */ 237 public static final int ACTION_COPY = 0x00004000; 238 239 /** 240 * Action to paste the current clipboard content. 241 */ 242 public static final int ACTION_PASTE = 0x00008000; 243 244 /** 245 * Action to cut the current selection and place it to the clipboard. 246 */ 247 public static final int ACTION_CUT = 0x00010000; 248 249 /** 250 * Action to set the selection. Performing this action with no arguments 251 * clears the selection. 252 * <p> 253 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SELECTION_START_INT}, 254 * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br> 255 * <strong>Example:</strong> 256 * <code><pre><p> 257 * Bundle arguments = new Bundle(); 258 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1); 259 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2); 260 * info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments); 261 * </code></pre></p> 262 * </p> 263 * 264 * @see #ACTION_ARGUMENT_SELECTION_START_INT 265 * @see #ACTION_ARGUMENT_SELECTION_END_INT 266 */ 267 public static final int ACTION_SET_SELECTION = 0x00020000; 268 269 /** 270 * Argument for which movement granularity to be used when traversing the node text. 271 * <p> 272 * <strong>Type:</strong> int<br> 273 * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY}, 274 * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY} 275 * </p> 276 * 277 * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY 278 * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY 279 */ 280 public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = 281 "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT"; 282 283 /** 284 * Argument for which HTML element to get moving to the next/previous HTML element. 285 * <p> 286 * <strong>Type:</strong> String<br> 287 * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT}, 288 * {@link #ACTION_PREVIOUS_HTML_ELEMENT} 289 * </p> 290 * 291 * @see #ACTION_NEXT_HTML_ELEMENT 292 * @see #ACTION_PREVIOUS_HTML_ELEMENT 293 */ 294 public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = 295 "ACTION_ARGUMENT_HTML_ELEMENT_STRING"; 296 297 /** 298 * Argument for whether when moving at granularity to extend the selection 299 * or to move it otherwise. 300 * <p> 301 * <strong>Type:</strong> boolean<br> 302 * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY}, 303 * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY} 304 * </p> 305 * 306 * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY 307 * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY 308 */ 309 public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = 310 "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN"; 311 312 /** 313 * Argument for specifying the selection start. 314 * <p> 315 * <strong>Type:</strong> int<br> 316 * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION} 317 * </p> 318 * 319 * @see #ACTION_SET_SELECTION 320 */ 321 public static final String ACTION_ARGUMENT_SELECTION_START_INT = 322 "ACTION_ARGUMENT_SELECTION_START_INT"; 323 324 /** 325 * Argument for specifying the selection end. 326 * <p> 327 * <strong>Type:</strong> int<br> 328 * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION} 329 * </p> 330 * 331 * @see #ACTION_SET_SELECTION 332 */ 333 public static final String ACTION_ARGUMENT_SELECTION_END_INT = 334 "ACTION_ARGUMENT_SELECTION_END_INT"; 335 336 /** 337 * The input focus. 338 */ 339 public static final int FOCUS_INPUT = 1; 340 341 /** 342 * The accessibility focus. 343 */ 344 public static final int FOCUS_ACCESSIBILITY = 2; 345 346 // Movement granularities 347 348 /** 349 * Movement granularity bit for traversing the text of a node by character. 350 */ 351 public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001; 352 353 /** 354 * Movement granularity bit for traversing the text of a node by word. 355 */ 356 public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002; 357 358 /** 359 * Movement granularity bit for traversing the text of a node by line. 360 */ 361 public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004; 362 363 /** 364 * Movement granularity bit for traversing the text of a node by paragraph. 365 */ 366 public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008; 367 368 /** 369 * Movement granularity bit for traversing the text of a node by page. 370 */ 371 public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010; 372 373 // Boolean attributes. 374 375 private static final int BOOLEAN_PROPERTY_CHECKABLE = 0x00000001; 376 377 private static final int BOOLEAN_PROPERTY_CHECKED = 0x00000002; 378 379 private static final int BOOLEAN_PROPERTY_FOCUSABLE = 0x00000004; 380 381 private static final int BOOLEAN_PROPERTY_FOCUSED = 0x00000008; 382 383 private static final int BOOLEAN_PROPERTY_SELECTED = 0x00000010; 384 385 private static final int BOOLEAN_PROPERTY_CLICKABLE = 0x00000020; 386 387 private static final int BOOLEAN_PROPERTY_LONG_CLICKABLE = 0x00000040; 388 389 private static final int BOOLEAN_PROPERTY_ENABLED = 0x00000080; 390 391 private static final int BOOLEAN_PROPERTY_PASSWORD = 0x00000100; 392 393 private static final int BOOLEAN_PROPERTY_SCROLLABLE = 0x00000200; 394 395 private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400; 396 397 private static final int BOOLEAN_PROPERTY_VISIBLE_TO_USER = 0x00000800; 398 399 private static final int BOOLEAN_PROPERTY_EDITABLE = 0x00001000; 400 401 /** 402 * Bits that provide the id of a virtual descendant of a view. 403 */ 404 private static final long VIRTUAL_DESCENDANT_ID_MASK = 0xffffffff00000000L; 405 406 /** 407 * Bit shift of {@link #VIRTUAL_DESCENDANT_ID_MASK} to get to the id for a 408 * virtual descendant of a view. Such a descendant does not exist in the view 409 * hierarchy and is only reported via the accessibility APIs. 410 */ 411 private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32; 412 413 /** 414 * Gets the accessibility view id which identifies a View in the view three. 415 * 416 * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}. 417 * @return The accessibility view id part of the node id. 418 * 419 * @hide 420 */ 421 public static int getAccessibilityViewId(long accessibilityNodeId) { 422 return (int) accessibilityNodeId; 423 } 424 425 /** 426 * Gets the virtual descendant id which identifies an imaginary view in a 427 * containing View. 428 * 429 * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}. 430 * @return The virtual view id part of the node id. 431 * 432 * @hide 433 */ 434 public static int getVirtualDescendantId(long accessibilityNodeId) { 435 return (int) ((accessibilityNodeId & VIRTUAL_DESCENDANT_ID_MASK) 436 >> VIRTUAL_DESCENDANT_ID_SHIFT); 437 } 438 439 /** 440 * Makes a node id by shifting the <code>virtualDescendantId</code> 441 * by {@link #VIRTUAL_DESCENDANT_ID_SHIFT} and taking 442 * the bitwise or with the <code>accessibilityViewId</code>. 443 * 444 * @param accessibilityViewId A View accessibility id. 445 * @param virtualDescendantId A virtual descendant id. 446 * @return The node id. 447 * 448 * @hide 449 */ 450 public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) { 451 return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId; 452 } 453 454 // Housekeeping. 455 private static final int MAX_POOL_SIZE = 50; 456 private static final SynchronizedPool<AccessibilityNodeInfo> sPool = 457 new SynchronizedPool<AccessibilityNodeInfo>(MAX_POOL_SIZE); 458 459 private boolean mSealed; 460 461 // Data. 462 private int mWindowId = UNDEFINED; 463 private long mSourceNodeId = ROOT_NODE_ID; 464 private long mParentNodeId = ROOT_NODE_ID; 465 private long mLabelForId = ROOT_NODE_ID; 466 private long mLabeledById = ROOT_NODE_ID; 467 468 private int mBooleanProperties; 469 private final Rect mBoundsInParent = new Rect(); 470 private final Rect mBoundsInScreen = new Rect(); 471 472 private CharSequence mPackageName; 473 private CharSequence mClassName; 474 private CharSequence mText; 475 private CharSequence mContentDescription; 476 private String mViewIdResourceName; 477 478 private final SparseLongArray mChildNodeIds = new SparseLongArray(); 479 private int mActions; 480 481 private int mMovementGranularities; 482 483 private int mTextSelectionStart = UNDEFINED; 484 private int mTextSelectionEnd = UNDEFINED; 485 486 private int mConnectionId = UNDEFINED; 487 488 /** 489 * Hide constructor from clients. 490 */ 491 private AccessibilityNodeInfo() { 492 /* do nothing */ 493 } 494 495 /** 496 * Sets the source. 497 * <p> 498 * <strong>Note:</strong> Cannot be called from an 499 * {@link android.accessibilityservice.AccessibilityService}. 500 * This class is made immutable before being delivered to an AccessibilityService. 501 * </p> 502 * 503 * @param source The info source. 504 */ 505 public void setSource(View source) { 506 setSource(source, UNDEFINED); 507 } 508 509 /** 510 * Sets the source to be a virtual descendant of the given <code>root</code>. 511 * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root 512 * is set as the source. 513 * <p> 514 * A virtual descendant is an imaginary View that is reported as a part of the view 515 * hierarchy for accessibility purposes. This enables custom views that draw complex 516 * content to report themselves as a tree of virtual views, thus conveying their 517 * logical structure. 518 * </p> 519 * <p> 520 * <strong>Note:</strong> Cannot be called from an 521 * {@link android.accessibilityservice.AccessibilityService}. 522 * This class is made immutable before being delivered to an AccessibilityService. 523 * </p> 524 * 525 * @param root The root of the virtual subtree. 526 * @param virtualDescendantId The id of the virtual descendant. 527 */ 528 public void setSource(View root, int virtualDescendantId) { 529 enforceNotSealed(); 530 mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED; 531 final int rootAccessibilityViewId = 532 (root != null) ? root.getAccessibilityViewId() : UNDEFINED; 533 mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 534 } 535 536 /** 537 * Find the view that has the specified focus type. The search starts from 538 * the view represented by this node info. 539 * 540 * @param focus The focus to find. One of {@link #FOCUS_INPUT} or 541 * {@link #FOCUS_ACCESSIBILITY}. 542 * @return The node info of the focused view or null. 543 * 544 * @see #FOCUS_INPUT 545 * @see #FOCUS_ACCESSIBILITY 546 */ 547 public AccessibilityNodeInfo findFocus(int focus) { 548 enforceSealed(); 549 enforceValidFocusType(focus); 550 if (!canPerformRequestOverConnection(mSourceNodeId)) { 551 return null; 552 } 553 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId, 554 mSourceNodeId, focus); 555 } 556 557 /** 558 * Searches for the nearest view in the specified direction that can take 559 * the input focus. 560 * 561 * @param direction The direction. Can be one of: 562 * {@link View#FOCUS_DOWN}, 563 * {@link View#FOCUS_UP}, 564 * {@link View#FOCUS_LEFT}, 565 * {@link View#FOCUS_RIGHT}, 566 * {@link View#FOCUS_FORWARD}, 567 * {@link View#FOCUS_BACKWARD}. 568 * 569 * @return The node info for the view that can take accessibility focus. 570 */ 571 public AccessibilityNodeInfo focusSearch(int direction) { 572 enforceSealed(); 573 enforceValidFocusDirection(direction); 574 if (!canPerformRequestOverConnection(mSourceNodeId)) { 575 return null; 576 } 577 return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId, 578 mSourceNodeId, direction); 579 } 580 581 /** 582 * Gets the id of the window from which the info comes from. 583 * 584 * @return The window id. 585 */ 586 public int getWindowId() { 587 return mWindowId; 588 } 589 590 /** 591 * Refreshes this info with the latest state of the view it represents. 592 * <p> 593 * <strong>Note:</strong> If this method returns false this info is obsolete 594 * since it represents a view that is no longer in the view tree and should 595 * be recycled. 596 * </p> 597 * @return Whether the refresh succeeded. 598 */ 599 public boolean refresh() { 600 enforceSealed(); 601 if (!canPerformRequestOverConnection(mSourceNodeId)) { 602 return false; 603 } 604 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 605 AccessibilityNodeInfo refreshedInfo = client.findAccessibilityNodeInfoByAccessibilityId( 606 mConnectionId, mWindowId, mSourceNodeId, 0); 607 if (refreshedInfo == null) { 608 return false; 609 } 610 init(refreshedInfo); 611 refreshedInfo.recycle(); 612 return true; 613 } 614 615 /** 616 * @return The ids of the children. 617 * 618 * @hide 619 */ 620 public SparseLongArray getChildNodeIds() { 621 return mChildNodeIds; 622 } 623 624 /** 625 * Gets the number of children. 626 * 627 * @return The child count. 628 */ 629 public int getChildCount() { 630 return mChildNodeIds.size(); 631 } 632 633 /** 634 * Get the child at given index. 635 * <p> 636 * <strong>Note:</strong> It is a client responsibility to recycle the 637 * received info by calling {@link AccessibilityNodeInfo#recycle()} 638 * to avoid creating of multiple instances. 639 * </p> 640 * 641 * @param index The child index. 642 * @return The child node. 643 * 644 * @throws IllegalStateException If called outside of an AccessibilityService. 645 * 646 */ 647 public AccessibilityNodeInfo getChild(int index) { 648 enforceSealed(); 649 if (!canPerformRequestOverConnection(mSourceNodeId)) { 650 return null; 651 } 652 final long childId = mChildNodeIds.get(index); 653 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 654 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId, 655 childId, FLAG_PREFETCH_DESCENDANTS); 656 } 657 658 /** 659 * Adds a child. 660 * <p> 661 * <strong>Note:</strong> Cannot be called from an 662 * {@link android.accessibilityservice.AccessibilityService}. 663 * This class is made immutable before being delivered to an AccessibilityService. 664 * </p> 665 * 666 * @param child The child. 667 * 668 * @throws IllegalStateException If called from an AccessibilityService. 669 */ 670 public void addChild(View child) { 671 addChild(child, UNDEFINED); 672 } 673 674 /** 675 * Adds a virtual child which is a descendant of the given <code>root</code>. 676 * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root 677 * is added as a child. 678 * <p> 679 * A virtual descendant is an imaginary View that is reported as a part of the view 680 * hierarchy for accessibility purposes. This enables custom views that draw complex 681 * content to report them selves as a tree of virtual views, thus conveying their 682 * logical structure. 683 * </p> 684 * 685 * @param root The root of the virtual subtree. 686 * @param virtualDescendantId The id of the virtual child. 687 */ 688 public void addChild(View root, int virtualDescendantId) { 689 enforceNotSealed(); 690 final int index = mChildNodeIds.size(); 691 final int rootAccessibilityViewId = 692 (root != null) ? root.getAccessibilityViewId() : UNDEFINED; 693 final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 694 mChildNodeIds.put(index, childNodeId); 695 } 696 697 /** 698 * Gets the actions that can be performed on the node. 699 * 700 * @return The bit mask of with actions. 701 * 702 * @see AccessibilityNodeInfo#ACTION_FOCUS 703 * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS 704 * @see AccessibilityNodeInfo#ACTION_SELECT 705 * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION 706 * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS 707 * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS 708 * @see AccessibilityNodeInfo#ACTION_CLICK 709 * @see AccessibilityNodeInfo#ACTION_LONG_CLICK 710 * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY 711 * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY 712 * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT 713 * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT 714 * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD 715 * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD 716 */ 717 public int getActions() { 718 return mActions; 719 } 720 721 /** 722 * Adds an action that can be performed on the node. 723 * <p> 724 * <strong>Note:</strong> Cannot be called from an 725 * {@link android.accessibilityservice.AccessibilityService}. 726 * This class is made immutable before being delivered to an AccessibilityService. 727 * </p> 728 * 729 * @param action The action. 730 * 731 * @throws IllegalStateException If called from an AccessibilityService. 732 */ 733 public void addAction(int action) { 734 enforceNotSealed(); 735 mActions |= action; 736 } 737 738 /** 739 * Sets the movement granularities for traversing the text of this node. 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 granularities The bit mask with granularities. 747 * 748 * @throws IllegalStateException If called from an AccessibilityService. 749 */ 750 public void setMovementGranularities(int granularities) { 751 enforceNotSealed(); 752 mMovementGranularities = granularities; 753 } 754 755 /** 756 * Gets the movement granularities for traversing the text of this node. 757 * 758 * @return The bit mask with granularities. 759 */ 760 public int getMovementGranularities() { 761 return mMovementGranularities; 762 } 763 764 /** 765 * Performs an action on the node. 766 * <p> 767 * <strong>Note:</strong> An action can be performed only if the request is made 768 * from an {@link android.accessibilityservice.AccessibilityService}. 769 * </p> 770 * 771 * @param action The action to perform. 772 * @return True if the action was performed. 773 * 774 * @throws IllegalStateException If called outside of an AccessibilityService. 775 */ 776 public boolean performAction(int action) { 777 enforceSealed(); 778 if (!canPerformRequestOverConnection(mSourceNodeId)) { 779 return false; 780 } 781 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 782 return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, 783 action, null); 784 } 785 786 /** 787 * Performs an action on the node. 788 * <p> 789 * <strong>Note:</strong> An action can be performed only if the request is made 790 * from an {@link android.accessibilityservice.AccessibilityService}. 791 * </p> 792 * 793 * @param action The action to perform. 794 * @param arguments A bundle with additional arguments. 795 * @return True if the action was performed. 796 * 797 * @throws IllegalStateException If called outside of an AccessibilityService. 798 */ 799 public boolean performAction(int action, Bundle arguments) { 800 enforceSealed(); 801 if (!canPerformRequestOverConnection(mSourceNodeId)) { 802 return false; 803 } 804 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 805 return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, 806 action, arguments); 807 } 808 809 /** 810 * Finds {@link AccessibilityNodeInfo}s by text. The match is case 811 * insensitive containment. The search is relative to this info i.e. 812 * this info is the root of the traversed tree. 813 * 814 * <p> 815 * <strong>Note:</strong> It is a client responsibility to recycle the 816 * received info by calling {@link AccessibilityNodeInfo#recycle()} 817 * to avoid creating of multiple instances. 818 * </p> 819 * 820 * @param text The searched text. 821 * @return A list of node info. 822 */ 823 public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) { 824 enforceSealed(); 825 if (!canPerformRequestOverConnection(mSourceNodeId)) { 826 return Collections.emptyList(); 827 } 828 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 829 return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId, 830 text); 831 } 832 833 /** 834 * Finds {@link AccessibilityNodeInfo}s by the fully qualified view id's resource 835 * name where a fully qualified id is of the from "package:id/id_resource_name". 836 * For example, if the target application's package is "foo.bar" and the id 837 * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz". 838 * 839 * <p> 840 * <strong>Note:</strong> It is a client responsibility to recycle the 841 * received info by calling {@link AccessibilityNodeInfo#recycle()} 842 * to avoid creating of multiple instances. 843 * </p> 844 * <p> 845 * <strong>Note:</strong> The primary usage of this API is for UI test automation 846 * and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo} 847 * the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} 848 * flag when configuring his {@link android.accessibilityservice.AccessibilityService}. 849 * </p> 850 * 851 * @param viewId The fully qualified resource name of the view id to find. 852 * @return A list of node info. 853 */ 854 public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) { 855 enforceSealed(); 856 if (!canPerformRequestOverConnection(mSourceNodeId)) { 857 return Collections.emptyList(); 858 } 859 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 860 return client.findAccessibilityNodeInfosByViewId(mConnectionId, mWindowId, mSourceNodeId, 861 viewId); 862 } 863 864 /** 865 * Gets the parent. 866 * <p> 867 * <strong>Note:</strong> It is a client responsibility to recycle the 868 * received info by calling {@link AccessibilityNodeInfo#recycle()} 869 * to avoid creating of multiple instances. 870 * </p> 871 * 872 * @return The parent. 873 */ 874 public AccessibilityNodeInfo getParent() { 875 enforceSealed(); 876 if (!canPerformRequestOverConnection(mParentNodeId)) { 877 return null; 878 } 879 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 880 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, 881 mWindowId, mParentNodeId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); 882 } 883 884 /** 885 * @return The parent node id. 886 * 887 * @hide 888 */ 889 public long getParentNodeId() { 890 return mParentNodeId; 891 } 892 893 /** 894 * Sets the parent. 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 parent The parent. 902 * 903 * @throws IllegalStateException If called from an AccessibilityService. 904 */ 905 public void setParent(View parent) { 906 setParent(parent, UNDEFINED); 907 } 908 909 /** 910 * Sets the parent to be a virtual descendant of the given <code>root</code>. 911 * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root 912 * is set as the parent. 913 * <p> 914 * A virtual descendant is an imaginary View that is reported as a part of the view 915 * hierarchy for accessibility purposes. This enables custom views that draw complex 916 * content to report them selves as a tree of virtual views, thus conveying their 917 * logical structure. 918 * </p> 919 * <p> 920 * <strong>Note:</strong> Cannot be called from an 921 * {@link android.accessibilityservice.AccessibilityService}. 922 * This class is made immutable before being delivered to an AccessibilityService. 923 * </p> 924 * 925 * @param root The root of the virtual subtree. 926 * @param virtualDescendantId The id of the virtual descendant. 927 */ 928 public void setParent(View root, int virtualDescendantId) { 929 enforceNotSealed(); 930 final int rootAccessibilityViewId = 931 (root != null) ? root.getAccessibilityViewId() : UNDEFINED; 932 mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 933 } 934 935 /** 936 * Gets the node bounds in parent coordinates. 937 * 938 * @param outBounds The output node bounds. 939 */ 940 public void getBoundsInParent(Rect outBounds) { 941 outBounds.set(mBoundsInParent.left, mBoundsInParent.top, 942 mBoundsInParent.right, mBoundsInParent.bottom); 943 } 944 945 /** 946 * Sets the node bounds in parent coordinates. 947 * <p> 948 * <strong>Note:</strong> Cannot be called from an 949 * {@link android.accessibilityservice.AccessibilityService}. 950 * This class is made immutable before being delivered to an AccessibilityService. 951 * </p> 952 * 953 * @param bounds The node bounds. 954 * 955 * @throws IllegalStateException If called from an AccessibilityService. 956 */ 957 public void setBoundsInParent(Rect bounds) { 958 enforceNotSealed(); 959 mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom); 960 } 961 962 /** 963 * Gets the node bounds in screen coordinates. 964 * 965 * @param outBounds The output node bounds. 966 */ 967 public void getBoundsInScreen(Rect outBounds) { 968 outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top, 969 mBoundsInScreen.right, mBoundsInScreen.bottom); 970 } 971 972 /** 973 * Sets the node bounds in screen coordinates. 974 * <p> 975 * <strong>Note:</strong> Cannot be called from an 976 * {@link android.accessibilityservice.AccessibilityService}. 977 * This class is made immutable before being delivered to an AccessibilityService. 978 * </p> 979 * 980 * @param bounds The node bounds. 981 * 982 * @throws IllegalStateException If called from an AccessibilityService. 983 */ 984 public void setBoundsInScreen(Rect bounds) { 985 enforceNotSealed(); 986 mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom); 987 } 988 989 /** 990 * Gets whether this node is checkable. 991 * 992 * @return True if the node is checkable. 993 */ 994 public boolean isCheckable() { 995 return getBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE); 996 } 997 998 /** 999 * Sets whether this node is checkable. 1000 * <p> 1001 * <strong>Note:</strong> Cannot be called from an 1002 * {@link android.accessibilityservice.AccessibilityService}. 1003 * This class is made immutable before being delivered to an AccessibilityService. 1004 * </p> 1005 * 1006 * @param checkable True if the node is checkable. 1007 * 1008 * @throws IllegalStateException If called from an AccessibilityService. 1009 */ 1010 public void setCheckable(boolean checkable) { 1011 setBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE, checkable); 1012 } 1013 1014 /** 1015 * Gets whether this node is checked. 1016 * 1017 * @return True if the node is checked. 1018 */ 1019 public boolean isChecked() { 1020 return getBooleanProperty(BOOLEAN_PROPERTY_CHECKED); 1021 } 1022 1023 /** 1024 * Sets whether this node is checked. 1025 * <p> 1026 * <strong>Note:</strong> Cannot be called from an 1027 * {@link android.accessibilityservice.AccessibilityService}. 1028 * This class is made immutable before being delivered to an AccessibilityService. 1029 * </p> 1030 * 1031 * @param checked True if the node is checked. 1032 * 1033 * @throws IllegalStateException If called from an AccessibilityService. 1034 */ 1035 public void setChecked(boolean checked) { 1036 setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked); 1037 } 1038 1039 /** 1040 * Gets whether this node is focusable. 1041 * 1042 * @return True if the node is focusable. 1043 */ 1044 public boolean isFocusable() { 1045 return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE); 1046 } 1047 1048 /** 1049 * Sets whether this node is focusable. 1050 * <p> 1051 * <strong>Note:</strong> Cannot be called from an 1052 * {@link android.accessibilityservice.AccessibilityService}. 1053 * This class is made immutable before being delivered to an AccessibilityService. 1054 * </p> 1055 * 1056 * @param focusable True if the node is focusable. 1057 * 1058 * @throws IllegalStateException If called from an AccessibilityService. 1059 */ 1060 public void setFocusable(boolean focusable) { 1061 setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable); 1062 } 1063 1064 /** 1065 * Gets whether this node is focused. 1066 * 1067 * @return True if the node is focused. 1068 */ 1069 public boolean isFocused() { 1070 return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED); 1071 } 1072 1073 /** 1074 * Sets whether this node is focused. 1075 * <p> 1076 * <strong>Note:</strong> Cannot be called from an 1077 * {@link android.accessibilityservice.AccessibilityService}. 1078 * This class is made immutable before being delivered to an AccessibilityService. 1079 * </p> 1080 * 1081 * @param focused True if the node is focused. 1082 * 1083 * @throws IllegalStateException If called from an AccessibilityService. 1084 */ 1085 public void setFocused(boolean focused) { 1086 setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused); 1087 } 1088 1089 /** 1090 * Sets whether this node is visible to the user. 1091 * 1092 * @return Whether the node is visible to the user. 1093 */ 1094 public boolean isVisibleToUser() { 1095 return getBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER); 1096 } 1097 1098 /** 1099 * Sets whether this node is visible to the user. 1100 * <p> 1101 * <strong>Note:</strong> Cannot be called from an 1102 * {@link android.accessibilityservice.AccessibilityService}. 1103 * This class is made immutable before being delivered to an AccessibilityService. 1104 * </p> 1105 * 1106 * @param visibleToUser Whether the node is visible to the user. 1107 * 1108 * @throws IllegalStateException If called from an AccessibilityService. 1109 */ 1110 public void setVisibleToUser(boolean visibleToUser) { 1111 setBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER, visibleToUser); 1112 } 1113 1114 /** 1115 * Gets whether this node is accessibility focused. 1116 * 1117 * @return True if the node is accessibility focused. 1118 */ 1119 public boolean isAccessibilityFocused() { 1120 return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED); 1121 } 1122 1123 /** 1124 * Sets whether this node is accessibility focused. 1125 * <p> 1126 * <strong>Note:</strong> Cannot be called from an 1127 * {@link android.accessibilityservice.AccessibilityService}. 1128 * This class is made immutable before being delivered to an AccessibilityService. 1129 * </p> 1130 * 1131 * @param focused True if the node is accessibility focused. 1132 * 1133 * @throws IllegalStateException If called from an AccessibilityService. 1134 */ 1135 public void setAccessibilityFocused(boolean focused) { 1136 setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused); 1137 } 1138 1139 /** 1140 * Gets whether this node is selected. 1141 * 1142 * @return True if the node is selected. 1143 */ 1144 public boolean isSelected() { 1145 return getBooleanProperty(BOOLEAN_PROPERTY_SELECTED); 1146 } 1147 1148 /** 1149 * Sets whether this node is selected. 1150 * <p> 1151 * <strong>Note:</strong> Cannot be called from an 1152 * {@link android.accessibilityservice.AccessibilityService}. 1153 * This class is made immutable before being delivered to an AccessibilityService. 1154 * </p> 1155 * 1156 * @param selected True if the node is selected. 1157 * 1158 * @throws IllegalStateException If called from an AccessibilityService. 1159 */ 1160 public void setSelected(boolean selected) { 1161 setBooleanProperty(BOOLEAN_PROPERTY_SELECTED, selected); 1162 } 1163 1164 /** 1165 * Gets whether this node is clickable. 1166 * 1167 * @return True if the node is clickable. 1168 */ 1169 public boolean isClickable() { 1170 return getBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE); 1171 } 1172 1173 /** 1174 * Sets whether this node is clickable. 1175 * <p> 1176 * <strong>Note:</strong> Cannot be called from an 1177 * {@link android.accessibilityservice.AccessibilityService}. 1178 * This class is made immutable before being delivered to an AccessibilityService. 1179 * </p> 1180 * 1181 * @param clickable True if the node is clickable. 1182 * 1183 * @throws IllegalStateException If called from an AccessibilityService. 1184 */ 1185 public void setClickable(boolean clickable) { 1186 setBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE, clickable); 1187 } 1188 1189 /** 1190 * Gets whether this node is long clickable. 1191 * 1192 * @return True if the node is long clickable. 1193 */ 1194 public boolean isLongClickable() { 1195 return getBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE); 1196 } 1197 1198 /** 1199 * Sets whether this node is long clickable. 1200 * <p> 1201 * <strong>Note:</strong> Cannot be called from an 1202 * {@link android.accessibilityservice.AccessibilityService}. 1203 * This class is made immutable before being delivered to an AccessibilityService. 1204 * </p> 1205 * 1206 * @param longClickable True if the node is long clickable. 1207 * 1208 * @throws IllegalStateException If called from an AccessibilityService. 1209 */ 1210 public void setLongClickable(boolean longClickable) { 1211 setBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE, longClickable); 1212 } 1213 1214 /** 1215 * Gets whether this node is enabled. 1216 * 1217 * @return True if the node is enabled. 1218 */ 1219 public boolean isEnabled() { 1220 return getBooleanProperty(BOOLEAN_PROPERTY_ENABLED); 1221 } 1222 1223 /** 1224 * Sets whether this node is enabled. 1225 * <p> 1226 * <strong>Note:</strong> Cannot be called from an 1227 * {@link android.accessibilityservice.AccessibilityService}. 1228 * This class is made immutable before being delivered to an AccessibilityService. 1229 * </p> 1230 * 1231 * @param enabled True if the node is enabled. 1232 * 1233 * @throws IllegalStateException If called from an AccessibilityService. 1234 */ 1235 public void setEnabled(boolean enabled) { 1236 setBooleanProperty(BOOLEAN_PROPERTY_ENABLED, enabled); 1237 } 1238 1239 /** 1240 * Gets whether this node is a password. 1241 * 1242 * @return True if the node is a password. 1243 */ 1244 public boolean isPassword() { 1245 return getBooleanProperty(BOOLEAN_PROPERTY_PASSWORD); 1246 } 1247 1248 /** 1249 * Sets whether this node is a password. 1250 * <p> 1251 * <strong>Note:</strong> Cannot be called from an 1252 * {@link android.accessibilityservice.AccessibilityService}. 1253 * This class is made immutable before being delivered to an AccessibilityService. 1254 * </p> 1255 * 1256 * @param password True if the node is a password. 1257 * 1258 * @throws IllegalStateException If called from an AccessibilityService. 1259 */ 1260 public void setPassword(boolean password) { 1261 setBooleanProperty(BOOLEAN_PROPERTY_PASSWORD, password); 1262 } 1263 1264 /** 1265 * Gets if the node is scrollable. 1266 * 1267 * @return True if the node is scrollable, false otherwise. 1268 */ 1269 public boolean isScrollable() { 1270 return getBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE); 1271 } 1272 1273 /** 1274 * Sets if the node is scrollable. 1275 * <p> 1276 * <strong>Note:</strong> Cannot be called from an 1277 * {@link android.accessibilityservice.AccessibilityService}. 1278 * This class is made immutable before being delivered to an AccessibilityService. 1279 * </p> 1280 * 1281 * @param scrollable True if the node is scrollable, false otherwise. 1282 * 1283 * @throws IllegalStateException If called from an AccessibilityService. 1284 */ 1285 public void setScrollable(boolean scrollable) { 1286 enforceNotSealed(); 1287 setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable); 1288 } 1289 1290 /** 1291 * Gets if the node is editable. 1292 * 1293 * @return True if the node is editable, false otherwise. 1294 */ 1295 public boolean isEditable() { 1296 return getBooleanProperty(BOOLEAN_PROPERTY_EDITABLE); 1297 } 1298 1299 /** 1300 * Sets whether this node is editable. 1301 * <p> 1302 * <strong>Note:</strong> Cannot be called from an 1303 * {@link android.accessibilityservice.AccessibilityService}. 1304 * This class is made immutable before being delivered to an AccessibilityService. 1305 * </p> 1306 * 1307 * @param editable True if the node is editable. 1308 * 1309 * @throws IllegalStateException If called from an AccessibilityService. 1310 */ 1311 public void setEditable(boolean editable) { 1312 setBooleanProperty(BOOLEAN_PROPERTY_EDITABLE, editable); 1313 } 1314 1315 /** 1316 * Gets the package this node comes from. 1317 * 1318 * @return The package name. 1319 */ 1320 public CharSequence getPackageName() { 1321 return mPackageName; 1322 } 1323 1324 /** 1325 * Sets the package this node comes from. 1326 * <p> 1327 * <strong>Note:</strong> Cannot be called from an 1328 * {@link android.accessibilityservice.AccessibilityService}. 1329 * This class is made immutable before being delivered to an AccessibilityService. 1330 * </p> 1331 * 1332 * @param packageName The package name. 1333 * 1334 * @throws IllegalStateException If called from an AccessibilityService. 1335 */ 1336 public void setPackageName(CharSequence packageName) { 1337 enforceNotSealed(); 1338 mPackageName = packageName; 1339 } 1340 1341 /** 1342 * Gets the class this node comes from. 1343 * 1344 * @return The class name. 1345 */ 1346 public CharSequence getClassName() { 1347 return mClassName; 1348 } 1349 1350 /** 1351 * Sets the class this node comes from. 1352 * <p> 1353 * <strong>Note:</strong> Cannot be called from an 1354 * {@link android.accessibilityservice.AccessibilityService}. 1355 * This class is made immutable before being delivered to an AccessibilityService. 1356 * </p> 1357 * 1358 * @param className The class name. 1359 * 1360 * @throws IllegalStateException If called from an AccessibilityService. 1361 */ 1362 public void setClassName(CharSequence className) { 1363 enforceNotSealed(); 1364 mClassName = className; 1365 } 1366 1367 /** 1368 * Gets the text of this node. 1369 * 1370 * @return The text. 1371 */ 1372 public CharSequence getText() { 1373 return mText; 1374 } 1375 1376 /** 1377 * Sets the text of this node. 1378 * <p> 1379 * <strong>Note:</strong> Cannot be called from an 1380 * {@link android.accessibilityservice.AccessibilityService}. 1381 * This class is made immutable before being delivered to an AccessibilityService. 1382 * </p> 1383 * 1384 * @param text The text. 1385 * 1386 * @throws IllegalStateException If called from an AccessibilityService. 1387 */ 1388 public void setText(CharSequence text) { 1389 enforceNotSealed(); 1390 mText = text; 1391 } 1392 1393 /** 1394 * Gets the content description of this node. 1395 * 1396 * @return The content description. 1397 */ 1398 public CharSequence getContentDescription() { 1399 return mContentDescription; 1400 } 1401 1402 /** 1403 * Sets the content description of this node. 1404 * <p> 1405 * <strong>Note:</strong> Cannot be called from an 1406 * {@link android.accessibilityservice.AccessibilityService}. 1407 * This class is made immutable before being delivered to an AccessibilityService. 1408 * </p> 1409 * 1410 * @param contentDescription The content description. 1411 * 1412 * @throws IllegalStateException If called from an AccessibilityService. 1413 */ 1414 public void setContentDescription(CharSequence contentDescription) { 1415 enforceNotSealed(); 1416 mContentDescription = contentDescription; 1417 } 1418 1419 /** 1420 * Sets the view for which the view represented by this info serves as a 1421 * label for accessibility purposes. 1422 * 1423 * @param labeled The view for which this info serves as a label. 1424 */ 1425 public void setLabelFor(View labeled) { 1426 setLabelFor(labeled, UNDEFINED); 1427 } 1428 1429 /** 1430 * Sets the view for which the view represented by this info serves as a 1431 * label for accessibility purposes. If <code>virtualDescendantId</code> 1432 * is {@link View#NO_ID} the root is set as the labeled. 1433 * <p> 1434 * A virtual descendant is an imaginary View that is reported as a part of the view 1435 * hierarchy for accessibility purposes. This enables custom views that draw complex 1436 * content to report themselves as a tree of virtual views, thus conveying their 1437 * logical structure. 1438 * </p> 1439 * <p> 1440 * <strong>Note:</strong> Cannot be called from an 1441 * {@link android.accessibilityservice.AccessibilityService}. 1442 * This class is made immutable before being delivered to an AccessibilityService. 1443 * </p> 1444 * 1445 * @param root The root whose virtual descendant serves as a label. 1446 * @param virtualDescendantId The id of the virtual descendant. 1447 */ 1448 public void setLabelFor(View root, int virtualDescendantId) { 1449 enforceNotSealed(); 1450 final int rootAccessibilityViewId = (root != null) 1451 ? root.getAccessibilityViewId() : UNDEFINED; 1452 mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 1453 } 1454 1455 /** 1456 * Gets the node info for which the view represented by this info serves as 1457 * a label for accessibility purposes. 1458 * <p> 1459 * <strong>Note:</strong> It is a client responsibility to recycle the 1460 * received info by calling {@link AccessibilityNodeInfo#recycle()} 1461 * to avoid creating of multiple instances. 1462 * </p> 1463 * 1464 * @return The labeled info. 1465 */ 1466 public AccessibilityNodeInfo getLabelFor() { 1467 enforceSealed(); 1468 if (!canPerformRequestOverConnection(mLabelForId)) { 1469 return null; 1470 } 1471 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 1472 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, 1473 mWindowId, mLabelForId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); 1474 } 1475 1476 /** 1477 * Sets the view which serves as the label of the view represented by 1478 * this info for accessibility purposes. 1479 * 1480 * @param label The view that labels this node's source. 1481 */ 1482 public void setLabeledBy(View label) { 1483 setLabeledBy(label, UNDEFINED); 1484 } 1485 1486 /** 1487 * Sets the view which serves as the label of the view represented by 1488 * this info for accessibility purposes. If <code>virtualDescendantId</code> 1489 * is {@link View#NO_ID} the root is set as the label. 1490 * <p> 1491 * A virtual descendant is an imaginary View that is reported as a part of the view 1492 * hierarchy for accessibility purposes. This enables custom views that draw complex 1493 * content to report themselves as a tree of virtual views, thus conveying their 1494 * logical structure. 1495 * </p> 1496 * <p> 1497 * <strong>Note:</strong> Cannot be called from an 1498 * {@link android.accessibilityservice.AccessibilityService}. 1499 * This class is made immutable before being delivered to an AccessibilityService. 1500 * </p> 1501 * 1502 * @param root The root whose virtual descendant labels this node's source. 1503 * @param virtualDescendantId The id of the virtual descendant. 1504 */ 1505 public void setLabeledBy(View root, int virtualDescendantId) { 1506 enforceNotSealed(); 1507 final int rootAccessibilityViewId = (root != null) 1508 ? root.getAccessibilityViewId() : UNDEFINED; 1509 mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 1510 } 1511 1512 /** 1513 * Gets the node info which serves as the label of the view represented by 1514 * this info for accessibility purposes. 1515 * <p> 1516 * <strong>Note:</strong> It is a client responsibility to recycle the 1517 * received info by calling {@link AccessibilityNodeInfo#recycle()} 1518 * to avoid creating of multiple instances. 1519 * </p> 1520 * 1521 * @return The label. 1522 */ 1523 public AccessibilityNodeInfo getLabeledBy() { 1524 enforceSealed(); 1525 if (!canPerformRequestOverConnection(mLabeledById)) { 1526 return null; 1527 } 1528 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 1529 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, 1530 mWindowId, mLabeledById, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); 1531 } 1532 1533 /** 1534 * Sets the fully qualified resource name of the source view's id. 1535 * 1536 * <p> 1537 * <strong>Note:</strong> Cannot be called from an 1538 * {@link android.accessibilityservice.AccessibilityService}. 1539 * This class is made immutable before being delivered to an AccessibilityService. 1540 * </p> 1541 * 1542 * @param viewIdResName The id resource name. 1543 */ 1544 public void setViewIdResourceName(String viewIdResName) { 1545 enforceNotSealed(); 1546 mViewIdResourceName = viewIdResName; 1547 } 1548 1549 /** 1550 * Gets the fully qualified resource name of the source view's id. 1551 * 1552 * <p> 1553 * <strong>Note:</strong> The primary usage of this API is for UI test automation 1554 * and in order to report the source view id of an {@link AccessibilityNodeInfo} the 1555 * client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} 1556 * flag when configuring his {@link android.accessibilityservice.AccessibilityService}. 1557 * </p> 1558 1559 * @return The id resource name. 1560 */ 1561 public String getViewIdResourceName() { 1562 return mViewIdResourceName; 1563 } 1564 1565 /** 1566 * Gets the text selection start. 1567 * 1568 * @return The text selection start if there is selection or -1. 1569 */ 1570 public int getTextSelectionStart() { 1571 return mTextSelectionStart; 1572 } 1573 1574 /** 1575 * Gets the text selection end. 1576 * 1577 * @return The text selection end if there is selection or -1. 1578 */ 1579 public int getTextSelectionEnd() { 1580 return mTextSelectionEnd; 1581 } 1582 1583 /** 1584 * Sets the text selection start and end. 1585 * <p> 1586 * <strong>Note:</strong> Cannot be called from an 1587 * {@link android.accessibilityservice.AccessibilityService}. 1588 * This class is made immutable before being delivered to an AccessibilityService. 1589 * </p> 1590 * 1591 * @param start The text selection start. 1592 * @param end The text selection end. 1593 * 1594 * @throws IllegalStateException If called from an AccessibilityService. 1595 */ 1596 public void setTextSelection(int start, int end) { 1597 enforceNotSealed(); 1598 mTextSelectionStart = start; 1599 mTextSelectionEnd = end; 1600 } 1601 1602 /** 1603 * Gets the value of a boolean property. 1604 * 1605 * @param property The property. 1606 * @return The value. 1607 */ 1608 private boolean getBooleanProperty(int property) { 1609 return (mBooleanProperties & property) != 0; 1610 } 1611 1612 /** 1613 * Sets a boolean property. 1614 * 1615 * @param property The property. 1616 * @param value The value. 1617 * 1618 * @throws IllegalStateException If called from an AccessibilityService. 1619 */ 1620 private void setBooleanProperty(int property, boolean value) { 1621 enforceNotSealed(); 1622 if (value) { 1623 mBooleanProperties |= property; 1624 } else { 1625 mBooleanProperties &= ~property; 1626 } 1627 } 1628 1629 /** 1630 * Sets the unique id of the IAccessibilityServiceConnection over which 1631 * this instance can send requests to the system. 1632 * 1633 * @param connectionId The connection id. 1634 * 1635 * @hide 1636 */ 1637 public void setConnectionId(int connectionId) { 1638 enforceNotSealed(); 1639 mConnectionId = connectionId; 1640 } 1641 1642 /** 1643 * {@inheritDoc} 1644 */ 1645 public int describeContents() { 1646 return 0; 1647 } 1648 1649 /** 1650 * Gets the id of the source node. 1651 * 1652 * @return The id. 1653 * 1654 * @hide 1655 */ 1656 public long getSourceNodeId() { 1657 return mSourceNodeId; 1658 } 1659 1660 /** 1661 * Sets if this instance is sealed. 1662 * 1663 * @param sealed Whether is sealed. 1664 * 1665 * @hide 1666 */ 1667 public void setSealed(boolean sealed) { 1668 mSealed = sealed; 1669 } 1670 1671 /** 1672 * Gets if this instance is sealed. 1673 * 1674 * @return Whether is sealed. 1675 * 1676 * @hide 1677 */ 1678 public boolean isSealed() { 1679 return mSealed; 1680 } 1681 1682 /** 1683 * Enforces that this instance is sealed. 1684 * 1685 * @throws IllegalStateException If this instance is not sealed. 1686 * 1687 * @hide 1688 */ 1689 protected void enforceSealed() { 1690 if (!isSealed()) { 1691 throw new IllegalStateException("Cannot perform this " 1692 + "action on a not sealed instance."); 1693 } 1694 } 1695 1696 private void enforceValidFocusDirection(int direction) { 1697 switch (direction) { 1698 case View.FOCUS_DOWN: 1699 case View.FOCUS_UP: 1700 case View.FOCUS_LEFT: 1701 case View.FOCUS_RIGHT: 1702 case View.FOCUS_FORWARD: 1703 case View.FOCUS_BACKWARD: 1704 return; 1705 default: 1706 throw new IllegalArgumentException("Unknown direction: " + direction); 1707 } 1708 } 1709 1710 private void enforceValidFocusType(int focusType) { 1711 switch (focusType) { 1712 case FOCUS_INPUT: 1713 case FOCUS_ACCESSIBILITY: 1714 return; 1715 default: 1716 throw new IllegalArgumentException("Unknown focus type: " + focusType); 1717 } 1718 } 1719 1720 /** 1721 * Enforces that this instance is not sealed. 1722 * 1723 * @throws IllegalStateException If this instance is sealed. 1724 * 1725 * @hide 1726 */ 1727 protected void enforceNotSealed() { 1728 if (isSealed()) { 1729 throw new IllegalStateException("Cannot perform this " 1730 + "action on a sealed instance."); 1731 } 1732 } 1733 1734 /** 1735 * Returns a cached instance if such is available otherwise a new one 1736 * and sets the source. 1737 * 1738 * @param source The source view. 1739 * @return An instance. 1740 * 1741 * @see #setSource(View) 1742 */ 1743 public static AccessibilityNodeInfo obtain(View source) { 1744 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 1745 info.setSource(source); 1746 return info; 1747 } 1748 1749 /** 1750 * Returns a cached instance if such is available otherwise a new one 1751 * and sets the source. 1752 * 1753 * @param root The root of the virtual subtree. 1754 * @param virtualDescendantId The id of the virtual descendant. 1755 * @return An instance. 1756 * 1757 * @see #setSource(View, int) 1758 */ 1759 public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) { 1760 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 1761 info.setSource(root, virtualDescendantId); 1762 return info; 1763 } 1764 1765 /** 1766 * Returns a cached instance if such is available otherwise a new one. 1767 * 1768 * @return An instance. 1769 */ 1770 public static AccessibilityNodeInfo obtain() { 1771 AccessibilityNodeInfo info = sPool.acquire(); 1772 return (info != null) ? info : new AccessibilityNodeInfo(); 1773 } 1774 1775 /** 1776 * Returns a cached instance if such is available or a new one is 1777 * create. The returned instance is initialized from the given 1778 * <code>info</code>. 1779 * 1780 * @param info The other info. 1781 * @return An instance. 1782 */ 1783 public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) { 1784 AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain(); 1785 infoClone.init(info); 1786 return infoClone; 1787 } 1788 1789 /** 1790 * Return an instance back to be reused. 1791 * <p> 1792 * <strong>Note:</strong> You must not touch the object after calling this function. 1793 * 1794 * @throws IllegalStateException If the info is already recycled. 1795 */ 1796 public void recycle() { 1797 clear(); 1798 sPool.release(this); 1799 } 1800 1801 /** 1802 * {@inheritDoc} 1803 * <p> 1804 * <strong>Note:</strong> After the instance is written to a parcel it 1805 * is recycled. You must not touch the object after calling this function. 1806 * </p> 1807 */ 1808 public void writeToParcel(Parcel parcel, int flags) { 1809 parcel.writeInt(isSealed() ? 1 : 0); 1810 parcel.writeLong(mSourceNodeId); 1811 parcel.writeInt(mWindowId); 1812 parcel.writeLong(mParentNodeId); 1813 parcel.writeLong(mLabelForId); 1814 parcel.writeLong(mLabeledById); 1815 parcel.writeInt(mConnectionId); 1816 1817 SparseLongArray childIds = mChildNodeIds; 1818 final int childIdsSize = childIds.size(); 1819 parcel.writeInt(childIdsSize); 1820 for (int i = 0; i < childIdsSize; i++) { 1821 parcel.writeLong(childIds.valueAt(i)); 1822 } 1823 1824 parcel.writeInt(mBoundsInParent.top); 1825 parcel.writeInt(mBoundsInParent.bottom); 1826 parcel.writeInt(mBoundsInParent.left); 1827 parcel.writeInt(mBoundsInParent.right); 1828 1829 parcel.writeInt(mBoundsInScreen.top); 1830 parcel.writeInt(mBoundsInScreen.bottom); 1831 parcel.writeInt(mBoundsInScreen.left); 1832 parcel.writeInt(mBoundsInScreen.right); 1833 1834 parcel.writeInt(mActions); 1835 1836 parcel.writeInt(mMovementGranularities); 1837 1838 parcel.writeInt(mBooleanProperties); 1839 1840 parcel.writeCharSequence(mPackageName); 1841 parcel.writeCharSequence(mClassName); 1842 parcel.writeCharSequence(mText); 1843 parcel.writeCharSequence(mContentDescription); 1844 parcel.writeString(mViewIdResourceName); 1845 1846 parcel.writeInt(mTextSelectionStart); 1847 parcel.writeInt(mTextSelectionEnd); 1848 1849 // Since instances of this class are fetched via synchronous i.e. blocking 1850 // calls in IPCs we always recycle as soon as the instance is marshaled. 1851 recycle(); 1852 } 1853 1854 /** 1855 * Initializes this instance from another one. 1856 * 1857 * @param other The other instance. 1858 */ 1859 private void init(AccessibilityNodeInfo other) { 1860 mSealed = other.mSealed; 1861 mSourceNodeId = other.mSourceNodeId; 1862 mParentNodeId = other.mParentNodeId; 1863 mLabelForId = other.mLabelForId; 1864 mLabeledById = other.mLabeledById; 1865 mWindowId = other.mWindowId; 1866 mConnectionId = other.mConnectionId; 1867 mBoundsInParent.set(other.mBoundsInParent); 1868 mBoundsInScreen.set(other.mBoundsInScreen); 1869 mPackageName = other.mPackageName; 1870 mClassName = other.mClassName; 1871 mText = other.mText; 1872 mContentDescription = other.mContentDescription; 1873 mViewIdResourceName = other.mViewIdResourceName; 1874 mActions= other.mActions; 1875 mBooleanProperties = other.mBooleanProperties; 1876 mMovementGranularities = other.mMovementGranularities; 1877 final int otherChildIdCount = other.mChildNodeIds.size(); 1878 for (int i = 0; i < otherChildIdCount; i++) { 1879 mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i)); 1880 } 1881 mTextSelectionStart = other.mTextSelectionStart; 1882 mTextSelectionEnd = other.mTextSelectionEnd; 1883 } 1884 1885 /** 1886 * Creates a new instance from a {@link Parcel}. 1887 * 1888 * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}. 1889 */ 1890 private void initFromParcel(Parcel parcel) { 1891 mSealed = (parcel.readInt() == 1); 1892 mSourceNodeId = parcel.readLong(); 1893 mWindowId = parcel.readInt(); 1894 mParentNodeId = parcel.readLong(); 1895 mLabelForId = parcel.readLong(); 1896 mLabeledById = parcel.readLong(); 1897 mConnectionId = parcel.readInt(); 1898 1899 SparseLongArray childIds = mChildNodeIds; 1900 final int childrenSize = parcel.readInt(); 1901 for (int i = 0; i < childrenSize; i++) { 1902 final long childId = parcel.readLong(); 1903 childIds.put(i, childId); 1904 } 1905 1906 mBoundsInParent.top = parcel.readInt(); 1907 mBoundsInParent.bottom = parcel.readInt(); 1908 mBoundsInParent.left = parcel.readInt(); 1909 mBoundsInParent.right = parcel.readInt(); 1910 1911 mBoundsInScreen.top = parcel.readInt(); 1912 mBoundsInScreen.bottom = parcel.readInt(); 1913 mBoundsInScreen.left = parcel.readInt(); 1914 mBoundsInScreen.right = parcel.readInt(); 1915 1916 mActions = parcel.readInt(); 1917 1918 mMovementGranularities = parcel.readInt(); 1919 1920 mBooleanProperties = parcel.readInt(); 1921 1922 mPackageName = parcel.readCharSequence(); 1923 mClassName = parcel.readCharSequence(); 1924 mText = parcel.readCharSequence(); 1925 mContentDescription = parcel.readCharSequence(); 1926 mViewIdResourceName = parcel.readString(); 1927 1928 mTextSelectionStart = parcel.readInt(); 1929 mTextSelectionEnd = parcel.readInt(); 1930 } 1931 1932 /** 1933 * Clears the state of this instance. 1934 */ 1935 private void clear() { 1936 mSealed = false; 1937 mSourceNodeId = ROOT_NODE_ID; 1938 mParentNodeId = ROOT_NODE_ID; 1939 mLabelForId = ROOT_NODE_ID; 1940 mLabeledById = ROOT_NODE_ID; 1941 mWindowId = UNDEFINED; 1942 mConnectionId = UNDEFINED; 1943 mMovementGranularities = 0; 1944 mChildNodeIds.clear(); 1945 mBoundsInParent.set(0, 0, 0, 0); 1946 mBoundsInScreen.set(0, 0, 0, 0); 1947 mBooleanProperties = 0; 1948 mPackageName = null; 1949 mClassName = null; 1950 mText = null; 1951 mContentDescription = null; 1952 mViewIdResourceName = null; 1953 mActions = 0; 1954 mTextSelectionStart = UNDEFINED; 1955 mTextSelectionEnd = UNDEFINED; 1956 } 1957 1958 /** 1959 * Gets the human readable action symbolic name. 1960 * 1961 * @param action The action. 1962 * @return The symbolic name. 1963 */ 1964 private static String getActionSymbolicName(int action) { 1965 switch (action) { 1966 case ACTION_FOCUS: 1967 return "ACTION_FOCUS"; 1968 case ACTION_CLEAR_FOCUS: 1969 return "ACTION_CLEAR_FOCUS"; 1970 case ACTION_SELECT: 1971 return "ACTION_SELECT"; 1972 case ACTION_CLEAR_SELECTION: 1973 return "ACTION_CLEAR_SELECTION"; 1974 case ACTION_CLICK: 1975 return "ACTION_CLICK"; 1976 case ACTION_LONG_CLICK: 1977 return "ACTION_LONG_CLICK"; 1978 case ACTION_ACCESSIBILITY_FOCUS: 1979 return "ACTION_ACCESSIBILITY_FOCUS"; 1980 case ACTION_CLEAR_ACCESSIBILITY_FOCUS: 1981 return "ACTION_CLEAR_ACCESSIBILITY_FOCUS"; 1982 case ACTION_NEXT_AT_MOVEMENT_GRANULARITY: 1983 return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY"; 1984 case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: 1985 return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY"; 1986 case ACTION_NEXT_HTML_ELEMENT: 1987 return "ACTION_NEXT_HTML_ELEMENT"; 1988 case ACTION_PREVIOUS_HTML_ELEMENT: 1989 return "ACTION_PREVIOUS_HTML_ELEMENT"; 1990 case ACTION_SCROLL_FORWARD: 1991 return "ACTION_SCROLL_FORWARD"; 1992 case ACTION_SCROLL_BACKWARD: 1993 return "ACTION_SCROLL_BACKWARD"; 1994 case ACTION_CUT: 1995 return "ACTION_CUT"; 1996 case ACTION_COPY: 1997 return "ACTION_COPY"; 1998 case ACTION_PASTE: 1999 return "ACTION_PASTE"; 2000 case ACTION_SET_SELECTION: 2001 return "ACTION_SET_SELECTION"; 2002 default: 2003 return"ACTION_UNKNOWN"; 2004 } 2005 } 2006 2007 /** 2008 * Gets the human readable movement granularity symbolic name. 2009 * 2010 * @param granularity The granularity. 2011 * @return The symbolic name. 2012 */ 2013 private static String getMovementGranularitySymbolicName(int granularity) { 2014 switch (granularity) { 2015 case MOVEMENT_GRANULARITY_CHARACTER: 2016 return "MOVEMENT_GRANULARITY_CHARACTER"; 2017 case MOVEMENT_GRANULARITY_WORD: 2018 return "MOVEMENT_GRANULARITY_WORD"; 2019 case MOVEMENT_GRANULARITY_LINE: 2020 return "MOVEMENT_GRANULARITY_LINE"; 2021 case MOVEMENT_GRANULARITY_PARAGRAPH: 2022 return "MOVEMENT_GRANULARITY_PARAGRAPH"; 2023 case MOVEMENT_GRANULARITY_PAGE: 2024 return "MOVEMENT_GRANULARITY_PAGE"; 2025 default: 2026 throw new IllegalArgumentException("Unknown movement granularity: " + granularity); 2027 } 2028 } 2029 2030 private boolean canPerformRequestOverConnection(long accessibilityNodeId) { 2031 return (mWindowId != UNDEFINED 2032 && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED 2033 && mConnectionId != UNDEFINED); 2034 } 2035 2036 @Override 2037 public boolean equals(Object object) { 2038 if (this == object) { 2039 return true; 2040 } 2041 if (object == null) { 2042 return false; 2043 } 2044 if (getClass() != object.getClass()) { 2045 return false; 2046 } 2047 AccessibilityNodeInfo other = (AccessibilityNodeInfo) object; 2048 if (mSourceNodeId != other.mSourceNodeId) { 2049 return false; 2050 } 2051 if (mWindowId != other.mWindowId) { 2052 return false; 2053 } 2054 return true; 2055 } 2056 2057 @Override 2058 public int hashCode() { 2059 final int prime = 31; 2060 int result = 1; 2061 result = prime * result + getAccessibilityViewId(mSourceNodeId); 2062 result = prime * result + getVirtualDescendantId(mSourceNodeId); 2063 result = prime * result + mWindowId; 2064 return result; 2065 } 2066 2067 @Override 2068 public String toString() { 2069 StringBuilder builder = new StringBuilder(); 2070 builder.append(super.toString()); 2071 2072 if (DEBUG) { 2073 builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId)); 2074 builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId)); 2075 builder.append("; mParentNodeId: " + mParentNodeId); 2076 2077 int granularities = mMovementGranularities; 2078 builder.append("; MovementGranularities: ["); 2079 while (granularities != 0) { 2080 final int granularity = 1 << Integer.numberOfTrailingZeros(granularities); 2081 granularities &= ~granularity; 2082 builder.append(getMovementGranularitySymbolicName(granularity)); 2083 if (granularities != 0) { 2084 builder.append(", "); 2085 } 2086 } 2087 builder.append("]"); 2088 2089 SparseLongArray childIds = mChildNodeIds; 2090 builder.append("; childAccessibilityIds: ["); 2091 for (int i = 0, count = childIds.size(); i < count; i++) { 2092 builder.append(childIds.valueAt(i)); 2093 if (i < count - 1) { 2094 builder.append(", "); 2095 } 2096 } 2097 builder.append("]"); 2098 } 2099 2100 builder.append("; boundsInParent: " + mBoundsInParent); 2101 builder.append("; boundsInScreen: " + mBoundsInScreen); 2102 2103 builder.append("; packageName: ").append(mPackageName); 2104 builder.append("; className: ").append(mClassName); 2105 builder.append("; text: ").append(mText); 2106 builder.append("; contentDescription: ").append(mContentDescription); 2107 builder.append("; viewIdResName: ").append(mViewIdResourceName); 2108 2109 builder.append("; checkable: ").append(isCheckable()); 2110 builder.append("; checked: ").append(isChecked()); 2111 builder.append("; focusable: ").append(isFocusable()); 2112 builder.append("; focused: ").append(isFocused()); 2113 builder.append("; selected: ").append(isSelected()); 2114 builder.append("; clickable: ").append(isClickable()); 2115 builder.append("; longClickable: ").append(isLongClickable()); 2116 builder.append("; enabled: ").append(isEnabled()); 2117 builder.append("; password: ").append(isPassword()); 2118 builder.append("; scrollable: " + isScrollable()); 2119 2120 builder.append("; ["); 2121 for (int actionBits = mActions; actionBits != 0;) { 2122 final int action = 1 << Integer.numberOfTrailingZeros(actionBits); 2123 actionBits &= ~action; 2124 builder.append(getActionSymbolicName(action)); 2125 if (actionBits != 0) { 2126 builder.append(", "); 2127 } 2128 } 2129 builder.append("]"); 2130 2131 return builder.toString(); 2132 } 2133 2134 /** 2135 * @see Parcelable.Creator 2136 */ 2137 public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR = 2138 new Parcelable.Creator<AccessibilityNodeInfo>() { 2139 public AccessibilityNodeInfo createFromParcel(Parcel parcel) { 2140 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 2141 info.initFromParcel(parcel); 2142 return info; 2143 } 2144 2145 public AccessibilityNodeInfo[] newArray(int size) { 2146 return new AccessibilityNodeInfo[size]; 2147 } 2148 }; 2149 } 2150