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.text.InputType; 25 import android.util.Pools.SynchronizedPool; 26 import android.util.SparseLongArray; 27 import android.view.View; 28 29 import java.util.Collections; 30 import java.util.List; 31 32 /** 33 * This class represents a node of the window content as well as actions that 34 * can be requested from its source. From the point of view of an 35 * {@link android.accessibilityservice.AccessibilityService} a window content is 36 * presented as tree of accessibility node info which may or may not map one-to-one 37 * to the view hierarchy. In other words, a custom view is free to report itself as 38 * a tree of accessibility node info. 39 * </p> 40 * <p> 41 * Once an accessibility node info is delivered to an accessibility service it is 42 * made immutable and calling a state mutation method generates an error. 43 * </p> 44 * <p> 45 * Please refer to {@link android.accessibilityservice.AccessibilityService} for 46 * details about how to obtain a handle to window content as a tree of accessibility 47 * node info as well as familiarizing with the security model. 48 * </p> 49 * <div class="special reference"> 50 * <h3>Developer Guides</h3> 51 * <p>For more information about making applications accessible, read the 52 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> 53 * developer guide.</p> 54 * </div> 55 * 56 * @see android.accessibilityservice.AccessibilityService 57 * @see AccessibilityEvent 58 * @see AccessibilityManager 59 */ 60 public class AccessibilityNodeInfo implements Parcelable { 61 62 private static final boolean DEBUG = false; 63 64 /** @hide */ 65 public static final int UNDEFINED = -1; 66 67 /** @hide */ 68 public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED, UNDEFINED); 69 70 /** @hide */ 71 public static final int ACTIVE_WINDOW_ID = UNDEFINED; 72 73 /** @hide */ 74 public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001; 75 76 /** @hide */ 77 public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002; 78 79 /** @hide */ 80 public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004; 81 82 /** @hide */ 83 public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008; 84 85 /** @hide */ 86 public static final int FLAG_REPORT_VIEW_IDS = 0x00000010; 87 88 // Actions. 89 90 /** 91 * Action that gives input focus to the node. 92 */ 93 public static final int ACTION_FOCUS = 0x00000001; 94 95 /** 96 * Action that clears input focus of the node. 97 */ 98 public static final int ACTION_CLEAR_FOCUS = 0x00000002; 99 100 /** 101 * Action that selects the node. 102 */ 103 public static final int ACTION_SELECT = 0x00000004; 104 105 /** 106 * Action that unselects the node. 107 */ 108 public static final int ACTION_CLEAR_SELECTION = 0x00000008; 109 110 /** 111 * Action that clicks on the node info. 112 */ 113 public static final int ACTION_CLICK = 0x00000010; 114 115 /** 116 * Action that long clicks on the node. 117 */ 118 public static final int ACTION_LONG_CLICK = 0x00000020; 119 120 /** 121 * Action that gives accessibility focus to the node. 122 */ 123 public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040; 124 125 /** 126 * Action that clears accessibility focus of the node. 127 */ 128 public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080; 129 130 /** 131 * Action that requests to go to the next entity in this node's text 132 * at a given movement granularity. For example, move to the next character, 133 * word, etc. 134 * <p> 135 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<, 136 * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br> 137 * <strong>Example:</strong> Move to the previous character and do not extend selection. 138 * <code><pre><p> 139 * Bundle arguments = new Bundle(); 140 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, 141 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); 142 * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, 143 * false); 144 * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments); 145 * </code></pre></p> 146 * </p> 147 * 148 * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT 149 * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 150 * 151 * @see #setMovementGranularities(int) 152 * @see #getMovementGranularities() 153 * 154 * @see #MOVEMENT_GRANULARITY_CHARACTER 155 * @see #MOVEMENT_GRANULARITY_WORD 156 * @see #MOVEMENT_GRANULARITY_LINE 157 * @see #MOVEMENT_GRANULARITY_PARAGRAPH 158 * @see #MOVEMENT_GRANULARITY_PAGE 159 */ 160 public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100; 161 162 /** 163 * Action that requests to go to the previous entity in this node's text 164 * at a given movement granularity. For example, move to the next character, 165 * word, etc. 166 * <p> 167 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<, 168 * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br> 169 * <strong>Example:</strong> Move to the next character and do not extend selection. 170 * <code><pre><p> 171 * Bundle arguments = new Bundle(); 172 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, 173 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); 174 * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, 175 * false); 176 * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, 177 * arguments); 178 * </code></pre></p> 179 * </p> 180 * 181 * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT 182 * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 183 * 184 * @see #setMovementGranularities(int) 185 * @see #getMovementGranularities() 186 * 187 * @see #MOVEMENT_GRANULARITY_CHARACTER 188 * @see #MOVEMENT_GRANULARITY_WORD 189 * @see #MOVEMENT_GRANULARITY_LINE 190 * @see #MOVEMENT_GRANULARITY_PARAGRAPH 191 * @see #MOVEMENT_GRANULARITY_PAGE 192 */ 193 public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200; 194 195 /** 196 * Action to move to the next HTML element of a given type. For example, move 197 * to the BUTTON, INPUT, TABLE, etc. 198 * <p> 199 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> 200 * <strong>Example:</strong> 201 * <code><pre><p> 202 * Bundle arguments = new Bundle(); 203 * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); 204 * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments); 205 * </code></pre></p> 206 * </p> 207 */ 208 public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400; 209 210 /** 211 * Action to move to the previous HTML element of a given type. For example, move 212 * to the BUTTON, INPUT, TABLE, etc. 213 * <p> 214 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> 215 * <strong>Example:</strong> 216 * <code><pre><p> 217 * Bundle arguments = new Bundle(); 218 * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); 219 * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments); 220 * </code></pre></p> 221 * </p> 222 */ 223 public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800; 224 225 /** 226 * Action to scroll the node content forward. 227 */ 228 public static final int ACTION_SCROLL_FORWARD = 0x00001000; 229 230 /** 231 * Action to scroll the node content backward. 232 */ 233 public static final int ACTION_SCROLL_BACKWARD = 0x00002000; 234 235 /** 236 * Action to copy the current selection to the clipboard. 237 */ 238 public static final int ACTION_COPY = 0x00004000; 239 240 /** 241 * Action to paste the current clipboard content. 242 */ 243 public static final int ACTION_PASTE = 0x00008000; 244 245 /** 246 * Action to cut the current selection and place it to the clipboard. 247 */ 248 public static final int ACTION_CUT = 0x00010000; 249 250 /** 251 * Action to set the selection. Performing this action with no arguments 252 * clears the selection. 253 * <p> 254 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SELECTION_START_INT}, 255 * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br> 256 * <strong>Example:</strong> 257 * <code><pre><p> 258 * Bundle arguments = new Bundle(); 259 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1); 260 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2); 261 * info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments); 262 * </code></pre></p> 263 * </p> 264 * 265 * @see #ACTION_ARGUMENT_SELECTION_START_INT 266 * @see #ACTION_ARGUMENT_SELECTION_END_INT 267 */ 268 public static final int ACTION_SET_SELECTION = 0x00020000; 269 270 /** 271 * Action to expand an expandable node. 272 */ 273 public static final int ACTION_EXPAND = 0x00040000; 274 275 /** 276 * Action to collapse an expandable node. 277 */ 278 public static final int ACTION_COLLAPSE = 0x00080000; 279 280 /** 281 * Action to dismiss a dismissable node. 282 */ 283 public static final int ACTION_DISMISS = 0x00100000; 284 285 // Action arguments 286 287 /** 288 * Argument for which movement granularity to be used when traversing the node text. 289 * <p> 290 * <strong>Type:</strong> int<br> 291 * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY}, 292 * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY} 293 * </p> 294 * 295 * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY 296 * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY 297 */ 298 public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = 299 "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT"; 300 301 /** 302 * Argument for which HTML element to get moving to the next/previous HTML element. 303 * <p> 304 * <strong>Type:</strong> String<br> 305 * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT}, 306 * {@link #ACTION_PREVIOUS_HTML_ELEMENT} 307 * </p> 308 * 309 * @see #ACTION_NEXT_HTML_ELEMENT 310 * @see #ACTION_PREVIOUS_HTML_ELEMENT 311 */ 312 public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = 313 "ACTION_ARGUMENT_HTML_ELEMENT_STRING"; 314 315 /** 316 * Argument for whether when moving at granularity to extend the selection 317 * or to move it otherwise. 318 * <p> 319 * <strong>Type:</strong> boolean<br> 320 * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY}, 321 * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY} 322 * </p> 323 * 324 * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY 325 * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY 326 */ 327 public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = 328 "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN"; 329 330 /** 331 * Argument for specifying the selection start. 332 * <p> 333 * <strong>Type:</strong> int<br> 334 * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION} 335 * </p> 336 * 337 * @see #ACTION_SET_SELECTION 338 */ 339 public static final String ACTION_ARGUMENT_SELECTION_START_INT = 340 "ACTION_ARGUMENT_SELECTION_START_INT"; 341 342 /** 343 * Argument for specifying the selection end. 344 * <p> 345 * <strong>Type:</strong> int<br> 346 * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION} 347 * </p> 348 * 349 * @see #ACTION_SET_SELECTION 350 */ 351 public static final String ACTION_ARGUMENT_SELECTION_END_INT = 352 "ACTION_ARGUMENT_SELECTION_END_INT"; 353 354 // Focus types 355 356 /** 357 * The input focus. 358 */ 359 public static final int FOCUS_INPUT = 1; 360 361 /** 362 * The accessibility focus. 363 */ 364 public static final int FOCUS_ACCESSIBILITY = 2; 365 366 // Movement granularities 367 368 /** 369 * Movement granularity bit for traversing the text of a node by character. 370 */ 371 public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001; 372 373 /** 374 * Movement granularity bit for traversing the text of a node by word. 375 */ 376 public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002; 377 378 /** 379 * Movement granularity bit for traversing the text of a node by line. 380 */ 381 public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004; 382 383 /** 384 * Movement granularity bit for traversing the text of a node by paragraph. 385 */ 386 public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008; 387 388 /** 389 * Movement granularity bit for traversing the text of a node by page. 390 */ 391 public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010; 392 393 // Boolean attributes. 394 395 private static final int BOOLEAN_PROPERTY_CHECKABLE = 0x00000001; 396 397 private static final int BOOLEAN_PROPERTY_CHECKED = 0x00000002; 398 399 private static final int BOOLEAN_PROPERTY_FOCUSABLE = 0x00000004; 400 401 private static final int BOOLEAN_PROPERTY_FOCUSED = 0x00000008; 402 403 private static final int BOOLEAN_PROPERTY_SELECTED = 0x00000010; 404 405 private static final int BOOLEAN_PROPERTY_CLICKABLE = 0x00000020; 406 407 private static final int BOOLEAN_PROPERTY_LONG_CLICKABLE = 0x00000040; 408 409 private static final int BOOLEAN_PROPERTY_ENABLED = 0x00000080; 410 411 private static final int BOOLEAN_PROPERTY_PASSWORD = 0x00000100; 412 413 private static final int BOOLEAN_PROPERTY_SCROLLABLE = 0x00000200; 414 415 private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400; 416 417 private static final int BOOLEAN_PROPERTY_VISIBLE_TO_USER = 0x00000800; 418 419 private static final int BOOLEAN_PROPERTY_EDITABLE = 0x00001000; 420 421 private static final int BOOLEAN_PROPERTY_OPENS_POPUP = 0x00002000; 422 423 private static final int BOOLEAN_PROPERTY_DISMISSABLE = 0x00004000; 424 425 private static final int BOOLEAN_PROPERTY_MULTI_LINE = 0x00008000; 426 427 private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 0x00010000; 428 429 /** 430 * Bits that provide the id of a virtual descendant of a view. 431 */ 432 private static final long VIRTUAL_DESCENDANT_ID_MASK = 0xffffffff00000000L; 433 434 /** 435 * Bit shift of {@link #VIRTUAL_DESCENDANT_ID_MASK} to get to the id for a 436 * virtual descendant of a view. Such a descendant does not exist in the view 437 * hierarchy and is only reported via the accessibility APIs. 438 */ 439 private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32; 440 441 /** 442 * Gets the accessibility view id which identifies a View in the view three. 443 * 444 * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}. 445 * @return The accessibility view id part of the node id. 446 * 447 * @hide 448 */ 449 public static int getAccessibilityViewId(long accessibilityNodeId) { 450 return (int) accessibilityNodeId; 451 } 452 453 /** 454 * Gets the virtual descendant id which identifies an imaginary view in a 455 * containing View. 456 * 457 * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}. 458 * @return The virtual view id part of the node id. 459 * 460 * @hide 461 */ 462 public static int getVirtualDescendantId(long accessibilityNodeId) { 463 return (int) ((accessibilityNodeId & VIRTUAL_DESCENDANT_ID_MASK) 464 >> VIRTUAL_DESCENDANT_ID_SHIFT); 465 } 466 467 /** 468 * Makes a node id by shifting the <code>virtualDescendantId</code> 469 * by {@link #VIRTUAL_DESCENDANT_ID_SHIFT} and taking 470 * the bitwise or with the <code>accessibilityViewId</code>. 471 * 472 * @param accessibilityViewId A View accessibility id. 473 * @param virtualDescendantId A virtual descendant id. 474 * @return The node id. 475 * 476 * @hide 477 */ 478 public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) { 479 return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId; 480 } 481 482 // Housekeeping. 483 private static final int MAX_POOL_SIZE = 50; 484 private static final SynchronizedPool<AccessibilityNodeInfo> sPool = 485 new SynchronizedPool<AccessibilityNodeInfo>(MAX_POOL_SIZE); 486 487 private boolean mSealed; 488 489 // Data. 490 private int mWindowId = UNDEFINED; 491 private long mSourceNodeId = ROOT_NODE_ID; 492 private long mParentNodeId = ROOT_NODE_ID; 493 private long mLabelForId = ROOT_NODE_ID; 494 private long mLabeledById = ROOT_NODE_ID; 495 496 private int mBooleanProperties; 497 private final Rect mBoundsInParent = new Rect(); 498 private final Rect mBoundsInScreen = new Rect(); 499 500 private CharSequence mPackageName; 501 private CharSequence mClassName; 502 private CharSequence mText; 503 private CharSequence mContentDescription; 504 private String mViewIdResourceName; 505 506 private final SparseLongArray mChildNodeIds = new SparseLongArray(); 507 private int mActions; 508 509 private int mMovementGranularities; 510 511 private int mTextSelectionStart = UNDEFINED; 512 private int mTextSelectionEnd = UNDEFINED; 513 private int mInputType = InputType.TYPE_NULL; 514 private int mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE; 515 516 private Bundle mExtras; 517 518 private int mConnectionId = UNDEFINED; 519 520 private RangeInfo mRangeInfo; 521 private CollectionInfo mCollectionInfo; 522 private CollectionItemInfo mCollectionItemInfo; 523 524 /** 525 * Hide constructor from clients. 526 */ 527 private AccessibilityNodeInfo() { 528 /* do nothing */ 529 } 530 531 /** 532 * Sets the source. 533 * <p> 534 * <strong>Note:</strong> Cannot be called from an 535 * {@link android.accessibilityservice.AccessibilityService}. 536 * This class is made immutable before being delivered to an AccessibilityService. 537 * </p> 538 * 539 * @param source The info source. 540 */ 541 public void setSource(View source) { 542 setSource(source, UNDEFINED); 543 } 544 545 /** 546 * Sets the source to be a virtual descendant of the given <code>root</code>. 547 * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root 548 * is set as the source. 549 * <p> 550 * A virtual descendant is an imaginary View that is reported as a part of the view 551 * hierarchy for accessibility purposes. This enables custom views that draw complex 552 * content to report themselves as a tree of virtual views, thus conveying their 553 * logical structure. 554 * </p> 555 * <p> 556 * <strong>Note:</strong> Cannot be called from an 557 * {@link android.accessibilityservice.AccessibilityService}. 558 * This class is made immutable before being delivered to an AccessibilityService. 559 * </p> 560 * 561 * @param root The root of the virtual subtree. 562 * @param virtualDescendantId The id of the virtual descendant. 563 */ 564 public void setSource(View root, int virtualDescendantId) { 565 enforceNotSealed(); 566 mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED; 567 final int rootAccessibilityViewId = 568 (root != null) ? root.getAccessibilityViewId() : UNDEFINED; 569 mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 570 } 571 572 /** 573 * Find the view that has the specified focus type. The search starts from 574 * the view represented by this node info. 575 * 576 * @param focus The focus to find. One of {@link #FOCUS_INPUT} or 577 * {@link #FOCUS_ACCESSIBILITY}. 578 * @return The node info of the focused view or null. 579 * 580 * @see #FOCUS_INPUT 581 * @see #FOCUS_ACCESSIBILITY 582 */ 583 public AccessibilityNodeInfo findFocus(int focus) { 584 enforceSealed(); 585 enforceValidFocusType(focus); 586 if (!canPerformRequestOverConnection(mSourceNodeId)) { 587 return null; 588 } 589 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId, 590 mSourceNodeId, focus); 591 } 592 593 /** 594 * Searches for the nearest view in the specified direction that can take 595 * the input focus. 596 * 597 * @param direction The direction. Can be one of: 598 * {@link View#FOCUS_DOWN}, 599 * {@link View#FOCUS_UP}, 600 * {@link View#FOCUS_LEFT}, 601 * {@link View#FOCUS_RIGHT}, 602 * {@link View#FOCUS_FORWARD}, 603 * {@link View#FOCUS_BACKWARD}. 604 * 605 * @return The node info for the view that can take accessibility focus. 606 */ 607 public AccessibilityNodeInfo focusSearch(int direction) { 608 enforceSealed(); 609 enforceValidFocusDirection(direction); 610 if (!canPerformRequestOverConnection(mSourceNodeId)) { 611 return null; 612 } 613 return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId, 614 mSourceNodeId, direction); 615 } 616 617 /** 618 * Gets the id of the window from which the info comes from. 619 * 620 * @return The window id. 621 */ 622 public int getWindowId() { 623 return mWindowId; 624 } 625 626 /** 627 * Refreshes this info with the latest state of the view it represents. 628 * <p> 629 * <strong>Note:</strong> If this method returns false this info is obsolete 630 * since it represents a view that is no longer in the view tree and should 631 * be recycled. 632 * </p> 633 * 634 * @param bypassCache Whether to bypass the cache. 635 * @return Whether the refresh succeeded. 636 * 637 * @hide 638 */ 639 public boolean refresh(boolean bypassCache) { 640 enforceSealed(); 641 if (!canPerformRequestOverConnection(mSourceNodeId)) { 642 return false; 643 } 644 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 645 AccessibilityNodeInfo refreshedInfo = client.findAccessibilityNodeInfoByAccessibilityId( 646 mConnectionId, mWindowId, mSourceNodeId, bypassCache, 0); 647 if (refreshedInfo == null) { 648 return false; 649 } 650 init(refreshedInfo); 651 refreshedInfo.recycle(); 652 return true; 653 } 654 655 /** 656 * Refreshes this info with the latest state of the view it represents. 657 * <p> 658 * <strong>Note:</strong> If this method returns false this info is obsolete 659 * since it represents a view that is no longer in the view tree and should 660 * be recycled. 661 * </p> 662 * @return Whether the refresh succeeded. 663 */ 664 public boolean refresh() { 665 return refresh(false); 666 } 667 668 /** 669 * @return The ids of the children. 670 * 671 * @hide 672 */ 673 public SparseLongArray getChildNodeIds() { 674 return mChildNodeIds; 675 } 676 677 /** 678 * Gets the number of children. 679 * 680 * @return The child count. 681 */ 682 public int getChildCount() { 683 return mChildNodeIds.size(); 684 } 685 686 /** 687 * Get the child at given index. 688 * <p> 689 * <strong>Note:</strong> It is a client responsibility to recycle the 690 * received info by calling {@link AccessibilityNodeInfo#recycle()} 691 * to avoid creating of multiple instances. 692 * </p> 693 * 694 * @param index The child index. 695 * @return The child node. 696 * 697 * @throws IllegalStateException If called outside of an AccessibilityService. 698 * 699 */ 700 public AccessibilityNodeInfo getChild(int index) { 701 enforceSealed(); 702 if (!canPerformRequestOverConnection(mSourceNodeId)) { 703 return null; 704 } 705 final long childId = mChildNodeIds.get(index); 706 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 707 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId, 708 childId, false, FLAG_PREFETCH_DESCENDANTS); 709 } 710 711 /** 712 * Adds a child. 713 * <p> 714 * <strong>Note:</strong> Cannot be called from an 715 * {@link android.accessibilityservice.AccessibilityService}. 716 * This class is made immutable before being delivered to an AccessibilityService. 717 * </p> 718 * 719 * @param child The child. 720 * 721 * @throws IllegalStateException If called from an AccessibilityService. 722 */ 723 public void addChild(View child) { 724 addChild(child, UNDEFINED); 725 } 726 727 /** 728 * Adds a virtual child which is a descendant of the given <code>root</code>. 729 * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root 730 * is added as a child. 731 * <p> 732 * A virtual descendant is an imaginary View that is reported as a part of the view 733 * hierarchy for accessibility purposes. This enables custom views that draw complex 734 * content to report them selves as a tree of virtual views, thus conveying their 735 * logical structure. 736 * </p> 737 * 738 * @param root The root of the virtual subtree. 739 * @param virtualDescendantId The id of the virtual child. 740 */ 741 public void addChild(View root, int virtualDescendantId) { 742 enforceNotSealed(); 743 final int index = mChildNodeIds.size(); 744 final int rootAccessibilityViewId = 745 (root != null) ? root.getAccessibilityViewId() : UNDEFINED; 746 final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 747 mChildNodeIds.put(index, childNodeId); 748 } 749 750 /** 751 * Gets the actions that can be performed on the node. 752 * 753 * @return The bit mask of with actions. 754 * 755 * @see AccessibilityNodeInfo#ACTION_FOCUS 756 * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS 757 * @see AccessibilityNodeInfo#ACTION_SELECT 758 * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION 759 * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS 760 * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS 761 * @see AccessibilityNodeInfo#ACTION_CLICK 762 * @see AccessibilityNodeInfo#ACTION_LONG_CLICK 763 * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY 764 * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY 765 * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT 766 * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT 767 * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD 768 * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD 769 */ 770 public int getActions() { 771 return mActions; 772 } 773 774 /** 775 * Adds an action that can be performed on the node. 776 * <p> 777 * <strong>Note:</strong> Cannot be called from an 778 * {@link android.accessibilityservice.AccessibilityService}. 779 * This class is made immutable before being delivered to an AccessibilityService. 780 * </p> 781 * 782 * @param action The action. 783 * 784 * @throws IllegalStateException If called from an AccessibilityService. 785 */ 786 public void addAction(int action) { 787 enforceNotSealed(); 788 mActions |= action; 789 } 790 791 /** 792 * Sets the movement granularities for traversing the text of this node. 793 * <p> 794 * <strong>Note:</strong> Cannot be called from an 795 * {@link android.accessibilityservice.AccessibilityService}. 796 * This class is made immutable before being delivered to an AccessibilityService. 797 * </p> 798 * 799 * @param granularities The bit mask with granularities. 800 * 801 * @throws IllegalStateException If called from an AccessibilityService. 802 */ 803 public void setMovementGranularities(int granularities) { 804 enforceNotSealed(); 805 mMovementGranularities = granularities; 806 } 807 808 /** 809 * Gets the movement granularities for traversing the text of this node. 810 * 811 * @return The bit mask with granularities. 812 */ 813 public int getMovementGranularities() { 814 return mMovementGranularities; 815 } 816 817 /** 818 * Performs an action on the node. 819 * <p> 820 * <strong>Note:</strong> An action can be performed only if the request is made 821 * from an {@link android.accessibilityservice.AccessibilityService}. 822 * </p> 823 * 824 * @param action The action to perform. 825 * @return True if the action was performed. 826 * 827 * @throws IllegalStateException If called outside of an AccessibilityService. 828 */ 829 public boolean performAction(int action) { 830 enforceSealed(); 831 if (!canPerformRequestOverConnection(mSourceNodeId)) { 832 return false; 833 } 834 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 835 return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, 836 action, null); 837 } 838 839 /** 840 * Performs an action on the node. 841 * <p> 842 * <strong>Note:</strong> An action can be performed only if the request is made 843 * from an {@link android.accessibilityservice.AccessibilityService}. 844 * </p> 845 * 846 * @param action The action to perform. 847 * @param arguments A bundle with additional arguments. 848 * @return True if the action was performed. 849 * 850 * @throws IllegalStateException If called outside of an AccessibilityService. 851 */ 852 public boolean performAction(int action, Bundle arguments) { 853 enforceSealed(); 854 if (!canPerformRequestOverConnection(mSourceNodeId)) { 855 return false; 856 } 857 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 858 return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, 859 action, arguments); 860 } 861 862 /** 863 * Finds {@link AccessibilityNodeInfo}s by text. The match is case 864 * insensitive containment. The search is relative to this info i.e. 865 * this info is the root of the traversed tree. 866 * 867 * <p> 868 * <strong>Note:</strong> It is a client responsibility to recycle the 869 * received info by calling {@link AccessibilityNodeInfo#recycle()} 870 * to avoid creating of multiple instances. 871 * </p> 872 * 873 * @param text The searched text. 874 * @return A list of node info. 875 */ 876 public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) { 877 enforceSealed(); 878 if (!canPerformRequestOverConnection(mSourceNodeId)) { 879 return Collections.emptyList(); 880 } 881 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 882 return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId, 883 text); 884 } 885 886 /** 887 * Finds {@link AccessibilityNodeInfo}s by the fully qualified view id's resource 888 * name where a fully qualified id is of the from "package:id/id_resource_name". 889 * For example, if the target application's package is "foo.bar" and the id 890 * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz". 891 * 892 * <p> 893 * <strong>Note:</strong> It is a client responsibility to recycle the 894 * received info by calling {@link AccessibilityNodeInfo#recycle()} 895 * to avoid creating of multiple instances. 896 * </p> 897 * <p> 898 * <strong>Note:</strong> The primary usage of this API is for UI test automation 899 * and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo} 900 * the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} 901 * flag when configuring his {@link android.accessibilityservice.AccessibilityService}. 902 * </p> 903 * 904 * @param viewId The fully qualified resource name of the view id to find. 905 * @return A list of node info. 906 */ 907 public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) { 908 enforceSealed(); 909 if (!canPerformRequestOverConnection(mSourceNodeId)) { 910 return Collections.emptyList(); 911 } 912 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 913 return client.findAccessibilityNodeInfosByViewId(mConnectionId, mWindowId, mSourceNodeId, 914 viewId); 915 } 916 917 /** 918 * Gets the parent. 919 * <p> 920 * <strong>Note:</strong> It is a client responsibility to recycle the 921 * received info by calling {@link AccessibilityNodeInfo#recycle()} 922 * to avoid creating of multiple instances. 923 * </p> 924 * 925 * @return The parent. 926 */ 927 public AccessibilityNodeInfo getParent() { 928 enforceSealed(); 929 if (!canPerformRequestOverConnection(mParentNodeId)) { 930 return null; 931 } 932 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 933 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, 934 mWindowId, mParentNodeId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); 935 } 936 937 /** 938 * @return The parent node id. 939 * 940 * @hide 941 */ 942 public long getParentNodeId() { 943 return mParentNodeId; 944 } 945 946 /** 947 * Sets the parent. 948 * <p> 949 * <strong>Note:</strong> Cannot be called from an 950 * {@link android.accessibilityservice.AccessibilityService}. 951 * This class is made immutable before being delivered to an AccessibilityService. 952 * </p> 953 * 954 * @param parent The parent. 955 * 956 * @throws IllegalStateException If called from an AccessibilityService. 957 */ 958 public void setParent(View parent) { 959 setParent(parent, UNDEFINED); 960 } 961 962 /** 963 * Sets the parent to be a virtual descendant of the given <code>root</code>. 964 * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root 965 * is set as the parent. 966 * <p> 967 * A virtual descendant is an imaginary View that is reported as a part of the view 968 * hierarchy for accessibility purposes. This enables custom views that draw complex 969 * content to report them selves as a tree of virtual views, thus conveying their 970 * logical structure. 971 * </p> 972 * <p> 973 * <strong>Note:</strong> Cannot be called from an 974 * {@link android.accessibilityservice.AccessibilityService}. 975 * This class is made immutable before being delivered to an AccessibilityService. 976 * </p> 977 * 978 * @param root The root of the virtual subtree. 979 * @param virtualDescendantId The id of the virtual descendant. 980 */ 981 public void setParent(View root, int virtualDescendantId) { 982 enforceNotSealed(); 983 final int rootAccessibilityViewId = 984 (root != null) ? root.getAccessibilityViewId() : UNDEFINED; 985 mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 986 } 987 988 /** 989 * Gets the node bounds in parent coordinates. 990 * 991 * @param outBounds The output node bounds. 992 */ 993 public void getBoundsInParent(Rect outBounds) { 994 outBounds.set(mBoundsInParent.left, mBoundsInParent.top, 995 mBoundsInParent.right, mBoundsInParent.bottom); 996 } 997 998 /** 999 * Sets the node bounds in parent coordinates. 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 bounds The node bounds. 1007 * 1008 * @throws IllegalStateException If called from an AccessibilityService. 1009 */ 1010 public void setBoundsInParent(Rect bounds) { 1011 enforceNotSealed(); 1012 mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom); 1013 } 1014 1015 /** 1016 * Gets the node bounds in screen coordinates. 1017 * 1018 * @param outBounds The output node bounds. 1019 */ 1020 public void getBoundsInScreen(Rect outBounds) { 1021 outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top, 1022 mBoundsInScreen.right, mBoundsInScreen.bottom); 1023 } 1024 1025 /** 1026 * Sets the node bounds in screen coordinates. 1027 * <p> 1028 * <strong>Note:</strong> Cannot be called from an 1029 * {@link android.accessibilityservice.AccessibilityService}. 1030 * This class is made immutable before being delivered to an AccessibilityService. 1031 * </p> 1032 * 1033 * @param bounds The node bounds. 1034 * 1035 * @throws IllegalStateException If called from an AccessibilityService. 1036 */ 1037 public void setBoundsInScreen(Rect bounds) { 1038 enforceNotSealed(); 1039 mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom); 1040 } 1041 1042 /** 1043 * Gets whether this node is checkable. 1044 * 1045 * @return True if the node is checkable. 1046 */ 1047 public boolean isCheckable() { 1048 return getBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE); 1049 } 1050 1051 /** 1052 * Sets whether this node is checkable. 1053 * <p> 1054 * <strong>Note:</strong> Cannot be called from an 1055 * {@link android.accessibilityservice.AccessibilityService}. 1056 * This class is made immutable before being delivered to an AccessibilityService. 1057 * </p> 1058 * 1059 * @param checkable True if the node is checkable. 1060 * 1061 * @throws IllegalStateException If called from an AccessibilityService. 1062 */ 1063 public void setCheckable(boolean checkable) { 1064 setBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE, checkable); 1065 } 1066 1067 /** 1068 * Gets whether this node is checked. 1069 * 1070 * @return True if the node is checked. 1071 */ 1072 public boolean isChecked() { 1073 return getBooleanProperty(BOOLEAN_PROPERTY_CHECKED); 1074 } 1075 1076 /** 1077 * Sets whether this node is checked. 1078 * <p> 1079 * <strong>Note:</strong> Cannot be called from an 1080 * {@link android.accessibilityservice.AccessibilityService}. 1081 * This class is made immutable before being delivered to an AccessibilityService. 1082 * </p> 1083 * 1084 * @param checked True if the node is checked. 1085 * 1086 * @throws IllegalStateException If called from an AccessibilityService. 1087 */ 1088 public void setChecked(boolean checked) { 1089 setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked); 1090 } 1091 1092 /** 1093 * Gets whether this node is focusable. 1094 * 1095 * @return True if the node is focusable. 1096 */ 1097 public boolean isFocusable() { 1098 return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE); 1099 } 1100 1101 /** 1102 * Sets whether this node is focusable. 1103 * <p> 1104 * <strong>Note:</strong> Cannot be called from an 1105 * {@link android.accessibilityservice.AccessibilityService}. 1106 * This class is made immutable before being delivered to an AccessibilityService. 1107 * </p> 1108 * 1109 * @param focusable True if the node is focusable. 1110 * 1111 * @throws IllegalStateException If called from an AccessibilityService. 1112 */ 1113 public void setFocusable(boolean focusable) { 1114 setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable); 1115 } 1116 1117 /** 1118 * Gets whether this node is focused. 1119 * 1120 * @return True if the node is focused. 1121 */ 1122 public boolean isFocused() { 1123 return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED); 1124 } 1125 1126 /** 1127 * Sets whether this node is focused. 1128 * <p> 1129 * <strong>Note:</strong> Cannot be called from an 1130 * {@link android.accessibilityservice.AccessibilityService}. 1131 * This class is made immutable before being delivered to an AccessibilityService. 1132 * </p> 1133 * 1134 * @param focused True if the node is focused. 1135 * 1136 * @throws IllegalStateException If called from an AccessibilityService. 1137 */ 1138 public void setFocused(boolean focused) { 1139 setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused); 1140 } 1141 1142 /** 1143 * Sets whether this node is visible to the user. 1144 * 1145 * @return Whether the node is visible to the user. 1146 */ 1147 public boolean isVisibleToUser() { 1148 return getBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER); 1149 } 1150 1151 /** 1152 * Sets whether this node is visible to the user. 1153 * <p> 1154 * <strong>Note:</strong> Cannot be called from an 1155 * {@link android.accessibilityservice.AccessibilityService}. 1156 * This class is made immutable before being delivered to an AccessibilityService. 1157 * </p> 1158 * 1159 * @param visibleToUser Whether the node is visible to the user. 1160 * 1161 * @throws IllegalStateException If called from an AccessibilityService. 1162 */ 1163 public void setVisibleToUser(boolean visibleToUser) { 1164 setBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER, visibleToUser); 1165 } 1166 1167 /** 1168 * Gets whether this node is accessibility focused. 1169 * 1170 * @return True if the node is accessibility focused. 1171 */ 1172 public boolean isAccessibilityFocused() { 1173 return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED); 1174 } 1175 1176 /** 1177 * Sets whether this node is accessibility focused. 1178 * <p> 1179 * <strong>Note:</strong> Cannot be called from an 1180 * {@link android.accessibilityservice.AccessibilityService}. 1181 * This class is made immutable before being delivered to an AccessibilityService. 1182 * </p> 1183 * 1184 * @param focused True if the node is accessibility focused. 1185 * 1186 * @throws IllegalStateException If called from an AccessibilityService. 1187 */ 1188 public void setAccessibilityFocused(boolean focused) { 1189 setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused); 1190 } 1191 1192 /** 1193 * Gets whether this node is selected. 1194 * 1195 * @return True if the node is selected. 1196 */ 1197 public boolean isSelected() { 1198 return getBooleanProperty(BOOLEAN_PROPERTY_SELECTED); 1199 } 1200 1201 /** 1202 * Sets whether this node is selected. 1203 * <p> 1204 * <strong>Note:</strong> Cannot be called from an 1205 * {@link android.accessibilityservice.AccessibilityService}. 1206 * This class is made immutable before being delivered to an AccessibilityService. 1207 * </p> 1208 * 1209 * @param selected True if the node is selected. 1210 * 1211 * @throws IllegalStateException If called from an AccessibilityService. 1212 */ 1213 public void setSelected(boolean selected) { 1214 setBooleanProperty(BOOLEAN_PROPERTY_SELECTED, selected); 1215 } 1216 1217 /** 1218 * Gets whether this node is clickable. 1219 * 1220 * @return True if the node is clickable. 1221 */ 1222 public boolean isClickable() { 1223 return getBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE); 1224 } 1225 1226 /** 1227 * Sets whether this node is clickable. 1228 * <p> 1229 * <strong>Note:</strong> Cannot be called from an 1230 * {@link android.accessibilityservice.AccessibilityService}. 1231 * This class is made immutable before being delivered to an AccessibilityService. 1232 * </p> 1233 * 1234 * @param clickable True if the node is clickable. 1235 * 1236 * @throws IllegalStateException If called from an AccessibilityService. 1237 */ 1238 public void setClickable(boolean clickable) { 1239 setBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE, clickable); 1240 } 1241 1242 /** 1243 * Gets whether this node is long clickable. 1244 * 1245 * @return True if the node is long clickable. 1246 */ 1247 public boolean isLongClickable() { 1248 return getBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE); 1249 } 1250 1251 /** 1252 * Sets whether this node is long clickable. 1253 * <p> 1254 * <strong>Note:</strong> Cannot be called from an 1255 * {@link android.accessibilityservice.AccessibilityService}. 1256 * This class is made immutable before being delivered to an AccessibilityService. 1257 * </p> 1258 * 1259 * @param longClickable True if the node is long clickable. 1260 * 1261 * @throws IllegalStateException If called from an AccessibilityService. 1262 */ 1263 public void setLongClickable(boolean longClickable) { 1264 setBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE, longClickable); 1265 } 1266 1267 /** 1268 * Gets whether this node is enabled. 1269 * 1270 * @return True if the node is enabled. 1271 */ 1272 public boolean isEnabled() { 1273 return getBooleanProperty(BOOLEAN_PROPERTY_ENABLED); 1274 } 1275 1276 /** 1277 * Sets whether this node is enabled. 1278 * <p> 1279 * <strong>Note:</strong> Cannot be called from an 1280 * {@link android.accessibilityservice.AccessibilityService}. 1281 * This class is made immutable before being delivered to an AccessibilityService. 1282 * </p> 1283 * 1284 * @param enabled True if the node is enabled. 1285 * 1286 * @throws IllegalStateException If called from an AccessibilityService. 1287 */ 1288 public void setEnabled(boolean enabled) { 1289 setBooleanProperty(BOOLEAN_PROPERTY_ENABLED, enabled); 1290 } 1291 1292 /** 1293 * Gets whether this node is a password. 1294 * 1295 * @return True if the node is a password. 1296 */ 1297 public boolean isPassword() { 1298 return getBooleanProperty(BOOLEAN_PROPERTY_PASSWORD); 1299 } 1300 1301 /** 1302 * Sets whether this node is a password. 1303 * <p> 1304 * <strong>Note:</strong> Cannot be called from an 1305 * {@link android.accessibilityservice.AccessibilityService}. 1306 * This class is made immutable before being delivered to an AccessibilityService. 1307 * </p> 1308 * 1309 * @param password True if the node is a password. 1310 * 1311 * @throws IllegalStateException If called from an AccessibilityService. 1312 */ 1313 public void setPassword(boolean password) { 1314 setBooleanProperty(BOOLEAN_PROPERTY_PASSWORD, password); 1315 } 1316 1317 /** 1318 * Gets if the node is scrollable. 1319 * 1320 * @return True if the node is scrollable, false otherwise. 1321 */ 1322 public boolean isScrollable() { 1323 return getBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE); 1324 } 1325 1326 /** 1327 * Sets if the node is scrollable. 1328 * <p> 1329 * <strong>Note:</strong> Cannot be called from an 1330 * {@link android.accessibilityservice.AccessibilityService}. 1331 * This class is made immutable before being delivered to an AccessibilityService. 1332 * </p> 1333 * 1334 * @param scrollable True if the node is scrollable, false otherwise. 1335 * 1336 * @throws IllegalStateException If called from an AccessibilityService. 1337 */ 1338 public void setScrollable(boolean scrollable) { 1339 setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable); 1340 } 1341 1342 /** 1343 * Gets if the node is editable. 1344 * 1345 * @return True if the node is editable, false otherwise. 1346 */ 1347 public boolean isEditable() { 1348 return getBooleanProperty(BOOLEAN_PROPERTY_EDITABLE); 1349 } 1350 1351 /** 1352 * Sets whether this node is editable. 1353 * <p> 1354 * <strong>Note:</strong> Cannot be called from an 1355 * {@link android.accessibilityservice.AccessibilityService}. 1356 * This class is made immutable before being delivered to an AccessibilityService. 1357 * </p> 1358 * 1359 * @param editable True if the node is editable. 1360 * 1361 * @throws IllegalStateException If called from an AccessibilityService. 1362 */ 1363 public void setEditable(boolean editable) { 1364 setBooleanProperty(BOOLEAN_PROPERTY_EDITABLE, editable); 1365 } 1366 1367 /** 1368 * Gets the collection info if the node is a collection. A collection 1369 * child is always a collection item. 1370 * 1371 * @return The collection info. 1372 */ 1373 public CollectionInfo getCollectionInfo() { 1374 return mCollectionInfo; 1375 } 1376 1377 /** 1378 * Sets the collection info if the node is a collection. A collection 1379 * child is always a collection item. 1380 * <p> 1381 * <strong>Note:</strong> Cannot be called from an 1382 * {@link android.accessibilityservice.AccessibilityService}. 1383 * This class is made immutable before being delivered to an AccessibilityService. 1384 * </p> 1385 * 1386 * @param collectionInfo The collection info. 1387 */ 1388 public void setCollectionInfo(CollectionInfo collectionInfo) { 1389 enforceNotSealed(); 1390 mCollectionInfo = collectionInfo; 1391 } 1392 1393 /** 1394 * Gets the collection item info if the node is a collection item. A collection 1395 * item is always a child of a collection. 1396 * 1397 * @return The collection item info. 1398 */ 1399 public CollectionItemInfo getCollectionItemInfo() { 1400 return mCollectionItemInfo; 1401 } 1402 1403 /** 1404 * Sets the collection item info if the node is a collection item. A collection 1405 * item is always a child of a collection. 1406 * <p> 1407 * <strong>Note:</strong> Cannot be called from an 1408 * {@link android.accessibilityservice.AccessibilityService}. 1409 * This class is made immutable before being delivered to an AccessibilityService. 1410 * </p> 1411 * 1412 * @return collectionItem True if the node is an item. 1413 */ 1414 public void setCollectionItemInfo(CollectionItemInfo collectionItemInfo) { 1415 enforceNotSealed(); 1416 mCollectionItemInfo = collectionItemInfo; 1417 } 1418 1419 /** 1420 * Gets the range info if this node is a range. 1421 * 1422 * @return The range. 1423 */ 1424 public RangeInfo getRangeInfo() { 1425 return mRangeInfo; 1426 } 1427 1428 /** 1429 * Sets the range info if this node is a range. 1430 * <p> 1431 * <strong>Note:</strong> Cannot be called from an 1432 * {@link android.accessibilityservice.AccessibilityService}. 1433 * This class is made immutable before being delivered to an AccessibilityService. 1434 * </p> 1435 * 1436 * @param rangeInfo The range info. 1437 */ 1438 public void setRangeInfo(RangeInfo rangeInfo) { 1439 enforceNotSealed(); 1440 mRangeInfo = rangeInfo; 1441 } 1442 1443 /** 1444 * Gets if the content of this node is invalid. For example, 1445 * a date is not well-formed. 1446 * 1447 * @return If the node content is invalid. 1448 */ 1449 public boolean isContentInvalid() { 1450 return getBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID); 1451 } 1452 1453 /** 1454 * Sets if the content of this node is invalid. For example, 1455 * a date is not well-formed. 1456 * <p> 1457 * <strong>Note:</strong> Cannot be called from an 1458 * {@link android.accessibilityservice.AccessibilityService}. 1459 * This class is made immutable before being delivered to an AccessibilityService. 1460 * </p> 1461 * 1462 * @param contentInvalid If the node content is invalid. 1463 */ 1464 public void setContentInvalid(boolean contentInvalid) { 1465 setBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID, contentInvalid); 1466 } 1467 1468 /** 1469 * Gets the node's live region mode. 1470 * <p> 1471 * A live region is a node that contains information that is important for 1472 * the user and when it changes the user should be notified. For example, 1473 * in a login screen with a TextView that displays an "incorrect password" 1474 * notification, that view should be marked as a live region with mode 1475 * {@link View#ACCESSIBILITY_LIVE_REGION_POLITE}. 1476 * <p> 1477 * It is the responsibility of the accessibility service to monitor 1478 * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events indicating 1479 * changes to live region nodes and their children. 1480 * 1481 * @return The live region mode, or 1482 * {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a 1483 * live region. 1484 * @see android.view.View#getAccessibilityLiveRegion() 1485 */ 1486 public int getLiveRegion() { 1487 return mLiveRegion; 1488 } 1489 1490 /** 1491 * Sets the node's live region mode. 1492 * <p> 1493 * <strong>Note:</strong> Cannot be called from an 1494 * {@link android.accessibilityservice.AccessibilityService}. This class is 1495 * made immutable before being delivered to an AccessibilityService. 1496 * 1497 * @param mode The live region mode, or 1498 * {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a 1499 * live region. 1500 * @see android.view.View#setAccessibilityLiveRegion(int) 1501 */ 1502 public void setLiveRegion(int mode) { 1503 enforceNotSealed(); 1504 mLiveRegion = mode; 1505 } 1506 1507 /** 1508 * Gets if the node is a multi line editable text. 1509 * 1510 * @return True if the node is multi line. 1511 */ 1512 public boolean isMultiLine() { 1513 return getBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE); 1514 } 1515 1516 /** 1517 * Sets if the node is a multi line editable text. 1518 * <p> 1519 * <strong>Note:</strong> Cannot be called from an 1520 * {@link android.accessibilityservice.AccessibilityService}. 1521 * This class is made immutable before being delivered to an AccessibilityService. 1522 * </p> 1523 * 1524 * @param multiLine True if the node is multi line. 1525 */ 1526 public void setMultiLine(boolean multiLine) { 1527 setBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE, multiLine); 1528 } 1529 1530 /** 1531 * Gets if this node opens a popup or a dialog. 1532 * 1533 * @return If the the node opens a popup. 1534 */ 1535 public boolean canOpenPopup() { 1536 return getBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP); 1537 } 1538 1539 /** 1540 * Sets if this node opens a popup or a dialog. 1541 * <p> 1542 * <strong>Note:</strong> Cannot be called from an 1543 * {@link android.accessibilityservice.AccessibilityService}. 1544 * This class is made immutable before being delivered to an AccessibilityService. 1545 * </p> 1546 * 1547 * @param opensPopup If the the node opens a popup. 1548 */ 1549 public void setCanOpenPopup(boolean opensPopup) { 1550 enforceNotSealed(); 1551 setBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP, opensPopup); 1552 } 1553 1554 /** 1555 * Gets if the node can be dismissed. 1556 * 1557 * @return If the node can be dismissed. 1558 */ 1559 public boolean isDismissable() { 1560 return getBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE); 1561 } 1562 1563 /** 1564 * Sets if the node can be dismissed. 1565 * <p> 1566 * <strong>Note:</strong> Cannot be called from an 1567 * {@link android.accessibilityservice.AccessibilityService}. 1568 * This class is made immutable before being delivered to an AccessibilityService. 1569 * </p> 1570 * 1571 * @param dismissable If the node can be dismissed. 1572 */ 1573 public void setDismissable(boolean dismissable) { 1574 setBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE, dismissable); 1575 } 1576 1577 /** 1578 * Gets the package this node comes from. 1579 * 1580 * @return The package name. 1581 */ 1582 public CharSequence getPackageName() { 1583 return mPackageName; 1584 } 1585 1586 /** 1587 * Sets the package this node comes from. 1588 * <p> 1589 * <strong>Note:</strong> Cannot be called from an 1590 * {@link android.accessibilityservice.AccessibilityService}. 1591 * This class is made immutable before being delivered to an AccessibilityService. 1592 * </p> 1593 * 1594 * @param packageName The package name. 1595 * 1596 * @throws IllegalStateException If called from an AccessibilityService. 1597 */ 1598 public void setPackageName(CharSequence packageName) { 1599 enforceNotSealed(); 1600 mPackageName = packageName; 1601 } 1602 1603 /** 1604 * Gets the class this node comes from. 1605 * 1606 * @return The class name. 1607 */ 1608 public CharSequence getClassName() { 1609 return mClassName; 1610 } 1611 1612 /** 1613 * Sets the class this node comes from. 1614 * <p> 1615 * <strong>Note:</strong> Cannot be called from an 1616 * {@link android.accessibilityservice.AccessibilityService}. 1617 * This class is made immutable before being delivered to an AccessibilityService. 1618 * </p> 1619 * 1620 * @param className The class name. 1621 * 1622 * @throws IllegalStateException If called from an AccessibilityService. 1623 */ 1624 public void setClassName(CharSequence className) { 1625 enforceNotSealed(); 1626 mClassName = className; 1627 } 1628 1629 /** 1630 * Gets the text of this node. 1631 * 1632 * @return The text. 1633 */ 1634 public CharSequence getText() { 1635 return mText; 1636 } 1637 1638 /** 1639 * Sets the text of this node. 1640 * <p> 1641 * <strong>Note:</strong> Cannot be called from an 1642 * {@link android.accessibilityservice.AccessibilityService}. 1643 * This class is made immutable before being delivered to an AccessibilityService. 1644 * </p> 1645 * 1646 * @param text The text. 1647 * 1648 * @throws IllegalStateException If called from an AccessibilityService. 1649 */ 1650 public void setText(CharSequence text) { 1651 enforceNotSealed(); 1652 mText = text; 1653 } 1654 1655 /** 1656 * Gets the content description of this node. 1657 * 1658 * @return The content description. 1659 */ 1660 public CharSequence getContentDescription() { 1661 return mContentDescription; 1662 } 1663 1664 /** 1665 * Sets the content description of this node. 1666 * <p> 1667 * <strong>Note:</strong> Cannot be called from an 1668 * {@link android.accessibilityservice.AccessibilityService}. 1669 * This class is made immutable before being delivered to an AccessibilityService. 1670 * </p> 1671 * 1672 * @param contentDescription The content description. 1673 * 1674 * @throws IllegalStateException If called from an AccessibilityService. 1675 */ 1676 public void setContentDescription(CharSequence contentDescription) { 1677 enforceNotSealed(); 1678 mContentDescription = contentDescription; 1679 } 1680 1681 /** 1682 * Sets the view for which the view represented by this info serves as a 1683 * label for accessibility purposes. 1684 * 1685 * @param labeled The view for which this info serves as a label. 1686 */ 1687 public void setLabelFor(View labeled) { 1688 setLabelFor(labeled, UNDEFINED); 1689 } 1690 1691 /** 1692 * Sets the view for which the view represented by this info serves as a 1693 * label for accessibility purposes. If <code>virtualDescendantId</code> 1694 * is {@link View#NO_ID} the root is set as the labeled. 1695 * <p> 1696 * A virtual descendant is an imaginary View that is reported as a part of the view 1697 * hierarchy for accessibility purposes. This enables custom views that draw complex 1698 * content to report themselves as a tree of virtual views, thus conveying their 1699 * logical structure. 1700 * </p> 1701 * <p> 1702 * <strong>Note:</strong> Cannot be called from an 1703 * {@link android.accessibilityservice.AccessibilityService}. 1704 * This class is made immutable before being delivered to an AccessibilityService. 1705 * </p> 1706 * 1707 * @param root The root whose virtual descendant serves as a label. 1708 * @param virtualDescendantId The id of the virtual descendant. 1709 */ 1710 public void setLabelFor(View root, int virtualDescendantId) { 1711 enforceNotSealed(); 1712 final int rootAccessibilityViewId = (root != null) 1713 ? root.getAccessibilityViewId() : UNDEFINED; 1714 mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 1715 } 1716 1717 /** 1718 * Gets the node info for which the view represented by this info serves as 1719 * a label for accessibility purposes. 1720 * <p> 1721 * <strong>Note:</strong> It is a client responsibility to recycle the 1722 * received info by calling {@link AccessibilityNodeInfo#recycle()} 1723 * to avoid creating of multiple instances. 1724 * </p> 1725 * 1726 * @return The labeled info. 1727 */ 1728 public AccessibilityNodeInfo getLabelFor() { 1729 enforceSealed(); 1730 if (!canPerformRequestOverConnection(mLabelForId)) { 1731 return null; 1732 } 1733 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 1734 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, 1735 mWindowId, mLabelForId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); 1736 } 1737 1738 /** 1739 * Sets the view which serves as the label of the view represented by 1740 * this info for accessibility purposes. 1741 * 1742 * @param label The view that labels this node's source. 1743 */ 1744 public void setLabeledBy(View label) { 1745 setLabeledBy(label, UNDEFINED); 1746 } 1747 1748 /** 1749 * Sets the view which serves as the label of the view represented by 1750 * this info for accessibility purposes. If <code>virtualDescendantId</code> 1751 * is {@link View#NO_ID} the root is set as the label. 1752 * <p> 1753 * A virtual descendant is an imaginary View that is reported as a part of the view 1754 * hierarchy for accessibility purposes. This enables custom views that draw complex 1755 * content to report themselves as a tree of virtual views, thus conveying their 1756 * logical structure. 1757 * </p> 1758 * <p> 1759 * <strong>Note:</strong> Cannot be called from an 1760 * {@link android.accessibilityservice.AccessibilityService}. 1761 * This class is made immutable before being delivered to an AccessibilityService. 1762 * </p> 1763 * 1764 * @param root The root whose virtual descendant labels this node's source. 1765 * @param virtualDescendantId The id of the virtual descendant. 1766 */ 1767 public void setLabeledBy(View root, int virtualDescendantId) { 1768 enforceNotSealed(); 1769 final int rootAccessibilityViewId = (root != null) 1770 ? root.getAccessibilityViewId() : UNDEFINED; 1771 mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 1772 } 1773 1774 /** 1775 * Gets the node info which serves as the label of the view represented by 1776 * this info for accessibility purposes. 1777 * <p> 1778 * <strong>Note:</strong> It is a client responsibility to recycle the 1779 * received info by calling {@link AccessibilityNodeInfo#recycle()} 1780 * to avoid creating of multiple instances. 1781 * </p> 1782 * 1783 * @return The label. 1784 */ 1785 public AccessibilityNodeInfo getLabeledBy() { 1786 enforceSealed(); 1787 if (!canPerformRequestOverConnection(mLabeledById)) { 1788 return null; 1789 } 1790 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 1791 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, 1792 mWindowId, mLabeledById, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); 1793 } 1794 1795 /** 1796 * Sets the fully qualified resource name of the source view's id. 1797 * 1798 * <p> 1799 * <strong>Note:</strong> Cannot be called from an 1800 * {@link android.accessibilityservice.AccessibilityService}. 1801 * This class is made immutable before being delivered to an AccessibilityService. 1802 * </p> 1803 * 1804 * @param viewIdResName The id resource name. 1805 */ 1806 public void setViewIdResourceName(String viewIdResName) { 1807 enforceNotSealed(); 1808 mViewIdResourceName = viewIdResName; 1809 } 1810 1811 /** 1812 * Gets the fully qualified resource name of the source view's id. 1813 * 1814 * <p> 1815 * <strong>Note:</strong> The primary usage of this API is for UI test automation 1816 * and in order to report the source view id of an {@link AccessibilityNodeInfo} the 1817 * client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} 1818 * flag when configuring his {@link android.accessibilityservice.AccessibilityService}. 1819 * </p> 1820 1821 * @return The id resource name. 1822 */ 1823 public String getViewIdResourceName() { 1824 return mViewIdResourceName; 1825 } 1826 1827 /** 1828 * Gets the text selection start. 1829 * 1830 * @return The text selection start if there is selection or -1. 1831 */ 1832 public int getTextSelectionStart() { 1833 return mTextSelectionStart; 1834 } 1835 1836 /** 1837 * Gets the text selection end. 1838 * 1839 * @return The text selection end if there is selection or -1. 1840 */ 1841 public int getTextSelectionEnd() { 1842 return mTextSelectionEnd; 1843 } 1844 1845 /** 1846 * Sets the text selection start and end. 1847 * <p> 1848 * <strong>Note:</strong> Cannot be called from an 1849 * {@link android.accessibilityservice.AccessibilityService}. 1850 * This class is made immutable before being delivered to an AccessibilityService. 1851 * </p> 1852 * 1853 * @param start The text selection start. 1854 * @param end The text selection end. 1855 * 1856 * @throws IllegalStateException If called from an AccessibilityService. 1857 */ 1858 public void setTextSelection(int start, int end) { 1859 enforceNotSealed(); 1860 mTextSelectionStart = start; 1861 mTextSelectionEnd = end; 1862 } 1863 1864 /** 1865 * Gets the input type of the source as defined by {@link InputType}. 1866 * 1867 * @return The input type. 1868 */ 1869 public int getInputType() { 1870 return mInputType; 1871 } 1872 1873 /** 1874 * Sets the input type of the source as defined by {@link InputType}. 1875 * <p> 1876 * <strong>Note:</strong> Cannot be called from an 1877 * {@link android.accessibilityservice.AccessibilityService}. 1878 * This class is made immutable before being delivered to an 1879 * AccessibilityService. 1880 * </p> 1881 * 1882 * @param inputType The input type. 1883 * 1884 * @throws IllegalStateException If called from an AccessibilityService. 1885 */ 1886 public void setInputType(int inputType) { 1887 enforceNotSealed(); 1888 mInputType = inputType; 1889 } 1890 1891 /** 1892 * Gets an optional bundle with extra data. The bundle 1893 * is lazily created and never <code>null</code>. 1894 * <p> 1895 * <strong>Note:</strong> It is recommended to use the package 1896 * name of your application as a prefix for the keys to avoid 1897 * collisions which may confuse an accessibility service if the 1898 * same key has different meaning when emitted from different 1899 * applications. 1900 * </p> 1901 * 1902 * @return The bundle. 1903 */ 1904 public Bundle getExtras() { 1905 if (mExtras == null) { 1906 mExtras = new Bundle(); 1907 } 1908 return mExtras; 1909 } 1910 1911 /** 1912 * Gets the value of a boolean property. 1913 * 1914 * @param property The property. 1915 * @return The value. 1916 */ 1917 private boolean getBooleanProperty(int property) { 1918 return (mBooleanProperties & property) != 0; 1919 } 1920 1921 /** 1922 * Sets a boolean property. 1923 * 1924 * @param property The property. 1925 * @param value The value. 1926 * 1927 * @throws IllegalStateException If called from an AccessibilityService. 1928 */ 1929 private void setBooleanProperty(int property, boolean value) { 1930 enforceNotSealed(); 1931 if (value) { 1932 mBooleanProperties |= property; 1933 } else { 1934 mBooleanProperties &= ~property; 1935 } 1936 } 1937 1938 /** 1939 * Sets the unique id of the IAccessibilityServiceConnection over which 1940 * this instance can send requests to the system. 1941 * 1942 * @param connectionId The connection id. 1943 * 1944 * @hide 1945 */ 1946 public void setConnectionId(int connectionId) { 1947 enforceNotSealed(); 1948 mConnectionId = connectionId; 1949 } 1950 1951 /** 1952 * {@inheritDoc} 1953 */ 1954 public int describeContents() { 1955 return 0; 1956 } 1957 1958 /** 1959 * Gets the id of the source node. 1960 * 1961 * @return The id. 1962 * 1963 * @hide 1964 */ 1965 public long getSourceNodeId() { 1966 return mSourceNodeId; 1967 } 1968 1969 /** 1970 * Sets if this instance is sealed. 1971 * 1972 * @param sealed Whether is sealed. 1973 * 1974 * @hide 1975 */ 1976 public void setSealed(boolean sealed) { 1977 mSealed = sealed; 1978 } 1979 1980 /** 1981 * Gets if this instance is sealed. 1982 * 1983 * @return Whether is sealed. 1984 * 1985 * @hide 1986 */ 1987 public boolean isSealed() { 1988 return mSealed; 1989 } 1990 1991 /** 1992 * Enforces that this instance is sealed. 1993 * 1994 * @throws IllegalStateException If this instance is not sealed. 1995 * 1996 * @hide 1997 */ 1998 protected void enforceSealed() { 1999 if (!isSealed()) { 2000 throw new IllegalStateException("Cannot perform this " 2001 + "action on a not sealed instance."); 2002 } 2003 } 2004 2005 private void enforceValidFocusDirection(int direction) { 2006 switch (direction) { 2007 case View.FOCUS_DOWN: 2008 case View.FOCUS_UP: 2009 case View.FOCUS_LEFT: 2010 case View.FOCUS_RIGHT: 2011 case View.FOCUS_FORWARD: 2012 case View.FOCUS_BACKWARD: 2013 return; 2014 default: 2015 throw new IllegalArgumentException("Unknown direction: " + direction); 2016 } 2017 } 2018 2019 private void enforceValidFocusType(int focusType) { 2020 switch (focusType) { 2021 case FOCUS_INPUT: 2022 case FOCUS_ACCESSIBILITY: 2023 return; 2024 default: 2025 throw new IllegalArgumentException("Unknown focus type: " + focusType); 2026 } 2027 } 2028 2029 /** 2030 * Enforces that this instance is not sealed. 2031 * 2032 * @throws IllegalStateException If this instance is sealed. 2033 * 2034 * @hide 2035 */ 2036 protected void enforceNotSealed() { 2037 if (isSealed()) { 2038 throw new IllegalStateException("Cannot perform this " 2039 + "action on a sealed instance."); 2040 } 2041 } 2042 2043 /** 2044 * Returns a cached instance if such is available otherwise a new one 2045 * and sets the source. 2046 * 2047 * @param source The source view. 2048 * @return An instance. 2049 * 2050 * @see #setSource(View) 2051 */ 2052 public static AccessibilityNodeInfo obtain(View source) { 2053 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 2054 info.setSource(source); 2055 return info; 2056 } 2057 2058 /** 2059 * Returns a cached instance if such is available otherwise a new one 2060 * and sets the source. 2061 * 2062 * @param root The root of the virtual subtree. 2063 * @param virtualDescendantId The id of the virtual descendant. 2064 * @return An instance. 2065 * 2066 * @see #setSource(View, int) 2067 */ 2068 public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) { 2069 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 2070 info.setSource(root, virtualDescendantId); 2071 return info; 2072 } 2073 2074 /** 2075 * Returns a cached instance if such is available otherwise a new one. 2076 * 2077 * @return An instance. 2078 */ 2079 public static AccessibilityNodeInfo obtain() { 2080 AccessibilityNodeInfo info = sPool.acquire(); 2081 return (info != null) ? info : new AccessibilityNodeInfo(); 2082 } 2083 2084 /** 2085 * Returns a cached instance if such is available or a new one is 2086 * create. The returned instance is initialized from the given 2087 * <code>info</code>. 2088 * 2089 * @param info The other info. 2090 * @return An instance. 2091 */ 2092 public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) { 2093 AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain(); 2094 infoClone.init(info); 2095 return infoClone; 2096 } 2097 2098 /** 2099 * Return an instance back to be reused. 2100 * <p> 2101 * <strong>Note:</strong> You must not touch the object after calling this function. 2102 * 2103 * @throws IllegalStateException If the info is already recycled. 2104 */ 2105 public void recycle() { 2106 clear(); 2107 sPool.release(this); 2108 } 2109 2110 /** 2111 * {@inheritDoc} 2112 * <p> 2113 * <strong>Note:</strong> After the instance is written to a parcel it 2114 * is recycled. You must not touch the object after calling this function. 2115 * </p> 2116 */ 2117 public void writeToParcel(Parcel parcel, int flags) { 2118 parcel.writeInt(isSealed() ? 1 : 0); 2119 parcel.writeLong(mSourceNodeId); 2120 parcel.writeInt(mWindowId); 2121 parcel.writeLong(mParentNodeId); 2122 parcel.writeLong(mLabelForId); 2123 parcel.writeLong(mLabeledById); 2124 parcel.writeInt(mConnectionId); 2125 2126 SparseLongArray childIds = mChildNodeIds; 2127 final int childIdsSize = childIds.size(); 2128 parcel.writeInt(childIdsSize); 2129 for (int i = 0; i < childIdsSize; i++) { 2130 parcel.writeLong(childIds.valueAt(i)); 2131 } 2132 2133 parcel.writeInt(mBoundsInParent.top); 2134 parcel.writeInt(mBoundsInParent.bottom); 2135 parcel.writeInt(mBoundsInParent.left); 2136 parcel.writeInt(mBoundsInParent.right); 2137 2138 parcel.writeInt(mBoundsInScreen.top); 2139 parcel.writeInt(mBoundsInScreen.bottom); 2140 parcel.writeInt(mBoundsInScreen.left); 2141 parcel.writeInt(mBoundsInScreen.right); 2142 2143 parcel.writeInt(mActions); 2144 2145 parcel.writeInt(mMovementGranularities); 2146 2147 parcel.writeInt(mBooleanProperties); 2148 2149 parcel.writeCharSequence(mPackageName); 2150 parcel.writeCharSequence(mClassName); 2151 parcel.writeCharSequence(mText); 2152 parcel.writeCharSequence(mContentDescription); 2153 parcel.writeString(mViewIdResourceName); 2154 2155 parcel.writeInt(mTextSelectionStart); 2156 parcel.writeInt(mTextSelectionEnd); 2157 parcel.writeInt(mInputType); 2158 parcel.writeInt(mLiveRegion); 2159 2160 if (mExtras != null) { 2161 parcel.writeInt(1); 2162 parcel.writeBundle(mExtras); 2163 } else { 2164 parcel.writeInt(0); 2165 } 2166 2167 if (mRangeInfo != null) { 2168 parcel.writeInt(1); 2169 parcel.writeInt(mRangeInfo.getType()); 2170 parcel.writeFloat(mRangeInfo.getMin()); 2171 parcel.writeFloat(mRangeInfo.getMax()); 2172 parcel.writeFloat(mRangeInfo.getCurrent()); 2173 } else { 2174 parcel.writeInt(0); 2175 } 2176 2177 if (mCollectionInfo != null) { 2178 parcel.writeInt(1); 2179 parcel.writeInt(mCollectionInfo.getRowCount()); 2180 parcel.writeInt(mCollectionInfo.getColumnCount()); 2181 parcel.writeInt(mCollectionInfo.isHierarchical() ? 1 : 0); 2182 } else { 2183 parcel.writeInt(0); 2184 } 2185 2186 if (mCollectionItemInfo != null) { 2187 parcel.writeInt(1); 2188 parcel.writeInt(mCollectionItemInfo.getColumnIndex()); 2189 parcel.writeInt(mCollectionItemInfo.getColumnSpan()); 2190 parcel.writeInt(mCollectionItemInfo.getRowIndex()); 2191 parcel.writeInt(mCollectionItemInfo.getRowSpan()); 2192 parcel.writeInt(mCollectionItemInfo.isHeading() ? 1 : 0); 2193 } else { 2194 parcel.writeInt(0); 2195 } 2196 2197 // Since instances of this class are fetched via synchronous i.e. blocking 2198 // calls in IPCs we always recycle as soon as the instance is marshaled. 2199 recycle(); 2200 } 2201 2202 /** 2203 * Initializes this instance from another one. 2204 * 2205 * @param other The other instance. 2206 */ 2207 private void init(AccessibilityNodeInfo other) { 2208 mSealed = other.mSealed; 2209 mSourceNodeId = other.mSourceNodeId; 2210 mParentNodeId = other.mParentNodeId; 2211 mLabelForId = other.mLabelForId; 2212 mLabeledById = other.mLabeledById; 2213 mWindowId = other.mWindowId; 2214 mConnectionId = other.mConnectionId; 2215 mBoundsInParent.set(other.mBoundsInParent); 2216 mBoundsInScreen.set(other.mBoundsInScreen); 2217 mPackageName = other.mPackageName; 2218 mClassName = other.mClassName; 2219 mText = other.mText; 2220 mContentDescription = other.mContentDescription; 2221 mViewIdResourceName = other.mViewIdResourceName; 2222 mActions= other.mActions; 2223 mBooleanProperties = other.mBooleanProperties; 2224 mMovementGranularities = other.mMovementGranularities; 2225 final int otherChildIdCount = other.mChildNodeIds.size(); 2226 for (int i = 0; i < otherChildIdCount; i++) { 2227 mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i)); 2228 } 2229 mTextSelectionStart = other.mTextSelectionStart; 2230 mTextSelectionEnd = other.mTextSelectionEnd; 2231 mInputType = other.mInputType; 2232 mLiveRegion = other.mLiveRegion; 2233 if (other.mExtras != null && !other.mExtras.isEmpty()) { 2234 getExtras().putAll(other.mExtras); 2235 } 2236 mRangeInfo = (other.mRangeInfo != null) 2237 ? RangeInfo.obtain(other.mRangeInfo) : null; 2238 mCollectionInfo = (other.mCollectionInfo != null) 2239 ? CollectionInfo.obtain(other.mCollectionInfo) : null; 2240 mCollectionItemInfo = (other.mCollectionItemInfo != null) 2241 ? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null; 2242 } 2243 2244 /** 2245 * Creates a new instance from a {@link Parcel}. 2246 * 2247 * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}. 2248 */ 2249 private void initFromParcel(Parcel parcel) { 2250 mSealed = (parcel.readInt() == 1); 2251 mSourceNodeId = parcel.readLong(); 2252 mWindowId = parcel.readInt(); 2253 mParentNodeId = parcel.readLong(); 2254 mLabelForId = parcel.readLong(); 2255 mLabeledById = parcel.readLong(); 2256 mConnectionId = parcel.readInt(); 2257 2258 SparseLongArray childIds = mChildNodeIds; 2259 final int childrenSize = parcel.readInt(); 2260 for (int i = 0; i < childrenSize; i++) { 2261 final long childId = parcel.readLong(); 2262 childIds.put(i, childId); 2263 } 2264 2265 mBoundsInParent.top = parcel.readInt(); 2266 mBoundsInParent.bottom = parcel.readInt(); 2267 mBoundsInParent.left = parcel.readInt(); 2268 mBoundsInParent.right = parcel.readInt(); 2269 2270 mBoundsInScreen.top = parcel.readInt(); 2271 mBoundsInScreen.bottom = parcel.readInt(); 2272 mBoundsInScreen.left = parcel.readInt(); 2273 mBoundsInScreen.right = parcel.readInt(); 2274 2275 mActions = parcel.readInt(); 2276 2277 mMovementGranularities = parcel.readInt(); 2278 2279 mBooleanProperties = parcel.readInt(); 2280 2281 mPackageName = parcel.readCharSequence(); 2282 mClassName = parcel.readCharSequence(); 2283 mText = parcel.readCharSequence(); 2284 mContentDescription = parcel.readCharSequence(); 2285 mViewIdResourceName = parcel.readString(); 2286 2287 mTextSelectionStart = parcel.readInt(); 2288 mTextSelectionEnd = parcel.readInt(); 2289 2290 mInputType = parcel.readInt(); 2291 mLiveRegion = parcel.readInt(); 2292 2293 if (parcel.readInt() == 1) { 2294 getExtras().putAll(parcel.readBundle()); 2295 } 2296 2297 if (parcel.readInt() == 1) { 2298 mRangeInfo = RangeInfo.obtain( 2299 parcel.readInt(), 2300 parcel.readFloat(), 2301 parcel.readFloat(), 2302 parcel.readFloat()); 2303 } 2304 2305 if (parcel.readInt() == 1) { 2306 mCollectionInfo = CollectionInfo.obtain( 2307 parcel.readInt(), 2308 parcel.readInt(), 2309 parcel.readInt() == 1); 2310 } 2311 2312 if (parcel.readInt() == 1) { 2313 mCollectionItemInfo = CollectionItemInfo.obtain( 2314 parcel.readInt(), 2315 parcel.readInt(), 2316 parcel.readInt(), 2317 parcel.readInt(), 2318 parcel.readInt() == 1); 2319 } 2320 } 2321 2322 /** 2323 * Clears the state of this instance. 2324 */ 2325 private void clear() { 2326 mSealed = false; 2327 mSourceNodeId = ROOT_NODE_ID; 2328 mParentNodeId = ROOT_NODE_ID; 2329 mLabelForId = ROOT_NODE_ID; 2330 mLabeledById = ROOT_NODE_ID; 2331 mWindowId = UNDEFINED; 2332 mConnectionId = UNDEFINED; 2333 mMovementGranularities = 0; 2334 mChildNodeIds.clear(); 2335 mBoundsInParent.set(0, 0, 0, 0); 2336 mBoundsInScreen.set(0, 0, 0, 0); 2337 mBooleanProperties = 0; 2338 mPackageName = null; 2339 mClassName = null; 2340 mText = null; 2341 mContentDescription = null; 2342 mViewIdResourceName = null; 2343 mActions = 0; 2344 mTextSelectionStart = UNDEFINED; 2345 mTextSelectionEnd = UNDEFINED; 2346 mInputType = InputType.TYPE_NULL; 2347 mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE; 2348 if (mExtras != null) { 2349 mExtras.clear(); 2350 } 2351 if (mRangeInfo != null) { 2352 mRangeInfo.recycle(); 2353 mRangeInfo = null; 2354 } 2355 if (mCollectionInfo != null) { 2356 mCollectionInfo.recycle(); 2357 mCollectionInfo = null; 2358 } 2359 if (mCollectionItemInfo != null) { 2360 mCollectionItemInfo.recycle(); 2361 mCollectionItemInfo = null; 2362 } 2363 } 2364 2365 /** 2366 * Gets the human readable action symbolic name. 2367 * 2368 * @param action The action. 2369 * @return The symbolic name. 2370 */ 2371 private static String getActionSymbolicName(int action) { 2372 switch (action) { 2373 case ACTION_FOCUS: 2374 return "ACTION_FOCUS"; 2375 case ACTION_CLEAR_FOCUS: 2376 return "ACTION_CLEAR_FOCUS"; 2377 case ACTION_SELECT: 2378 return "ACTION_SELECT"; 2379 case ACTION_CLEAR_SELECTION: 2380 return "ACTION_CLEAR_SELECTION"; 2381 case ACTION_CLICK: 2382 return "ACTION_CLICK"; 2383 case ACTION_LONG_CLICK: 2384 return "ACTION_LONG_CLICK"; 2385 case ACTION_ACCESSIBILITY_FOCUS: 2386 return "ACTION_ACCESSIBILITY_FOCUS"; 2387 case ACTION_CLEAR_ACCESSIBILITY_FOCUS: 2388 return "ACTION_CLEAR_ACCESSIBILITY_FOCUS"; 2389 case ACTION_NEXT_AT_MOVEMENT_GRANULARITY: 2390 return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY"; 2391 case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: 2392 return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY"; 2393 case ACTION_NEXT_HTML_ELEMENT: 2394 return "ACTION_NEXT_HTML_ELEMENT"; 2395 case ACTION_PREVIOUS_HTML_ELEMENT: 2396 return "ACTION_PREVIOUS_HTML_ELEMENT"; 2397 case ACTION_SCROLL_FORWARD: 2398 return "ACTION_SCROLL_FORWARD"; 2399 case ACTION_SCROLL_BACKWARD: 2400 return "ACTION_SCROLL_BACKWARD"; 2401 case ACTION_CUT: 2402 return "ACTION_CUT"; 2403 case ACTION_COPY: 2404 return "ACTION_COPY"; 2405 case ACTION_PASTE: 2406 return "ACTION_PASTE"; 2407 case ACTION_SET_SELECTION: 2408 return "ACTION_SET_SELECTION"; 2409 default: 2410 return"ACTION_UNKNOWN"; 2411 } 2412 } 2413 2414 /** 2415 * Gets the human readable movement granularity symbolic name. 2416 * 2417 * @param granularity The granularity. 2418 * @return The symbolic name. 2419 */ 2420 private static String getMovementGranularitySymbolicName(int granularity) { 2421 switch (granularity) { 2422 case MOVEMENT_GRANULARITY_CHARACTER: 2423 return "MOVEMENT_GRANULARITY_CHARACTER"; 2424 case MOVEMENT_GRANULARITY_WORD: 2425 return "MOVEMENT_GRANULARITY_WORD"; 2426 case MOVEMENT_GRANULARITY_LINE: 2427 return "MOVEMENT_GRANULARITY_LINE"; 2428 case MOVEMENT_GRANULARITY_PARAGRAPH: 2429 return "MOVEMENT_GRANULARITY_PARAGRAPH"; 2430 case MOVEMENT_GRANULARITY_PAGE: 2431 return "MOVEMENT_GRANULARITY_PAGE"; 2432 default: 2433 throw new IllegalArgumentException("Unknown movement granularity: " + granularity); 2434 } 2435 } 2436 2437 private boolean canPerformRequestOverConnection(long accessibilityNodeId) { 2438 return (mWindowId != UNDEFINED 2439 && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED 2440 && mConnectionId != UNDEFINED); 2441 } 2442 2443 @Override 2444 public boolean equals(Object object) { 2445 if (this == object) { 2446 return true; 2447 } 2448 if (object == null) { 2449 return false; 2450 } 2451 if (getClass() != object.getClass()) { 2452 return false; 2453 } 2454 AccessibilityNodeInfo other = (AccessibilityNodeInfo) object; 2455 if (mSourceNodeId != other.mSourceNodeId) { 2456 return false; 2457 } 2458 if (mWindowId != other.mWindowId) { 2459 return false; 2460 } 2461 return true; 2462 } 2463 2464 @Override 2465 public int hashCode() { 2466 final int prime = 31; 2467 int result = 1; 2468 result = prime * result + getAccessibilityViewId(mSourceNodeId); 2469 result = prime * result + getVirtualDescendantId(mSourceNodeId); 2470 result = prime * result + mWindowId; 2471 return result; 2472 } 2473 2474 @Override 2475 public String toString() { 2476 StringBuilder builder = new StringBuilder(); 2477 builder.append(super.toString()); 2478 2479 if (DEBUG) { 2480 builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId)); 2481 builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId)); 2482 builder.append("; mParentNodeId: " + mParentNodeId); 2483 2484 int granularities = mMovementGranularities; 2485 builder.append("; MovementGranularities: ["); 2486 while (granularities != 0) { 2487 final int granularity = 1 << Integer.numberOfTrailingZeros(granularities); 2488 granularities &= ~granularity; 2489 builder.append(getMovementGranularitySymbolicName(granularity)); 2490 if (granularities != 0) { 2491 builder.append(", "); 2492 } 2493 } 2494 builder.append("]"); 2495 2496 SparseLongArray childIds = mChildNodeIds; 2497 builder.append("; childAccessibilityIds: ["); 2498 for (int i = 0, count = childIds.size(); i < count; i++) { 2499 builder.append(childIds.valueAt(i)); 2500 if (i < count - 1) { 2501 builder.append(", "); 2502 } 2503 } 2504 builder.append("]"); 2505 } 2506 2507 builder.append("; boundsInParent: " + mBoundsInParent); 2508 builder.append("; boundsInScreen: " + mBoundsInScreen); 2509 2510 builder.append("; packageName: ").append(mPackageName); 2511 builder.append("; className: ").append(mClassName); 2512 builder.append("; text: ").append(mText); 2513 builder.append("; contentDescription: ").append(mContentDescription); 2514 builder.append("; viewIdResName: ").append(mViewIdResourceName); 2515 2516 builder.append("; checkable: ").append(isCheckable()); 2517 builder.append("; checked: ").append(isChecked()); 2518 builder.append("; focusable: ").append(isFocusable()); 2519 builder.append("; focused: ").append(isFocused()); 2520 builder.append("; selected: ").append(isSelected()); 2521 builder.append("; clickable: ").append(isClickable()); 2522 builder.append("; longClickable: ").append(isLongClickable()); 2523 builder.append("; enabled: ").append(isEnabled()); 2524 builder.append("; password: ").append(isPassword()); 2525 builder.append("; scrollable: " + isScrollable()); 2526 2527 builder.append("; ["); 2528 for (int actionBits = mActions; actionBits != 0;) { 2529 final int action = 1 << Integer.numberOfTrailingZeros(actionBits); 2530 actionBits &= ~action; 2531 builder.append(getActionSymbolicName(action)); 2532 if (actionBits != 0) { 2533 builder.append(", "); 2534 } 2535 } 2536 builder.append("]"); 2537 2538 return builder.toString(); 2539 } 2540 2541 /** 2542 * Class with information if a node is a range. Use 2543 * {@link RangeInfo#obtain(int, float, float, float) to get an instance. 2544 */ 2545 public static final class RangeInfo { 2546 private static final int MAX_POOL_SIZE = 10; 2547 2548 /** Range type: integer. */ 2549 public static final int RANGE_TYPE_INT = 0; 2550 /** Range type: float. */ 2551 public static final int RANGE_TYPE_FLOAT = 1; 2552 /** Range type: percent with values from zero to one.*/ 2553 public static final int RANGE_TYPE_PERCENT = 2; 2554 2555 private static final SynchronizedPool<RangeInfo> sPool = 2556 new SynchronizedPool<AccessibilityNodeInfo.RangeInfo>(MAX_POOL_SIZE); 2557 2558 private int mType; 2559 private float mMin; 2560 private float mMax; 2561 private float mCurrent; 2562 2563 /** 2564 * Obtains a pooled instance that is a clone of another one. 2565 * 2566 * @param other The instance to clone. 2567 * 2568 * @hide 2569 */ 2570 public static RangeInfo obtain(RangeInfo other) { 2571 return obtain(other.mType, other.mMin, other.mMax, other.mCurrent); 2572 } 2573 2574 /** 2575 * Obtains a pooled instance. 2576 * 2577 * @param type The type of the range. 2578 * @param min The min value. 2579 * @param max The max value. 2580 * @param current The current value. 2581 */ 2582 public static RangeInfo obtain(int type, float min, float max, float current) { 2583 RangeInfo info = sPool.acquire(); 2584 return (info != null) ? info : new RangeInfo(type, min, max, current); 2585 } 2586 2587 /** 2588 * Creates a new range. 2589 * 2590 * @param type The type of the range. 2591 * @param min The min value. 2592 * @param max The max value. 2593 * @param current The current value. 2594 */ 2595 private RangeInfo(int type, float min, float max, float current) { 2596 mType = type; 2597 mMin = min; 2598 mMax = max; 2599 mCurrent = current; 2600 } 2601 2602 /** 2603 * Gets the range type. 2604 * 2605 * @return The range type. 2606 * 2607 * @see #RANGE_TYPE_INT 2608 * @see #RANGE_TYPE_FLOAT 2609 * @see #RANGE_TYPE_PERCENT 2610 */ 2611 public int getType() { 2612 return mType; 2613 } 2614 2615 /** 2616 * Gets the min value. 2617 * 2618 * @return The min value. 2619 */ 2620 public float getMin() { 2621 return mMin; 2622 } 2623 2624 /** 2625 * Gets the max value. 2626 * 2627 * @return The max value. 2628 */ 2629 public float getMax() { 2630 return mMax; 2631 } 2632 2633 /** 2634 * Gets the current value. 2635 * 2636 * @return The current value. 2637 */ 2638 public float getCurrent() { 2639 return mCurrent; 2640 } 2641 2642 /** 2643 * Recycles this instance. 2644 */ 2645 void recycle() { 2646 clear(); 2647 sPool.release(this); 2648 } 2649 2650 private void clear() { 2651 mType = 0; 2652 mMin = 0; 2653 mMax = 0; 2654 mCurrent = 0; 2655 } 2656 } 2657 2658 /** 2659 * Class with information if a node is a collection. Use 2660 * {@link CollectionInfo#obtain(int, int, boolean)} to get an instance. 2661 * <p> 2662 * A collection of items has rows and columns and may be hierarchical. 2663 * For example, a horizontal list is a collection with one column, as 2664 * many rows as the list items, and is not hierarchical; A table is a 2665 * collection with several rows, several columns, and is not hierarchical; 2666 * A vertical tree is a hierarchical collection with one column and 2667 * as many rows as the first level children. 2668 * </p> 2669 */ 2670 public static final class CollectionInfo { 2671 private static final int MAX_POOL_SIZE = 20; 2672 2673 private static final SynchronizedPool<CollectionInfo> sPool = 2674 new SynchronizedPool<CollectionInfo>(MAX_POOL_SIZE); 2675 2676 private int mRowCount; 2677 private int mColumnCount; 2678 private boolean mHierarchical; 2679 2680 /** 2681 * Obtains a pooled instance that is a clone of another one. 2682 * 2683 * @param other The instance to clone. 2684 * 2685 * @hide 2686 */ 2687 public static CollectionInfo obtain(CollectionInfo other) { 2688 return CollectionInfo.obtain(other.mRowCount, other.mColumnCount, 2689 other.mHierarchical); 2690 } 2691 2692 /** 2693 * Obtains a pooled instance. 2694 * 2695 * @param rowCount The number of rows. 2696 * @param columnCount The number of columns. 2697 * @param hierarchical Whether the collection is hierarchical. 2698 */ 2699 public static CollectionInfo obtain(int rowCount, int columnCount, 2700 boolean hierarchical) { 2701 CollectionInfo info = sPool.acquire(); 2702 return (info != null) ? info : new CollectionInfo(rowCount, 2703 columnCount, hierarchical); 2704 } 2705 2706 /** 2707 * Creates a new instance. 2708 * 2709 * @param rowCount The number of rows. 2710 * @param columnCount The number of columns. 2711 * @param hierarchical Whether the collection is hierarchical. 2712 */ 2713 private CollectionInfo(int rowCount, int columnCount, 2714 boolean hierarchical) { 2715 mRowCount = rowCount; 2716 mColumnCount = columnCount; 2717 mHierarchical = hierarchical; 2718 } 2719 2720 /** 2721 * Gets the number of rows. 2722 * 2723 * @return The row count. 2724 */ 2725 public int getRowCount() { 2726 return mRowCount; 2727 } 2728 2729 /** 2730 * Gets the number of columns. 2731 * 2732 * @return The column count. 2733 */ 2734 public int getColumnCount() { 2735 return mColumnCount; 2736 } 2737 2738 /** 2739 * Gets if the collection is a hierarchically ordered. 2740 * 2741 * @return Whether the collection is hierarchical. 2742 */ 2743 public boolean isHierarchical() { 2744 return mHierarchical; 2745 } 2746 2747 /** 2748 * Recycles this instance. 2749 */ 2750 void recycle() { 2751 clear(); 2752 sPool.release(this); 2753 } 2754 2755 private void clear() { 2756 mRowCount = 0; 2757 mColumnCount = 0; 2758 mHierarchical = false; 2759 } 2760 } 2761 2762 /** 2763 * Class with information if a node is a collection item. Use 2764 * {@link CollectionItemInfo#obtain(int, int, int, int, boolean)} 2765 * to get an instance. 2766 * <p> 2767 * A collection item is contained in a collection, it starts at 2768 * a given row and column in the collection, and spans one or 2769 * more rows and columns. For example, a header of two related 2770 * table columns starts at the first row and the first column, 2771 * spans one row and two columns. 2772 * </p> 2773 */ 2774 public static final class CollectionItemInfo { 2775 private static final int MAX_POOL_SIZE = 20; 2776 2777 private static final SynchronizedPool<CollectionItemInfo> sPool = 2778 new SynchronizedPool<CollectionItemInfo>(MAX_POOL_SIZE); 2779 2780 /** 2781 * Obtains a pooled instance that is a clone of another one. 2782 * 2783 * @param other The instance to clone. 2784 * 2785 * @hide 2786 */ 2787 public static CollectionItemInfo obtain(CollectionItemInfo other) { 2788 return CollectionItemInfo.obtain(other.mRowIndex, other.mRowSpan, 2789 other.mColumnIndex, other.mColumnSpan, other.mHeading); 2790 } 2791 2792 /** 2793 * Obtains a pooled instance. 2794 * 2795 * @param rowIndex The row index at which the item is located. 2796 * @param rowSpan The number of rows the item spans. 2797 * @param columnIndex The column index at which the item is located. 2798 * @param columnSpan The number of columns the item spans. 2799 * @param heading Whether the item is a heading. 2800 */ 2801 public static CollectionItemInfo obtain(int rowIndex, int rowSpan, 2802 int columnIndex, int columnSpan, boolean heading) { 2803 CollectionItemInfo info = sPool.acquire(); 2804 return (info != null) ? info : new CollectionItemInfo(rowIndex, 2805 rowSpan, columnIndex, columnSpan, heading); 2806 } 2807 2808 private boolean mHeading; 2809 private int mColumnIndex; 2810 private int mRowIndex; 2811 private int mColumnSpan; 2812 private int mRowSpan; 2813 2814 /** 2815 * Creates a new instance. 2816 * 2817 * @param rowIndex The row index at which the item is located. 2818 * @param rowSpan The number of rows the item spans. 2819 * @param columnIndex The column index at which the item is located. 2820 * @param columnSpan The number of columns the item spans. 2821 * @param heading Whether the item is a heading. 2822 */ 2823 private CollectionItemInfo(int rowIndex, int rowSpan, 2824 int columnIndex, int columnSpan, boolean heading) { 2825 mRowIndex = rowIndex; 2826 mRowSpan = rowSpan; 2827 mColumnIndex = columnIndex; 2828 mColumnSpan = columnSpan; 2829 mHeading = heading; 2830 } 2831 2832 /** 2833 * Gets the column index at which the item is located. 2834 * 2835 * @return The column index. 2836 */ 2837 public int getColumnIndex() { 2838 return mColumnIndex; 2839 } 2840 2841 /** 2842 * Gets the row index at which the item is located. 2843 * 2844 * @return The row index. 2845 */ 2846 public int getRowIndex() { 2847 return mRowIndex; 2848 } 2849 2850 /** 2851 * Gets the number of columns the item spans. 2852 * 2853 * @return The column span. 2854 */ 2855 public int getColumnSpan() { 2856 return mColumnSpan; 2857 } 2858 2859 /** 2860 * Gets the number of rows the item spans. 2861 * 2862 * @return The row span. 2863 */ 2864 public int getRowSpan() { 2865 return mRowSpan; 2866 } 2867 2868 /** 2869 * Gets if the collection item is a heading. For example, section 2870 * heading, table header, etc. 2871 * 2872 * @return If the item is a heading. 2873 */ 2874 public boolean isHeading() { 2875 return mHeading; 2876 } 2877 2878 /** 2879 * Recycles this instance. 2880 */ 2881 void recycle() { 2882 clear(); 2883 sPool.release(this); 2884 } 2885 2886 private void clear() { 2887 mColumnIndex = 0; 2888 mColumnSpan = 0; 2889 mRowIndex = 0; 2890 mRowSpan = 0; 2891 mHeading = false; 2892 } 2893 } 2894 2895 /** 2896 * @see Parcelable.Creator 2897 */ 2898 public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR = 2899 new Parcelable.Creator<AccessibilityNodeInfo>() { 2900 public AccessibilityNodeInfo createFromParcel(Parcel parcel) { 2901 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 2902 info.initFromParcel(parcel); 2903 return info; 2904 } 2905 2906 public AccessibilityNodeInfo[] newArray(int size) { 2907 return new AccessibilityNodeInfo[size]; 2908 } 2909 }; 2910 } 2911