AccessibilityNodeInfo.java revision 0ebe6bd5da3b7f3ca7fcc7c80cf4589e194fc698
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.view.accessibility; 18 19import static java.util.Collections.EMPTY_LIST; 20 21import android.accessibilityservice.AccessibilityService; 22import android.accessibilityservice.AccessibilityServiceInfo; 23import android.annotation.Nullable; 24import android.annotation.TestApi; 25import android.graphics.Rect; 26import android.os.Bundle; 27import android.os.Parcel; 28import android.os.Parcelable; 29import android.text.InputType; 30import android.text.Spannable; 31import android.text.SpannableStringBuilder; 32import android.text.Spanned; 33import android.text.TextUtils; 34import android.text.style.AccessibilityClickableSpan; 35import android.text.style.AccessibilityURLSpan; 36import android.text.style.ClickableSpan; 37import android.text.style.URLSpan; 38import android.util.ArraySet; 39import android.util.LongArray; 40import android.util.Pools.SynchronizedPool; 41import android.view.View; 42 43import com.android.internal.R; 44 45import java.util.ArrayList; 46import java.util.Collections; 47import java.util.List; 48import java.util.concurrent.atomic.AtomicInteger; 49 50/** 51 * This class represents a node of the window content as well as actions that 52 * can be requested from its source. From the point of view of an 53 * {@link android.accessibilityservice.AccessibilityService} a window's content is 54 * presented as a tree of accessibility node infos, which may or may not map one-to-one 55 * to the view hierarchy. In other words, a custom view is free to report itself as 56 * a tree of accessibility node info. 57 * </p> 58 * <p> 59 * Once an accessibility node info is delivered to an accessibility service it is 60 * made immutable and calling a state mutation method generates an error. 61 * </p> 62 * <p> 63 * Please refer to {@link android.accessibilityservice.AccessibilityService} for 64 * details about how to obtain a handle to window content as a tree of accessibility 65 * node info as well as details about the security model. 66 * </p> 67 * <div class="special reference"> 68 * <h3>Developer Guides</h3> 69 * <p>For more information about making applications accessible, read the 70 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> 71 * developer guide.</p> 72 * </div> 73 * 74 * @see android.accessibilityservice.AccessibilityService 75 * @see AccessibilityEvent 76 * @see AccessibilityManager 77 */ 78public class AccessibilityNodeInfo implements Parcelable { 79 80 private static final boolean DEBUG = false; 81 82 /** @hide */ 83 public static final int UNDEFINED_CONNECTION_ID = -1; 84 85 /** @hide */ 86 public static final int UNDEFINED_SELECTION_INDEX = -1; 87 88 /** @hide */ 89 public static final int UNDEFINED_ITEM_ID = Integer.MAX_VALUE; 90 91 /** @hide */ 92 public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID); 93 94 /** @hide */ 95 public static final int ACTIVE_WINDOW_ID = UNDEFINED_ITEM_ID; 96 97 /** @hide */ 98 public static final int ANY_WINDOW_ID = -2; 99 100 /** @hide */ 101 public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001; 102 103 /** @hide */ 104 public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002; 105 106 /** @hide */ 107 public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004; 108 109 /** @hide */ 110 public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008; 111 112 /** @hide */ 113 public static final int FLAG_REPORT_VIEW_IDS = 0x00000010; 114 115 // Actions. 116 117 /** 118 * Action that gives input focus to the node. 119 */ 120 public static final int ACTION_FOCUS = 0x00000001; 121 122 /** 123 * Action that clears input focus of the node. 124 */ 125 public static final int ACTION_CLEAR_FOCUS = 0x00000002; 126 127 /** 128 * Action that selects the node. 129 */ 130 public static final int ACTION_SELECT = 0x00000004; 131 132 /** 133 * Action that deselects the node. 134 */ 135 public static final int ACTION_CLEAR_SELECTION = 0x00000008; 136 137 /** 138 * Action that clicks on the node info. 139 * 140 * See {@link AccessibilityAction#ACTION_CLICK} 141 */ 142 public static final int ACTION_CLICK = 0x00000010; 143 144 /** 145 * Action that long clicks on the node. 146 */ 147 public static final int ACTION_LONG_CLICK = 0x00000020; 148 149 /** 150 * Action that gives accessibility focus to the node. 151 */ 152 public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040; 153 154 /** 155 * Action that clears accessibility focus of the node. 156 */ 157 public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080; 158 159 /** 160 * Action that requests to go to the next entity in this node's text 161 * at a given movement granularity. For example, move to the next character, 162 * word, etc. 163 * <p> 164 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<, 165 * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br> 166 * <strong>Example:</strong> Move to the previous character and do not extend selection. 167 * <code><pre><p> 168 * Bundle arguments = new Bundle(); 169 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, 170 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); 171 * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, 172 * false); 173 * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments); 174 * </code></pre></p> 175 * </p> 176 * 177 * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT 178 * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 179 * 180 * @see #setMovementGranularities(int) 181 * @see #getMovementGranularities() 182 * 183 * @see #MOVEMENT_GRANULARITY_CHARACTER 184 * @see #MOVEMENT_GRANULARITY_WORD 185 * @see #MOVEMENT_GRANULARITY_LINE 186 * @see #MOVEMENT_GRANULARITY_PARAGRAPH 187 * @see #MOVEMENT_GRANULARITY_PAGE 188 */ 189 public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100; 190 191 /** 192 * Action that requests to go to the previous entity in this node's text 193 * at a given movement granularity. For example, move to the next character, 194 * word, etc. 195 * <p> 196 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<, 197 * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br> 198 * <strong>Example:</strong> Move to the next character and do not extend selection. 199 * <code><pre><p> 200 * Bundle arguments = new Bundle(); 201 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, 202 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); 203 * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, 204 * false); 205 * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, 206 * arguments); 207 * </code></pre></p> 208 * </p> 209 * 210 * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT 211 * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 212 * 213 * @see #setMovementGranularities(int) 214 * @see #getMovementGranularities() 215 * 216 * @see #MOVEMENT_GRANULARITY_CHARACTER 217 * @see #MOVEMENT_GRANULARITY_WORD 218 * @see #MOVEMENT_GRANULARITY_LINE 219 * @see #MOVEMENT_GRANULARITY_PARAGRAPH 220 * @see #MOVEMENT_GRANULARITY_PAGE 221 */ 222 public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200; 223 224 /** 225 * Action to move to the next HTML element of a given type. For example, move 226 * to the BUTTON, INPUT, TABLE, etc. 227 * <p> 228 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> 229 * <strong>Example:</strong> 230 * <code><pre><p> 231 * Bundle arguments = new Bundle(); 232 * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); 233 * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments); 234 * </code></pre></p> 235 * </p> 236 */ 237 public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400; 238 239 /** 240 * Action to move to the previous HTML element of a given type. For example, move 241 * to the BUTTON, INPUT, TABLE, etc. 242 * <p> 243 * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> 244 * <strong>Example:</strong> 245 * <code><pre><p> 246 * Bundle arguments = new Bundle(); 247 * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); 248 * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments); 249 * </code></pre></p> 250 * </p> 251 */ 252 public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800; 253 254 /** 255 * Action to scroll the node content forward. 256 */ 257 public static final int ACTION_SCROLL_FORWARD = 0x00001000; 258 259 /** 260 * Action to scroll the node content backward. 261 */ 262 public static final int ACTION_SCROLL_BACKWARD = 0x00002000; 263 264 /** 265 * Action to copy the current selection to the clipboard. 266 */ 267 public static final int ACTION_COPY = 0x00004000; 268 269 /** 270 * Action to paste the current clipboard content. 271 */ 272 public static final int ACTION_PASTE = 0x00008000; 273 274 /** 275 * Action to cut the current selection and place it to the clipboard. 276 */ 277 public static final int ACTION_CUT = 0x00010000; 278 279 /** 280 * Action to set the selection. Performing this action with no arguments 281 * clears the selection. 282 * <p> 283 * <strong>Arguments:</strong> 284 * {@link #ACTION_ARGUMENT_SELECTION_START_INT}, 285 * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br> 286 * <strong>Example:</strong> 287 * <code><pre><p> 288 * Bundle arguments = new Bundle(); 289 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1); 290 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2); 291 * info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments); 292 * </code></pre></p> 293 * </p> 294 * 295 * @see #ACTION_ARGUMENT_SELECTION_START_INT 296 * @see #ACTION_ARGUMENT_SELECTION_END_INT 297 */ 298 public static final int ACTION_SET_SELECTION = 0x00020000; 299 300 /** 301 * Action to expand an expandable node. 302 */ 303 public static final int ACTION_EXPAND = 0x00040000; 304 305 /** 306 * Action to collapse an expandable node. 307 */ 308 public static final int ACTION_COLLAPSE = 0x00080000; 309 310 /** 311 * Action to dismiss a dismissable node. 312 */ 313 public static final int ACTION_DISMISS = 0x00100000; 314 315 /** 316 * Action that sets the text of the node. Performing the action without argument, using <code> 317 * null</code> or empty {@link CharSequence} will clear the text. This action will also put the 318 * cursor at the end of text. 319 * <p> 320 * <strong>Arguments:</strong> 321 * {@link #ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br> 322 * <strong>Example:</strong> 323 * <code><pre><p> 324 * Bundle arguments = new Bundle(); 325 * arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, 326 * "android"); 327 * info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments); 328 * </code></pre></p> 329 */ 330 public static final int ACTION_SET_TEXT = 0x00200000; 331 332 private static final int LAST_LEGACY_STANDARD_ACTION = ACTION_SET_TEXT; 333 334 /** 335 * Mask to see if the value is larger than the largest ACTION_ constant 336 */ 337 private static final int ACTION_TYPE_MASK = 0xFF000000; 338 339 // Action arguments 340 341 /** 342 * Argument for which movement granularity to be used when traversing the node text. 343 * <p> 344 * <strong>Type:</strong> int<br> 345 * <strong>Actions:</strong> 346 * <ul> 347 * <li>{@link AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY}</li> 348 * <li>{@link AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}</li> 349 * </ul> 350 * </p> 351 * 352 * @see AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY 353 * @see AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY 354 */ 355 public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = 356 "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT"; 357 358 /** 359 * Argument for which HTML element to get moving to the next/previous HTML element. 360 * <p> 361 * <strong>Type:</strong> String<br> 362 * <strong>Actions:</strong> 363 * <ul> 364 * <li>{@link AccessibilityAction#ACTION_NEXT_HTML_ELEMENT}</li> 365 * <li>{@link AccessibilityAction#ACTION_PREVIOUS_HTML_ELEMENT}</li> 366 * </ul> 367 * </p> 368 * 369 * @see AccessibilityAction#ACTION_NEXT_HTML_ELEMENT 370 * @see AccessibilityAction#ACTION_PREVIOUS_HTML_ELEMENT 371 */ 372 public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = 373 "ACTION_ARGUMENT_HTML_ELEMENT_STRING"; 374 375 /** 376 * Argument for whether when moving at granularity to extend the selection 377 * or to move it otherwise. 378 * <p> 379 * <strong>Type:</strong> boolean<br> 380 * <strong>Actions:</strong> 381 * <ul> 382 * <li>{@link AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY}</li> 383 * <li>{@link AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}</li> 384 * </ul> 385 * 386 * @see AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY 387 * @see AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY 388 */ 389 public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = 390 "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN"; 391 392 /** 393 * Argument for specifying the selection start. 394 * <p> 395 * <strong>Type:</strong> int<br> 396 * <strong>Actions:</strong> 397 * <ul> 398 * <li>{@link AccessibilityAction#ACTION_SET_SELECTION}</li> 399 * </ul> 400 * 401 * @see AccessibilityAction#ACTION_SET_SELECTION 402 */ 403 public static final String ACTION_ARGUMENT_SELECTION_START_INT = 404 "ACTION_ARGUMENT_SELECTION_START_INT"; 405 406 /** 407 * Argument for specifying the selection end. 408 * <p> 409 * <strong>Type:</strong> int<br> 410 * <strong>Actions:</strong> 411 * <ul> 412 * <li>{@link AccessibilityAction#ACTION_SET_SELECTION}</li> 413 * </ul> 414 * 415 * @see AccessibilityAction#ACTION_SET_SELECTION 416 */ 417 public static final String ACTION_ARGUMENT_SELECTION_END_INT = 418 "ACTION_ARGUMENT_SELECTION_END_INT"; 419 420 /** 421 * Argument for specifying the text content to set. 422 * <p> 423 * <strong>Type:</strong> CharSequence<br> 424 * <strong>Actions:</strong> 425 * <ul> 426 * <li>{@link AccessibilityAction#ACTION_SET_TEXT}</li> 427 * </ul> 428 * 429 * @see AccessibilityAction#ACTION_SET_TEXT 430 */ 431 public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = 432 "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE"; 433 434 /** 435 * Argument for specifying the collection row to make visible on screen. 436 * <p> 437 * <strong>Type:</strong> int<br> 438 * <strong>Actions:</strong> 439 * <ul> 440 * <li>{@link AccessibilityAction#ACTION_SCROLL_TO_POSITION}</li> 441 * </ul> 442 * 443 * @see AccessibilityAction#ACTION_SCROLL_TO_POSITION 444 */ 445 public static final String ACTION_ARGUMENT_ROW_INT = 446 "android.view.accessibility.action.ARGUMENT_ROW_INT"; 447 448 /** 449 * Argument for specifying the collection column to make visible on screen. 450 * <p> 451 * <strong>Type:</strong> int<br> 452 * <strong>Actions:</strong> 453 * <ul> 454 * <li>{@link AccessibilityAction#ACTION_SCROLL_TO_POSITION}</li> 455 * </ul> 456 * 457 * @see AccessibilityAction#ACTION_SCROLL_TO_POSITION 458 */ 459 public static final String ACTION_ARGUMENT_COLUMN_INT = 460 "android.view.accessibility.action.ARGUMENT_COLUMN_INT"; 461 462 /** 463 * Argument for specifying the progress value to set. 464 * <p> 465 * <strong>Type:</strong> float<br> 466 * <strong>Actions:</strong> 467 * <ul> 468 * <li>{@link AccessibilityAction#ACTION_SET_PROGRESS}</li> 469 * </ul> 470 * 471 * @see AccessibilityAction#ACTION_SET_PROGRESS 472 */ 473 public static final String ACTION_ARGUMENT_PROGRESS_VALUE = 474 "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE"; 475 476 /** 477 * Argument to pass the {@link AccessibilityClickableSpan}. 478 * For use with R.id.accessibilityActionClickOnClickableSpan 479 * @hide 480 */ 481 public static final String ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN = 482 "android.view.accessibility.action.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN"; 483 484 // Focus types 485 486 /** 487 * The input focus. 488 */ 489 public static final int FOCUS_INPUT = 1; 490 491 /** 492 * The accessibility focus. 493 */ 494 public static final int FOCUS_ACCESSIBILITY = 2; 495 496 // Movement granularities 497 498 /** 499 * Movement granularity bit for traversing the text of a node by character. 500 */ 501 public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001; 502 503 /** 504 * Movement granularity bit for traversing the text of a node by word. 505 */ 506 public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002; 507 508 /** 509 * Movement granularity bit for traversing the text of a node by line. 510 */ 511 public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004; 512 513 /** 514 * Movement granularity bit for traversing the text of a node by paragraph. 515 */ 516 public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008; 517 518 /** 519 * Movement granularity bit for traversing the text of a node by page. 520 */ 521 public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010; 522 523 /** 524 * Key used to request and locate extra data for text character location. This key requests that 525 * an array of {@link android.graphics.RectF}s be added to the extras. This request is made with 526 * {@link #refreshWithExtraData(String, Bundle)}. The arguments taken by this request are two 527 * integers: {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX} and 528 * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH}. The starting index must be valid 529 * inside the CharSequence returned by {@link #getText()}, and the length must be positive. 530 * <p> 531 * The data can be retrieved from the {@code Bundle} returned by {@link #getExtras()} using this 532 * string as a key for {@link Bundle#getParcelableArray(String)}. The 533 * {@link android.graphics.RectF} will be null for characters that either do not exist or are 534 * off the screen. 535 * 536 * {@see #refreshWithExtraData(String, Bundle)} 537 */ 538 public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = 539 "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY"; 540 541 /** 542 * Integer argument specifying the start index of the requested text location data. Must be 543 * valid inside the CharSequence returned by {@link #getText()}. 544 * 545 * {@see EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY} 546 */ 547 public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = 548 "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX"; 549 550 /** 551 * Integer argument specifying the end index of the requested text location data. Must be 552 * positive. 553 * 554 * {@see EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY} 555 */ 556 public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = 557 "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH"; 558 559 /** @hide */ 560 public static final String EXTRA_DATA_REQUESTED_KEY = 561 "android.view.accessibility.AccessibilityNodeInfo.extra_data_requested"; 562 563 // Boolean attributes. 564 565 private static final int BOOLEAN_PROPERTY_CHECKABLE = 0x00000001; 566 567 private static final int BOOLEAN_PROPERTY_CHECKED = 0x00000002; 568 569 private static final int BOOLEAN_PROPERTY_FOCUSABLE = 0x00000004; 570 571 private static final int BOOLEAN_PROPERTY_FOCUSED = 0x00000008; 572 573 private static final int BOOLEAN_PROPERTY_SELECTED = 0x00000010; 574 575 private static final int BOOLEAN_PROPERTY_CLICKABLE = 0x00000020; 576 577 private static final int BOOLEAN_PROPERTY_LONG_CLICKABLE = 0x00000040; 578 579 private static final int BOOLEAN_PROPERTY_ENABLED = 0x00000080; 580 581 private static final int BOOLEAN_PROPERTY_PASSWORD = 0x00000100; 582 583 private static final int BOOLEAN_PROPERTY_SCROLLABLE = 0x00000200; 584 585 private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400; 586 587 private static final int BOOLEAN_PROPERTY_VISIBLE_TO_USER = 0x00000800; 588 589 private static final int BOOLEAN_PROPERTY_EDITABLE = 0x00001000; 590 591 private static final int BOOLEAN_PROPERTY_OPENS_POPUP = 0x00002000; 592 593 private static final int BOOLEAN_PROPERTY_DISMISSABLE = 0x00004000; 594 595 private static final int BOOLEAN_PROPERTY_MULTI_LINE = 0x00008000; 596 597 private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 0x00010000; 598 599 private static final int BOOLEAN_PROPERTY_CONTEXT_CLICKABLE = 0x00020000; 600 601 private static final int BOOLEAN_PROPERTY_IMPORTANCE = 0x0040000; 602 603 private static final int BOOLEAN_PROPERTY_IS_SHOWING_HINT = 0x0100000; 604 605 /** 606 * Bits that provide the id of a virtual descendant of a view. 607 */ 608 private static final long VIRTUAL_DESCENDANT_ID_MASK = 0xffffffff00000000L; 609 610 /** 611 * Bit shift of {@link #VIRTUAL_DESCENDANT_ID_MASK} to get to the id for a 612 * virtual descendant of a view. Such a descendant does not exist in the view 613 * hierarchy and is only reported via the accessibility APIs. 614 */ 615 private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32; 616 617 private static AtomicInteger sNumInstancesInUse; 618 619 /** 620 * Gets the accessibility view id which identifies a View in the view three. 621 * 622 * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}. 623 * @return The accessibility view id part of the node id. 624 * 625 * @hide 626 */ 627 public static int getAccessibilityViewId(long accessibilityNodeId) { 628 return (int) accessibilityNodeId; 629 } 630 631 /** 632 * Gets the virtual descendant id which identifies an imaginary view in a 633 * containing View. 634 * 635 * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}. 636 * @return The virtual view id part of the node id. 637 * 638 * @hide 639 */ 640 public static int getVirtualDescendantId(long accessibilityNodeId) { 641 return (int) ((accessibilityNodeId & VIRTUAL_DESCENDANT_ID_MASK) 642 >> VIRTUAL_DESCENDANT_ID_SHIFT); 643 } 644 645 /** 646 * Makes a node id by shifting the <code>virtualDescendantId</code> 647 * by {@link #VIRTUAL_DESCENDANT_ID_SHIFT} and taking 648 * the bitwise or with the <code>accessibilityViewId</code>. 649 * 650 * @param accessibilityViewId A View accessibility id. 651 * @param virtualDescendantId A virtual descendant id. 652 * @return The node id. 653 * 654 * @hide 655 */ 656 public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) { 657 // We changed the value for undefined node to positive due to wrong 658 // global id composition (two 32-bin ints into one 64-bit long) but 659 // the value used for the host node provider view has id -1 so we 660 // remap it here. 661 if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) { 662 virtualDescendantId = UNDEFINED_ITEM_ID; 663 } 664 return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId; 665 } 666 667 // Housekeeping. 668 private static final int MAX_POOL_SIZE = 50; 669 private static final SynchronizedPool<AccessibilityNodeInfo> sPool = 670 new SynchronizedPool<>(MAX_POOL_SIZE); 671 672 private boolean mSealed; 673 674 // Data. 675 private int mWindowId = UNDEFINED_ITEM_ID; 676 private long mSourceNodeId = ROOT_NODE_ID; 677 private long mParentNodeId = ROOT_NODE_ID; 678 private long mLabelForId = ROOT_NODE_ID; 679 private long mLabeledById = ROOT_NODE_ID; 680 private long mTraversalBefore = ROOT_NODE_ID; 681 private long mTraversalAfter = ROOT_NODE_ID; 682 683 private int mBooleanProperties; 684 private final Rect mBoundsInParent = new Rect(); 685 private final Rect mBoundsInScreen = new Rect(); 686 private int mDrawingOrderInParent; 687 688 private CharSequence mPackageName; 689 private CharSequence mClassName; 690 // Hidden, unparceled value used to hold the original value passed to setText 691 private CharSequence mOriginalText; 692 private CharSequence mText; 693 private CharSequence mHintText; 694 private CharSequence mError; 695 private CharSequence mContentDescription; 696 private String mViewIdResourceName; 697 private ArrayList<String> mExtraDataKeys; 698 699 private LongArray mChildNodeIds; 700 private ArrayList<AccessibilityAction> mActions; 701 702 private int mMaxTextLength = -1; 703 private int mMovementGranularities; 704 705 private int mTextSelectionStart = UNDEFINED_SELECTION_INDEX; 706 private int mTextSelectionEnd = UNDEFINED_SELECTION_INDEX; 707 private int mInputType = InputType.TYPE_NULL; 708 private int mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE; 709 710 private Bundle mExtras; 711 712 private int mConnectionId = UNDEFINED_CONNECTION_ID; 713 714 private RangeInfo mRangeInfo; 715 private CollectionInfo mCollectionInfo; 716 private CollectionItemInfo mCollectionItemInfo; 717 718 /** 719 * Hide constructor from clients. 720 */ 721 private AccessibilityNodeInfo() { 722 /* do nothing */ 723 } 724 725 /** 726 * Sets the source. 727 * <p> 728 * <strong>Note:</strong> Cannot be called from an 729 * {@link android.accessibilityservice.AccessibilityService}. 730 * This class is made immutable before being delivered to an AccessibilityService. 731 * </p> 732 * 733 * @param source The info source. 734 */ 735 public void setSource(View source) { 736 setSource(source, UNDEFINED_ITEM_ID); 737 } 738 739 /** 740 * Sets the source to be a virtual descendant of the given <code>root</code>. 741 * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root 742 * is set as the source. 743 * <p> 744 * A virtual descendant is an imaginary View that is reported as a part of the view 745 * hierarchy for accessibility purposes. This enables custom views that draw complex 746 * content to report themselves as a tree of virtual views, thus conveying their 747 * logical structure. 748 * </p> 749 * <p> 750 * <strong>Note:</strong> Cannot be called from an 751 * {@link android.accessibilityservice.AccessibilityService}. 752 * This class is made immutable before being delivered to an AccessibilityService. 753 * </p> 754 * 755 * @param root The root of the virtual subtree. 756 * @param virtualDescendantId The id of the virtual descendant. 757 */ 758 public void setSource(View root, int virtualDescendantId) { 759 enforceNotSealed(); 760 mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED_ITEM_ID; 761 final int rootAccessibilityViewId = 762 (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; 763 mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 764 } 765 766 /** 767 * Find the view that has the specified focus type. The search starts from 768 * the view represented by this node info. 769 * 770 * @param focus The focus to find. One of {@link #FOCUS_INPUT} or 771 * {@link #FOCUS_ACCESSIBILITY}. 772 * @return The node info of the focused view or null. 773 * 774 * @see #FOCUS_INPUT 775 * @see #FOCUS_ACCESSIBILITY 776 */ 777 public AccessibilityNodeInfo findFocus(int focus) { 778 enforceSealed(); 779 enforceValidFocusType(focus); 780 if (!canPerformRequestOverConnection(mSourceNodeId)) { 781 return null; 782 } 783 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId, 784 mSourceNodeId, focus); 785 } 786 787 /** 788 * Searches for the nearest view in the specified direction that can take 789 * the input focus. 790 * 791 * @param direction The direction. Can be one of: 792 * {@link View#FOCUS_DOWN}, 793 * {@link View#FOCUS_UP}, 794 * {@link View#FOCUS_LEFT}, 795 * {@link View#FOCUS_RIGHT}, 796 * {@link View#FOCUS_FORWARD}, 797 * {@link View#FOCUS_BACKWARD}. 798 * 799 * @return The node info for the view that can take accessibility focus. 800 */ 801 public AccessibilityNodeInfo focusSearch(int direction) { 802 enforceSealed(); 803 enforceValidFocusDirection(direction); 804 if (!canPerformRequestOverConnection(mSourceNodeId)) { 805 return null; 806 } 807 return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId, 808 mSourceNodeId, direction); 809 } 810 811 /** 812 * Gets the id of the window from which the info comes from. 813 * 814 * @return The window id. 815 */ 816 public int getWindowId() { 817 return mWindowId; 818 } 819 820 /** 821 * Refreshes this info with the latest state of the view it represents. 822 * <p> 823 * <strong>Note:</strong> If this method returns false this info is obsolete 824 * since it represents a view that is no longer in the view tree and should 825 * be recycled. 826 * </p> 827 * 828 * @param bypassCache Whether to bypass the cache. 829 * @return Whether the refresh succeeded. 830 * 831 * @hide 832 */ 833 public boolean refresh(Bundle arguments, boolean bypassCache) { 834 enforceSealed(); 835 if (!canPerformRequestOverConnection(mSourceNodeId)) { 836 return false; 837 } 838 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 839 AccessibilityNodeInfo refreshedInfo = client.findAccessibilityNodeInfoByAccessibilityId( 840 mConnectionId, mWindowId, mSourceNodeId, bypassCache, 0, arguments); 841 if (refreshedInfo == null) { 842 return false; 843 } 844 init(refreshedInfo); 845 refreshedInfo.recycle(); 846 return true; 847 } 848 849 /** 850 * Refreshes this info with the latest state of the view it represents. 851 * 852 * @return {@code true} if the refresh succeeded. {@code false} if the {@link View} represented 853 * by this node is no longer in the view tree (and thus this node is obsolete and should be 854 * recycled). 855 */ 856 public boolean refresh() { 857 return refresh(null, true); 858 } 859 860 /** 861 * Refreshes this info with the latest state of the view it represents, and request new 862 * data be added by the View. 863 * 864 * @param extraDataKey A bitmask of the extra data requested. Data that must be requested 865 * with this mechanism is generally expensive to retrieve, so should only be 866 * requested when needed. See 867 * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY} and 868 * {@link #getAvailableExtraData()}. 869 * @param args A bundle of arguments for the request. These depend on the particular request. 870 * 871 * @return {@code true} if the refresh succeeded. {@code false} if the {@link View} represented 872 * by this node is no longer in the view tree (and thus this node is obsolete and should be 873 * recycled). 874 */ 875 public boolean refreshWithExtraData(String extraDataKey, Bundle args) { 876 args.putString(EXTRA_DATA_REQUESTED_KEY, extraDataKey); 877 return refresh(args, true); 878 } 879 880 /** 881 * Returns the array containing the IDs of this node's children. 882 * 883 * @hide 884 */ 885 public LongArray getChildNodeIds() { 886 return mChildNodeIds; 887 } 888 889 /** 890 * Returns the id of the child at the specified index. 891 * 892 * @throws IndexOutOfBoundsException when index < 0 || index >= 893 * getChildCount() 894 * @hide 895 */ 896 public long getChildId(int index) { 897 if (mChildNodeIds == null) { 898 throw new IndexOutOfBoundsException(); 899 } 900 return mChildNodeIds.get(index); 901 } 902 903 /** 904 * Gets the number of children. 905 * 906 * @return The child count. 907 */ 908 public int getChildCount() { 909 return mChildNodeIds == null ? 0 : mChildNodeIds.size(); 910 } 911 912 /** 913 * Get the child at given index. 914 * <p> 915 * <strong>Note:</strong> It is a client responsibility to recycle the 916 * received info by calling {@link AccessibilityNodeInfo#recycle()} 917 * to avoid creating of multiple instances. 918 * </p> 919 * 920 * @param index The child index. 921 * @return The child node. 922 * 923 * @throws IllegalStateException If called outside of an AccessibilityService. 924 * 925 */ 926 public AccessibilityNodeInfo getChild(int index) { 927 enforceSealed(); 928 if (mChildNodeIds == null) { 929 return null; 930 } 931 if (!canPerformRequestOverConnection(mSourceNodeId)) { 932 return null; 933 } 934 final long childId = mChildNodeIds.get(index); 935 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 936 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId, 937 childId, false, FLAG_PREFETCH_DESCENDANTS, null); 938 } 939 940 /** 941 * Adds a child. 942 * <p> 943 * <strong>Note:</strong> Cannot be called from an 944 * {@link android.accessibilityservice.AccessibilityService}. 945 * This class is made immutable before being delivered to an AccessibilityService. 946 * </p> 947 * 948 * @param child The child. 949 * 950 * @throws IllegalStateException If called from an AccessibilityService. 951 */ 952 public void addChild(View child) { 953 addChildInternal(child, UNDEFINED_ITEM_ID, true); 954 } 955 956 /** 957 * Unchecked version of {@link #addChild(View)} that does not verify 958 * uniqueness. For framework use only. 959 * 960 * @hide 961 */ 962 public void addChildUnchecked(View child) { 963 addChildInternal(child, UNDEFINED_ITEM_ID, false); 964 } 965 966 /** 967 * Removes a child. If the child was not previously added to the node, 968 * calling this method has no effect. 969 * <p> 970 * <strong>Note:</strong> Cannot be called from an 971 * {@link android.accessibilityservice.AccessibilityService}. 972 * This class is made immutable before being delivered to an AccessibilityService. 973 * </p> 974 * 975 * @param child The child. 976 * @return true if the child was present 977 * 978 * @throws IllegalStateException If called from an AccessibilityService. 979 */ 980 public boolean removeChild(View child) { 981 return removeChild(child, UNDEFINED_ITEM_ID); 982 } 983 984 /** 985 * Adds a virtual child which is a descendant of the given <code>root</code>. 986 * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root 987 * is added as a child. 988 * <p> 989 * A virtual descendant is an imaginary View that is reported as a part of the view 990 * hierarchy for accessibility purposes. This enables custom views that draw complex 991 * content to report them selves as a tree of virtual views, thus conveying their 992 * logical structure. 993 * </p> 994 * 995 * @param root The root of the virtual subtree. 996 * @param virtualDescendantId The id of the virtual child. 997 */ 998 public void addChild(View root, int virtualDescendantId) { 999 addChildInternal(root, virtualDescendantId, true); 1000 } 1001 1002 private void addChildInternal(View root, int virtualDescendantId, boolean checked) { 1003 enforceNotSealed(); 1004 if (mChildNodeIds == null) { 1005 mChildNodeIds = new LongArray(); 1006 } 1007 final int rootAccessibilityViewId = 1008 (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; 1009 final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 1010 // If we're checking uniqueness and the ID already exists, abort. 1011 if (checked && mChildNodeIds.indexOf(childNodeId) >= 0) { 1012 return; 1013 } 1014 mChildNodeIds.add(childNodeId); 1015 } 1016 1017 /** 1018 * Removes a virtual child which is a descendant of the given 1019 * <code>root</code>. If the child was not previously added to the node, 1020 * calling this method has no effect. 1021 * 1022 * @param root The root of the virtual subtree. 1023 * @param virtualDescendantId The id of the virtual child. 1024 * @return true if the child was present 1025 * @see #addChild(View, int) 1026 */ 1027 public boolean removeChild(View root, int virtualDescendantId) { 1028 enforceNotSealed(); 1029 final LongArray childIds = mChildNodeIds; 1030 if (childIds == null) { 1031 return false; 1032 } 1033 final int rootAccessibilityViewId = 1034 (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; 1035 final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 1036 final int index = childIds.indexOf(childNodeId); 1037 if (index < 0) { 1038 return false; 1039 } 1040 childIds.remove(index); 1041 return true; 1042 } 1043 1044 /** 1045 * Gets the actions that can be performed on the node. 1046 */ 1047 public List<AccessibilityAction> getActionList() { 1048 if (mActions == null) { 1049 return Collections.emptyList(); 1050 } 1051 1052 return mActions; 1053 } 1054 1055 /** 1056 * Gets the actions that can be performed on the node. 1057 * 1058 * @return The bit mask of with actions. 1059 * 1060 * @see AccessibilityNodeInfo#ACTION_FOCUS 1061 * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS 1062 * @see AccessibilityNodeInfo#ACTION_SELECT 1063 * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION 1064 * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS 1065 * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS 1066 * @see AccessibilityNodeInfo#ACTION_CLICK 1067 * @see AccessibilityNodeInfo#ACTION_LONG_CLICK 1068 * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY 1069 * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY 1070 * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT 1071 * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT 1072 * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD 1073 * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD 1074 * 1075 * @deprecated Use {@link #getActionList()}. 1076 */ 1077 @Deprecated 1078 public int getActions() { 1079 int returnValue = 0; 1080 1081 if (mActions == null) { 1082 return returnValue; 1083 } 1084 1085 final int actionSize = mActions.size(); 1086 for (int i = 0; i < actionSize; i++) { 1087 int actionId = mActions.get(i).getId(); 1088 if (actionId <= LAST_LEGACY_STANDARD_ACTION) { 1089 returnValue |= actionId; 1090 } 1091 } 1092 1093 return returnValue; 1094 } 1095 1096 /** 1097 * Adds an action that can be performed on the node. 1098 * <p> 1099 * To add a standard action use the static constants on {@link AccessibilityAction}. 1100 * To add a custom action create a new {@link AccessibilityAction} by passing in a 1101 * resource id from your application as the action id and an optional label that 1102 * describes the action. To override one of the standard actions use as the action 1103 * id of a standard action id such as {@link #ACTION_CLICK} and an optional label that 1104 * describes the action. 1105 * </p> 1106 * <p> 1107 * <strong>Note:</strong> Cannot be called from an 1108 * {@link android.accessibilityservice.AccessibilityService}. 1109 * This class is made immutable before being delivered to an AccessibilityService. 1110 * </p> 1111 * 1112 * @param action The action. 1113 * 1114 * @throws IllegalStateException If called from an AccessibilityService. 1115 */ 1116 public void addAction(AccessibilityAction action) { 1117 enforceNotSealed(); 1118 1119 addActionUnchecked(action); 1120 } 1121 1122 private void addActionUnchecked(AccessibilityAction action) { 1123 if (action == null) { 1124 return; 1125 } 1126 1127 if (mActions == null) { 1128 mActions = new ArrayList<>(); 1129 } 1130 1131 mActions.remove(action); 1132 mActions.add(action); 1133 } 1134 1135 /** 1136 * Adds an action that can be performed on the node. 1137 * <p> 1138 * <strong>Note:</strong> Cannot be called from an 1139 * {@link android.accessibilityservice.AccessibilityService}. 1140 * This class is made immutable before being delivered to an AccessibilityService. 1141 * </p> 1142 * 1143 * @param action The action. 1144 * 1145 * @throws IllegalStateException If called from an AccessibilityService. 1146 * @throws IllegalArgumentException If the argument is not one of the standard actions. 1147 * 1148 * @deprecated This has been deprecated for {@link #addAction(AccessibilityAction)} 1149 */ 1150 @Deprecated 1151 public void addAction(int action) { 1152 enforceNotSealed(); 1153 1154 if ((action & ACTION_TYPE_MASK) != 0) { 1155 throw new IllegalArgumentException("Action is not a combination of the standard " + 1156 "actions: " + action); 1157 } 1158 1159 addLegacyStandardActions(action); 1160 } 1161 1162 /** 1163 * Removes an action that can be performed on the node. If the action was 1164 * not already added to the node, calling this method has no effect. 1165 * <p> 1166 * <strong>Note:</strong> Cannot be called from an 1167 * {@link android.accessibilityservice.AccessibilityService}. 1168 * This class is made immutable before being delivered to an AccessibilityService. 1169 * </p> 1170 * 1171 * @param action The action to be removed. 1172 * 1173 * @throws IllegalStateException If called from an AccessibilityService. 1174 * @deprecated Use {@link #removeAction(AccessibilityAction)} 1175 */ 1176 @Deprecated 1177 public void removeAction(int action) { 1178 enforceNotSealed(); 1179 1180 removeAction(getActionSingleton(action)); 1181 } 1182 1183 /** 1184 * Removes an action that can be performed on the node. If the action was 1185 * not already added to the node, calling this method has no effect. 1186 * <p> 1187 * <strong>Note:</strong> Cannot be called from an 1188 * {@link android.accessibilityservice.AccessibilityService}. 1189 * This class is made immutable before being delivered to an AccessibilityService. 1190 * </p> 1191 * 1192 * @param action The action to be removed. 1193 * @return The action removed from the list of actions. 1194 * 1195 * @throws IllegalStateException If called from an AccessibilityService. 1196 */ 1197 public boolean removeAction(AccessibilityAction action) { 1198 enforceNotSealed(); 1199 1200 if (mActions == null || action == null) { 1201 return false; 1202 } 1203 1204 return mActions.remove(action); 1205 } 1206 1207 /** 1208 * Gets the node before which this one is visited during traversal. A screen-reader 1209 * must visit the content of this node before the content of the one it precedes. 1210 * 1211 * @return The succeeding node if such or <code>null</code>. 1212 * 1213 * @see #setTraversalBefore(android.view.View) 1214 * @see #setTraversalBefore(android.view.View, int) 1215 */ 1216 public AccessibilityNodeInfo getTraversalBefore() { 1217 enforceSealed(); 1218 return getNodeForAccessibilityId(mTraversalBefore); 1219 } 1220 1221 /** 1222 * Sets the view before whose node this one should be visited during traversal. A 1223 * screen-reader must visit the content of this node before the content of the one 1224 * it precedes. 1225 * <p> 1226 * <strong>Note:</strong> Cannot be called from an 1227 * {@link android.accessibilityservice.AccessibilityService}. 1228 * This class is made immutable before being delivered to an AccessibilityService. 1229 * </p> 1230 * 1231 * @param view The view providing the preceding node. 1232 * 1233 * @see #getTraversalBefore() 1234 */ 1235 public void setTraversalBefore(View view) { 1236 setTraversalBefore(view, UNDEFINED_ITEM_ID); 1237 } 1238 1239 /** 1240 * Sets the node before which this one is visited during traversal. A screen-reader 1241 * must visit the content of this node before the content of the one it precedes. 1242 * The successor is a virtual descendant of the given <code>root</code>. If 1243 * <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root is set 1244 * as the successor. 1245 * <p> 1246 * A virtual descendant is an imaginary View that is reported as a part of the view 1247 * hierarchy for accessibility purposes. This enables custom views that draw complex 1248 * content to report them selves as a tree of virtual views, thus conveying their 1249 * logical structure. 1250 * </p> 1251 * <p> 1252 * <strong>Note:</strong> Cannot be called from an 1253 * {@link android.accessibilityservice.AccessibilityService}. 1254 * This class is made immutable before being delivered to an AccessibilityService. 1255 * </p> 1256 * 1257 * @param root The root of the virtual subtree. 1258 * @param virtualDescendantId The id of the virtual descendant. 1259 */ 1260 public void setTraversalBefore(View root, int virtualDescendantId) { 1261 enforceNotSealed(); 1262 final int rootAccessibilityViewId = (root != null) 1263 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; 1264 mTraversalBefore = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 1265 } 1266 1267 /** 1268 * Gets the node after which this one is visited in accessibility traversal. 1269 * A screen-reader must visit the content of the other node before the content 1270 * of this one. 1271 * 1272 * @return The succeeding node if such or <code>null</code>. 1273 * 1274 * @see #setTraversalAfter(android.view.View) 1275 * @see #setTraversalAfter(android.view.View, int) 1276 */ 1277 public AccessibilityNodeInfo getTraversalAfter() { 1278 enforceSealed(); 1279 return getNodeForAccessibilityId(mTraversalAfter); 1280 } 1281 1282 /** 1283 * Sets the view whose node is visited after this one in accessibility traversal. 1284 * A screen-reader must visit the content of the other node before the content 1285 * of this one. 1286 * <p> 1287 * <strong>Note:</strong> Cannot be called from an 1288 * {@link android.accessibilityservice.AccessibilityService}. 1289 * This class is made immutable before being delivered to an AccessibilityService. 1290 * </p> 1291 * 1292 * @param view The previous view. 1293 * 1294 * @see #getTraversalAfter() 1295 */ 1296 public void setTraversalAfter(View view) { 1297 setTraversalAfter(view, UNDEFINED_ITEM_ID); 1298 } 1299 1300 /** 1301 * Sets the node after which this one is visited in accessibility traversal. 1302 * A screen-reader must visit the content of the other node before the content 1303 * of this one. If <code>virtualDescendantId</code> equals to {@link View#NO_ID} 1304 * the root is set as the predecessor. 1305 * <p> 1306 * A virtual descendant is an imaginary View that is reported as a part of the view 1307 * hierarchy for accessibility purposes. This enables custom views that draw complex 1308 * content to report them selves as a tree of virtual views, thus conveying their 1309 * logical structure. 1310 * </p> 1311 * <p> 1312 * <strong>Note:</strong> Cannot be called from an 1313 * {@link android.accessibilityservice.AccessibilityService}. 1314 * This class is made immutable before being delivered to an AccessibilityService. 1315 * </p> 1316 * 1317 * @param root The root of the virtual subtree. 1318 * @param virtualDescendantId The id of the virtual descendant. 1319 */ 1320 public void setTraversalAfter(View root, int virtualDescendantId) { 1321 enforceNotSealed(); 1322 final int rootAccessibilityViewId = (root != null) 1323 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; 1324 mTraversalAfter = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 1325 } 1326 1327 /** 1328 * Get the extra data available for this node. 1329 * <p> 1330 * Some data that is useful for some accessibility services is expensive to compute, and would 1331 * place undue overhead on apps to compute all the time. That data can be requested with 1332 * {@link #refreshWithExtraData(String, Bundle)}. 1333 * 1334 * @return An unmodifiable list of keys corresponding to extra data that can be requested. 1335 * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY 1336 */ 1337 public List<String> getAvailableExtraData() { 1338 if (mExtraDataKeys != null) { 1339 return Collections.unmodifiableList(mExtraDataKeys); 1340 } else { 1341 return EMPTY_LIST; 1342 } 1343 } 1344 1345 /** 1346 * Set the extra data available for this node. 1347 * <p> 1348 * <strong>Note:</strong> When a {@code View} passes in a non-empty list, it promises that 1349 * it will populate the node's extras with corresponding pieces of information in 1350 * {@link View#addExtraDataToAccessibilityNodeInfo(AccessibilityNodeInfo, String, Bundle)}. 1351 * <p> 1352 * <strong>Note:</strong> Cannot be called from an 1353 * {@link android.accessibilityservice.AccessibilityService}. 1354 * This class is made immutable before being delivered to an AccessibilityService. 1355 * 1356 * @param extraDataKeys A list of types of extra data that are available. 1357 * @see #getAvailableExtraData() 1358 * 1359 * @throws IllegalStateException If called from an AccessibilityService. 1360 */ 1361 public void setAvailableExtraData(List<String> extraDataKeys) { 1362 enforceNotSealed(); 1363 mExtraDataKeys = new ArrayList<>(extraDataKeys); 1364 } 1365 1366 /** 1367 * Sets the maximum text length, or -1 for no limit. 1368 * <p> 1369 * Typically used to indicate that an editable text field has a limit on 1370 * the number of characters entered. 1371 * <p> 1372 * <strong>Note:</strong> Cannot be called from an 1373 * {@link android.accessibilityservice.AccessibilityService}. 1374 * This class is made immutable before being delivered to an AccessibilityService. 1375 * 1376 * @param max The maximum text length. 1377 * @see #getMaxTextLength() 1378 * 1379 * @throws IllegalStateException If called from an AccessibilityService. 1380 */ 1381 public void setMaxTextLength(int max) { 1382 enforceNotSealed(); 1383 mMaxTextLength = max; 1384 } 1385 1386 /** 1387 * Returns the maximum text length for this node. 1388 * 1389 * @return The maximum text length, or -1 for no limit. 1390 * @see #setMaxTextLength(int) 1391 */ 1392 public int getMaxTextLength() { 1393 return mMaxTextLength; 1394 } 1395 1396 /** 1397 * Sets the movement granularities for traversing the text of this node. 1398 * <p> 1399 * <strong>Note:</strong> Cannot be called from an 1400 * {@link android.accessibilityservice.AccessibilityService}. 1401 * This class is made immutable before being delivered to an AccessibilityService. 1402 * </p> 1403 * 1404 * @param granularities The bit mask with granularities. 1405 * 1406 * @throws IllegalStateException If called from an AccessibilityService. 1407 */ 1408 public void setMovementGranularities(int granularities) { 1409 enforceNotSealed(); 1410 mMovementGranularities = granularities; 1411 } 1412 1413 /** 1414 * Gets the movement granularities for traversing the text of this node. 1415 * 1416 * @return The bit mask with granularities. 1417 */ 1418 public int getMovementGranularities() { 1419 return mMovementGranularities; 1420 } 1421 1422 /** 1423 * Performs an action on the node. 1424 * <p> 1425 * <strong>Note:</strong> An action can be performed only if the request is made 1426 * from an {@link android.accessibilityservice.AccessibilityService}. 1427 * </p> 1428 * 1429 * @param action The action to perform. 1430 * @return True if the action was performed. 1431 * 1432 * @throws IllegalStateException If called outside of an AccessibilityService. 1433 */ 1434 public boolean performAction(int action) { 1435 enforceSealed(); 1436 if (!canPerformRequestOverConnection(mSourceNodeId)) { 1437 return false; 1438 } 1439 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 1440 return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, 1441 action, null); 1442 } 1443 1444 /** 1445 * Performs an action on the node. 1446 * <p> 1447 * <strong>Note:</strong> An action can be performed only if the request is made 1448 * from an {@link android.accessibilityservice.AccessibilityService}. 1449 * </p> 1450 * 1451 * @param action The action to perform. 1452 * @param arguments A bundle with additional arguments. 1453 * @return True if the action was performed. 1454 * 1455 * @throws IllegalStateException If called outside of an AccessibilityService. 1456 */ 1457 public boolean performAction(int action, Bundle arguments) { 1458 enforceSealed(); 1459 if (!canPerformRequestOverConnection(mSourceNodeId)) { 1460 return false; 1461 } 1462 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 1463 return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, 1464 action, arguments); 1465 } 1466 1467 /** 1468 * Finds {@link AccessibilityNodeInfo}s by text. The match is case 1469 * insensitive containment. The search is relative to this info i.e. 1470 * this info is the root of the traversed tree. 1471 * 1472 * <p> 1473 * <strong>Note:</strong> It is a client responsibility to recycle the 1474 * received info by calling {@link AccessibilityNodeInfo#recycle()} 1475 * to avoid creating of multiple instances. 1476 * </p> 1477 * 1478 * @param text The searched text. 1479 * @return A list of node info. 1480 */ 1481 public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) { 1482 enforceSealed(); 1483 if (!canPerformRequestOverConnection(mSourceNodeId)) { 1484 return Collections.emptyList(); 1485 } 1486 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 1487 return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId, 1488 text); 1489 } 1490 1491 /** 1492 * Finds {@link AccessibilityNodeInfo}s by the fully qualified view id's resource 1493 * name where a fully qualified id is of the from "package:id/id_resource_name". 1494 * For example, if the target application's package is "foo.bar" and the id 1495 * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz". 1496 * 1497 * <p> 1498 * <strong>Note:</strong> It is a client responsibility to recycle the 1499 * received info by calling {@link AccessibilityNodeInfo#recycle()} 1500 * to avoid creating of multiple instances. 1501 * </p> 1502 * <p> 1503 * <strong>Note:</strong> The primary usage of this API is for UI test automation 1504 * and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo} 1505 * the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} 1506 * flag when configuring his {@link android.accessibilityservice.AccessibilityService}. 1507 * </p> 1508 * 1509 * @param viewId The fully qualified resource name of the view id to find. 1510 * @return A list of node info. 1511 */ 1512 public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) { 1513 enforceSealed(); 1514 if (!canPerformRequestOverConnection(mSourceNodeId)) { 1515 return Collections.emptyList(); 1516 } 1517 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 1518 return client.findAccessibilityNodeInfosByViewId(mConnectionId, mWindowId, mSourceNodeId, 1519 viewId); 1520 } 1521 1522 /** 1523 * Gets the window to which this node belongs. 1524 * 1525 * @return The window. 1526 * 1527 * @see android.accessibilityservice.AccessibilityService#getWindows() 1528 */ 1529 public AccessibilityWindowInfo getWindow() { 1530 enforceSealed(); 1531 if (!canPerformRequestOverConnection(mSourceNodeId)) { 1532 return null; 1533 } 1534 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 1535 return client.getWindow(mConnectionId, mWindowId); 1536 } 1537 1538 /** 1539 * Gets the parent. 1540 * <p> 1541 * <strong>Note:</strong> It is a client responsibility to recycle the 1542 * received info by calling {@link AccessibilityNodeInfo#recycle()} 1543 * to avoid creating of multiple instances. 1544 * </p> 1545 * 1546 * @return The parent. 1547 */ 1548 public AccessibilityNodeInfo getParent() { 1549 enforceSealed(); 1550 return getNodeForAccessibilityId(mParentNodeId); 1551 } 1552 1553 /** 1554 * @return The parent node id. 1555 * 1556 * @hide 1557 */ 1558 public long getParentNodeId() { 1559 return mParentNodeId; 1560 } 1561 1562 /** 1563 * Sets the parent. 1564 * <p> 1565 * <strong>Note:</strong> Cannot be called from an 1566 * {@link android.accessibilityservice.AccessibilityService}. 1567 * This class is made immutable before being delivered to an AccessibilityService. 1568 * </p> 1569 * 1570 * @param parent The parent. 1571 * 1572 * @throws IllegalStateException If called from an AccessibilityService. 1573 */ 1574 public void setParent(View parent) { 1575 setParent(parent, UNDEFINED_ITEM_ID); 1576 } 1577 1578 /** 1579 * Sets the parent to be a virtual descendant of the given <code>root</code>. 1580 * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root 1581 * is set as the parent. 1582 * <p> 1583 * A virtual descendant is an imaginary View that is reported as a part of the view 1584 * hierarchy for accessibility purposes. This enables custom views that draw complex 1585 * content to report them selves as a tree of virtual views, thus conveying their 1586 * logical structure. 1587 * </p> 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 root The root of the virtual subtree. 1595 * @param virtualDescendantId The id of the virtual descendant. 1596 */ 1597 public void setParent(View root, int virtualDescendantId) { 1598 enforceNotSealed(); 1599 final int rootAccessibilityViewId = 1600 (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; 1601 mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 1602 } 1603 1604 /** 1605 * Gets the node bounds in parent coordinates. 1606 * 1607 * @param outBounds The output node bounds. 1608 */ 1609 public void getBoundsInParent(Rect outBounds) { 1610 outBounds.set(mBoundsInParent.left, mBoundsInParent.top, 1611 mBoundsInParent.right, mBoundsInParent.bottom); 1612 } 1613 1614 /** 1615 * Sets the node bounds in parent coordinates. 1616 * <p> 1617 * <strong>Note:</strong> Cannot be called from an 1618 * {@link android.accessibilityservice.AccessibilityService}. 1619 * This class is made immutable before being delivered to an AccessibilityService. 1620 * </p> 1621 * 1622 * @param bounds The node bounds. 1623 * 1624 * @throws IllegalStateException If called from an AccessibilityService. 1625 */ 1626 public void setBoundsInParent(Rect bounds) { 1627 enforceNotSealed(); 1628 mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom); 1629 } 1630 1631 /** 1632 * Gets the node bounds in screen coordinates. 1633 * 1634 * @param outBounds The output node bounds. 1635 */ 1636 public void getBoundsInScreen(Rect outBounds) { 1637 outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top, 1638 mBoundsInScreen.right, mBoundsInScreen.bottom); 1639 } 1640 1641 /** 1642 * Returns the actual rect containing the node bounds in screen coordinates. 1643 * 1644 * @hide Not safe to expose outside the framework. 1645 */ 1646 public Rect getBoundsInScreen() { 1647 return mBoundsInScreen; 1648 } 1649 1650 /** 1651 * Sets the node bounds in screen coordinates. 1652 * <p> 1653 * <strong>Note:</strong> Cannot be called from an 1654 * {@link android.accessibilityservice.AccessibilityService}. 1655 * This class is made immutable before being delivered to an AccessibilityService. 1656 * </p> 1657 * 1658 * @param bounds The node bounds. 1659 * 1660 * @throws IllegalStateException If called from an AccessibilityService. 1661 */ 1662 public void setBoundsInScreen(Rect bounds) { 1663 enforceNotSealed(); 1664 mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom); 1665 } 1666 1667 /** 1668 * Gets whether this node is checkable. 1669 * 1670 * @return True if the node is checkable. 1671 */ 1672 public boolean isCheckable() { 1673 return getBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE); 1674 } 1675 1676 /** 1677 * Sets whether this node is checkable. 1678 * <p> 1679 * <strong>Note:</strong> Cannot be called from an 1680 * {@link android.accessibilityservice.AccessibilityService}. 1681 * This class is made immutable before being delivered to an AccessibilityService. 1682 * </p> 1683 * 1684 * @param checkable True if the node is checkable. 1685 * 1686 * @throws IllegalStateException If called from an AccessibilityService. 1687 */ 1688 public void setCheckable(boolean checkable) { 1689 setBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE, checkable); 1690 } 1691 1692 /** 1693 * Gets whether this node is checked. 1694 * 1695 * @return True if the node is checked. 1696 */ 1697 public boolean isChecked() { 1698 return getBooleanProperty(BOOLEAN_PROPERTY_CHECKED); 1699 } 1700 1701 /** 1702 * Sets whether this node is checked. 1703 * <p> 1704 * <strong>Note:</strong> Cannot be called from an 1705 * {@link android.accessibilityservice.AccessibilityService}. 1706 * This class is made immutable before being delivered to an AccessibilityService. 1707 * </p> 1708 * 1709 * @param checked True if the node is checked. 1710 * 1711 * @throws IllegalStateException If called from an AccessibilityService. 1712 */ 1713 public void setChecked(boolean checked) { 1714 setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked); 1715 } 1716 1717 /** 1718 * Gets whether this node is focusable. 1719 * 1720 * @return True if the node is focusable. 1721 */ 1722 public boolean isFocusable() { 1723 return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE); 1724 } 1725 1726 /** 1727 * Sets whether this node is focusable. 1728 * <p> 1729 * <strong>Note:</strong> Cannot be called from an 1730 * {@link android.accessibilityservice.AccessibilityService}. 1731 * This class is made immutable before being delivered to an AccessibilityService. 1732 * </p> 1733 * 1734 * @param focusable True if the node is focusable. 1735 * 1736 * @throws IllegalStateException If called from an AccessibilityService. 1737 */ 1738 public void setFocusable(boolean focusable) { 1739 setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable); 1740 } 1741 1742 /** 1743 * Gets whether this node is focused. 1744 * 1745 * @return True if the node is focused. 1746 */ 1747 public boolean isFocused() { 1748 return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED); 1749 } 1750 1751 /** 1752 * Sets whether this node is focused. 1753 * <p> 1754 * <strong>Note:</strong> Cannot be called from an 1755 * {@link android.accessibilityservice.AccessibilityService}. 1756 * This class is made immutable before being delivered to an AccessibilityService. 1757 * </p> 1758 * 1759 * @param focused True if the node is focused. 1760 * 1761 * @throws IllegalStateException If called from an AccessibilityService. 1762 */ 1763 public void setFocused(boolean focused) { 1764 setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused); 1765 } 1766 1767 /** 1768 * Gets whether this node is visible to the user. 1769 * 1770 * @return Whether the node is visible to the user. 1771 */ 1772 public boolean isVisibleToUser() { 1773 return getBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER); 1774 } 1775 1776 /** 1777 * Sets whether this node is visible to the user. 1778 * <p> 1779 * <strong>Note:</strong> Cannot be called from an 1780 * {@link android.accessibilityservice.AccessibilityService}. 1781 * This class is made immutable before being delivered to an AccessibilityService. 1782 * </p> 1783 * 1784 * @param visibleToUser Whether the node is visible to the user. 1785 * 1786 * @throws IllegalStateException If called from an AccessibilityService. 1787 */ 1788 public void setVisibleToUser(boolean visibleToUser) { 1789 setBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER, visibleToUser); 1790 } 1791 1792 /** 1793 * Gets whether this node is accessibility focused. 1794 * 1795 * @return True if the node is accessibility focused. 1796 */ 1797 public boolean isAccessibilityFocused() { 1798 return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED); 1799 } 1800 1801 /** 1802 * Sets whether this node is accessibility focused. 1803 * <p> 1804 * <strong>Note:</strong> Cannot be called from an 1805 * {@link android.accessibilityservice.AccessibilityService}. 1806 * This class is made immutable before being delivered to an AccessibilityService. 1807 * </p> 1808 * 1809 * @param focused True if the node is accessibility focused. 1810 * 1811 * @throws IllegalStateException If called from an AccessibilityService. 1812 */ 1813 public void setAccessibilityFocused(boolean focused) { 1814 setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused); 1815 } 1816 1817 /** 1818 * Gets whether this node is selected. 1819 * 1820 * @return True if the node is selected. 1821 */ 1822 public boolean isSelected() { 1823 return getBooleanProperty(BOOLEAN_PROPERTY_SELECTED); 1824 } 1825 1826 /** 1827 * Sets whether this node is selected. 1828 * <p> 1829 * <strong>Note:</strong> Cannot be called from an 1830 * {@link android.accessibilityservice.AccessibilityService}. 1831 * This class is made immutable before being delivered to an AccessibilityService. 1832 * </p> 1833 * 1834 * @param selected True if the node is selected. 1835 * 1836 * @throws IllegalStateException If called from an AccessibilityService. 1837 */ 1838 public void setSelected(boolean selected) { 1839 setBooleanProperty(BOOLEAN_PROPERTY_SELECTED, selected); 1840 } 1841 1842 /** 1843 * Gets whether this node is clickable. 1844 * 1845 * @return True if the node is clickable. 1846 */ 1847 public boolean isClickable() { 1848 return getBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE); 1849 } 1850 1851 /** 1852 * Sets whether this node is clickable. 1853 * <p> 1854 * <strong>Note:</strong> Cannot be called from an 1855 * {@link android.accessibilityservice.AccessibilityService}. 1856 * This class is made immutable before being delivered to an AccessibilityService. 1857 * </p> 1858 * 1859 * @param clickable True if the node is clickable. 1860 * 1861 * @throws IllegalStateException If called from an AccessibilityService. 1862 */ 1863 public void setClickable(boolean clickable) { 1864 setBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE, clickable); 1865 } 1866 1867 /** 1868 * Gets whether this node is long clickable. 1869 * 1870 * @return True if the node is long clickable. 1871 */ 1872 public boolean isLongClickable() { 1873 return getBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE); 1874 } 1875 1876 /** 1877 * Sets whether this node is long clickable. 1878 * <p> 1879 * <strong>Note:</strong> Cannot be called from an 1880 * {@link android.accessibilityservice.AccessibilityService}. 1881 * This class is made immutable before being delivered to an AccessibilityService. 1882 * </p> 1883 * 1884 * @param longClickable True if the node is long clickable. 1885 * 1886 * @throws IllegalStateException If called from an AccessibilityService. 1887 */ 1888 public void setLongClickable(boolean longClickable) { 1889 setBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE, longClickable); 1890 } 1891 1892 /** 1893 * Gets whether this node is enabled. 1894 * 1895 * @return True if the node is enabled. 1896 */ 1897 public boolean isEnabled() { 1898 return getBooleanProperty(BOOLEAN_PROPERTY_ENABLED); 1899 } 1900 1901 /** 1902 * Sets whether this node is enabled. 1903 * <p> 1904 * <strong>Note:</strong> Cannot be called from an 1905 * {@link android.accessibilityservice.AccessibilityService}. 1906 * This class is made immutable before being delivered to an AccessibilityService. 1907 * </p> 1908 * 1909 * @param enabled True if the node is enabled. 1910 * 1911 * @throws IllegalStateException If called from an AccessibilityService. 1912 */ 1913 public void setEnabled(boolean enabled) { 1914 setBooleanProperty(BOOLEAN_PROPERTY_ENABLED, enabled); 1915 } 1916 1917 /** 1918 * Gets whether this node is a password. 1919 * 1920 * @return True if the node is a password. 1921 */ 1922 public boolean isPassword() { 1923 return getBooleanProperty(BOOLEAN_PROPERTY_PASSWORD); 1924 } 1925 1926 /** 1927 * Sets whether this node is a password. 1928 * <p> 1929 * <strong>Note:</strong> Cannot be called from an 1930 * {@link android.accessibilityservice.AccessibilityService}. 1931 * This class is made immutable before being delivered to an AccessibilityService. 1932 * </p> 1933 * 1934 * @param password True if the node is a password. 1935 * 1936 * @throws IllegalStateException If called from an AccessibilityService. 1937 */ 1938 public void setPassword(boolean password) { 1939 setBooleanProperty(BOOLEAN_PROPERTY_PASSWORD, password); 1940 } 1941 1942 /** 1943 * Gets if the node is scrollable. 1944 * 1945 * @return True if the node is scrollable, false otherwise. 1946 */ 1947 public boolean isScrollable() { 1948 return getBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE); 1949 } 1950 1951 /** 1952 * Sets if the node is scrollable. 1953 * <p> 1954 * <strong>Note:</strong> Cannot be called from an 1955 * {@link android.accessibilityservice.AccessibilityService}. 1956 * This class is made immutable before being delivered to an AccessibilityService. 1957 * </p> 1958 * 1959 * @param scrollable True if the node is scrollable, false otherwise. 1960 * 1961 * @throws IllegalStateException If called from an AccessibilityService. 1962 */ 1963 public void setScrollable(boolean scrollable) { 1964 setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable); 1965 } 1966 1967 /** 1968 * Gets if the node is editable. 1969 * 1970 * @return True if the node is editable, false otherwise. 1971 */ 1972 public boolean isEditable() { 1973 return getBooleanProperty(BOOLEAN_PROPERTY_EDITABLE); 1974 } 1975 1976 /** 1977 * Sets whether this node is editable. 1978 * <p> 1979 * <strong>Note:</strong> Cannot be called from an 1980 * {@link android.accessibilityservice.AccessibilityService}. 1981 * This class is made immutable before being delivered to an AccessibilityService. 1982 * </p> 1983 * 1984 * @param editable True if the node is editable. 1985 * 1986 * @throws IllegalStateException If called from an AccessibilityService. 1987 */ 1988 public void setEditable(boolean editable) { 1989 setBooleanProperty(BOOLEAN_PROPERTY_EDITABLE, editable); 1990 } 1991 1992 /** 1993 * Get the drawing order of the view corresponding it this node. 1994 * <p> 1995 * Drawing order is determined only within the node's parent, so this index is only relative 1996 * to its siblings. 1997 * <p> 1998 * In some cases, the drawing order is essentially simultaneous, so it is possible for two 1999 * siblings to return the same value. It is also possible that values will be skipped. 2000 * 2001 * @return The drawing position of the view corresponding to this node relative to its siblings. 2002 */ 2003 public int getDrawingOrder() { 2004 return mDrawingOrderInParent; 2005 } 2006 2007 /** 2008 * Set the drawing order of the view corresponding it this node. 2009 * 2010 * <p> 2011 * <strong>Note:</strong> Cannot be called from an 2012 * {@link android.accessibilityservice.AccessibilityService}. 2013 * This class is made immutable before being delivered to an AccessibilityService. 2014 * </p> 2015 * @param drawingOrderInParent 2016 * @throws IllegalStateException If called from an AccessibilityService. 2017 */ 2018 public void setDrawingOrder(int drawingOrderInParent) { 2019 enforceNotSealed(); 2020 mDrawingOrderInParent = drawingOrderInParent; 2021 } 2022 2023 /** 2024 * Gets the collection info if the node is a collection. A collection 2025 * child is always a collection item. 2026 * 2027 * @return The collection info. 2028 */ 2029 public CollectionInfo getCollectionInfo() { 2030 return mCollectionInfo; 2031 } 2032 2033 /** 2034 * Sets the collection info if the node is a collection. A collection 2035 * child is always a collection item. 2036 * <p> 2037 * <strong>Note:</strong> Cannot be called from an 2038 * {@link android.accessibilityservice.AccessibilityService}. 2039 * This class is made immutable before being delivered to an AccessibilityService. 2040 * </p> 2041 * 2042 * @param collectionInfo The collection info. 2043 */ 2044 public void setCollectionInfo(CollectionInfo collectionInfo) { 2045 enforceNotSealed(); 2046 mCollectionInfo = collectionInfo; 2047 } 2048 2049 /** 2050 * Gets the collection item info if the node is a collection item. A collection 2051 * item is always a child of a collection. 2052 * 2053 * @return The collection item info. 2054 */ 2055 public CollectionItemInfo getCollectionItemInfo() { 2056 return mCollectionItemInfo; 2057 } 2058 2059 /** 2060 * Sets the collection item info if the node is a collection item. A collection 2061 * item is always a child of a collection. 2062 * <p> 2063 * <strong>Note:</strong> Cannot be called from an 2064 * {@link android.accessibilityservice.AccessibilityService}. 2065 * This class is made immutable before being delivered to an AccessibilityService. 2066 * </p> 2067 */ 2068 public void setCollectionItemInfo(CollectionItemInfo collectionItemInfo) { 2069 enforceNotSealed(); 2070 mCollectionItemInfo = collectionItemInfo; 2071 } 2072 2073 /** 2074 * Gets the range info if this node is a range. 2075 * 2076 * @return The range. 2077 */ 2078 public RangeInfo getRangeInfo() { 2079 return mRangeInfo; 2080 } 2081 2082 /** 2083 * Sets the range info if this node is a range. 2084 * <p> 2085 * <strong>Note:</strong> Cannot be called from an 2086 * {@link android.accessibilityservice.AccessibilityService}. 2087 * This class is made immutable before being delivered to an AccessibilityService. 2088 * </p> 2089 * 2090 * @param rangeInfo The range info. 2091 */ 2092 public void setRangeInfo(RangeInfo rangeInfo) { 2093 enforceNotSealed(); 2094 mRangeInfo = rangeInfo; 2095 } 2096 2097 /** 2098 * Gets if the content of this node is invalid. For example, 2099 * a date is not well-formed. 2100 * 2101 * @return If the node content is invalid. 2102 */ 2103 public boolean isContentInvalid() { 2104 return getBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID); 2105 } 2106 2107 /** 2108 * Sets if the content of this node is invalid. For example, 2109 * a date is not well-formed. 2110 * <p> 2111 * <strong>Note:</strong> Cannot be called from an 2112 * {@link android.accessibilityservice.AccessibilityService}. 2113 * This class is made immutable before being delivered to an AccessibilityService. 2114 * </p> 2115 * 2116 * @param contentInvalid If the node content is invalid. 2117 */ 2118 public void setContentInvalid(boolean contentInvalid) { 2119 setBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID, contentInvalid); 2120 } 2121 2122 /** 2123 * Gets whether this node is context clickable. 2124 * 2125 * @return True if the node is context clickable. 2126 */ 2127 public boolean isContextClickable() { 2128 return getBooleanProperty(BOOLEAN_PROPERTY_CONTEXT_CLICKABLE); 2129 } 2130 2131 /** 2132 * Sets whether this node is context clickable. 2133 * <p> 2134 * <strong>Note:</strong> Cannot be called from an 2135 * {@link android.accessibilityservice.AccessibilityService}. This class is made immutable 2136 * before being delivered to an AccessibilityService. 2137 * </p> 2138 * 2139 * @param contextClickable True if the node is context clickable. 2140 * @throws IllegalStateException If called from an AccessibilityService. 2141 */ 2142 public void setContextClickable(boolean contextClickable) { 2143 setBooleanProperty(BOOLEAN_PROPERTY_CONTEXT_CLICKABLE, contextClickable); 2144 } 2145 2146 /** 2147 * Gets the node's live region mode. 2148 * <p> 2149 * A live region is a node that contains information that is important for 2150 * the user and when it changes the user should be notified. For example, 2151 * in a login screen with a TextView that displays an "incorrect password" 2152 * notification, that view should be marked as a live region with mode 2153 * {@link View#ACCESSIBILITY_LIVE_REGION_POLITE}. 2154 * <p> 2155 * It is the responsibility of the accessibility service to monitor 2156 * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events indicating 2157 * changes to live region nodes and their children. 2158 * 2159 * @return The live region mode, or 2160 * {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a 2161 * live region. 2162 * @see android.view.View#getAccessibilityLiveRegion() 2163 */ 2164 public int getLiveRegion() { 2165 return mLiveRegion; 2166 } 2167 2168 /** 2169 * Sets the node's live region mode. 2170 * <p> 2171 * <strong>Note:</strong> Cannot be called from an 2172 * {@link android.accessibilityservice.AccessibilityService}. This class is 2173 * made immutable before being delivered to an AccessibilityService. 2174 * 2175 * @param mode The live region mode, or 2176 * {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a 2177 * live region. 2178 * @see android.view.View#setAccessibilityLiveRegion(int) 2179 */ 2180 public void setLiveRegion(int mode) { 2181 enforceNotSealed(); 2182 mLiveRegion = mode; 2183 } 2184 2185 /** 2186 * Gets if the node is a multi line editable text. 2187 * 2188 * @return True if the node is multi line. 2189 */ 2190 public boolean isMultiLine() { 2191 return getBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE); 2192 } 2193 2194 /** 2195 * Sets if the node is a multi line editable text. 2196 * <p> 2197 * <strong>Note:</strong> Cannot be called from an 2198 * {@link android.accessibilityservice.AccessibilityService}. 2199 * This class is made immutable before being delivered to an AccessibilityService. 2200 * </p> 2201 * 2202 * @param multiLine True if the node is multi line. 2203 */ 2204 public void setMultiLine(boolean multiLine) { 2205 setBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE, multiLine); 2206 } 2207 2208 /** 2209 * Gets if this node opens a popup or a dialog. 2210 * 2211 * @return If the the node opens a popup. 2212 */ 2213 public boolean canOpenPopup() { 2214 return getBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP); 2215 } 2216 2217 /** 2218 * Sets if this node opens a popup or a dialog. 2219 * <p> 2220 * <strong>Note:</strong> Cannot be called from an 2221 * {@link android.accessibilityservice.AccessibilityService}. 2222 * This class is made immutable before being delivered to an AccessibilityService. 2223 * </p> 2224 * 2225 * @param opensPopup If the the node opens a popup. 2226 */ 2227 public void setCanOpenPopup(boolean opensPopup) { 2228 enforceNotSealed(); 2229 setBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP, opensPopup); 2230 } 2231 2232 /** 2233 * Gets if the node can be dismissed. 2234 * 2235 * @return If the node can be dismissed. 2236 */ 2237 public boolean isDismissable() { 2238 return getBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE); 2239 } 2240 2241 /** 2242 * Sets if the node can be dismissed. 2243 * <p> 2244 * <strong>Note:</strong> Cannot be called from an 2245 * {@link android.accessibilityservice.AccessibilityService}. 2246 * This class is made immutable before being delivered to an AccessibilityService. 2247 * </p> 2248 * 2249 * @param dismissable If the node can be dismissed. 2250 */ 2251 public void setDismissable(boolean dismissable) { 2252 setBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE, dismissable); 2253 } 2254 2255 /** 2256 * Returns whether the node originates from a view considered important for accessibility. 2257 * 2258 * @return {@code true} if the node originates from a view considered important for 2259 * accessibility, {@code false} otherwise 2260 * 2261 * @see View#isImportantForAccessibility() 2262 */ 2263 public boolean isImportantForAccessibility() { 2264 return getBooleanProperty(BOOLEAN_PROPERTY_IMPORTANCE); 2265 } 2266 2267 /** 2268 * Sets whether the node is considered important for accessibility. 2269 * <p> 2270 * <strong>Note:</strong> Cannot be called from an 2271 * {@link android.accessibilityservice.AccessibilityService}. 2272 * This class is made immutable before being delivered to an AccessibilityService. 2273 * </p> 2274 * 2275 * @param important {@code true} if the node is considered important for accessibility, 2276 * {@code false} otherwise 2277 */ 2278 public void setImportantForAccessibility(boolean important) { 2279 setBooleanProperty(BOOLEAN_PROPERTY_IMPORTANCE, important); 2280 } 2281 2282 /** 2283 * Returns whether the node's text represents a hint for the user to enter text. It should only 2284 * be {@code true} if the node has editable text. 2285 * 2286 * @return {@code true} if the text in the node represents a hint to the user, {@code false} 2287 * otherwise. 2288 */ 2289 public boolean isShowingHintText() { 2290 return getBooleanProperty(BOOLEAN_PROPERTY_IS_SHOWING_HINT); 2291 } 2292 2293 /** 2294 * Sets whether the node's text represents a hint for the user to enter text. It should only 2295 * be {@code true} if the node has editable text. 2296 * <p> 2297 * <strong>Note:</strong> Cannot be called from an 2298 * {@link android.accessibilityservice.AccessibilityService}. 2299 * This class is made immutable before being delivered to an AccessibilityService. 2300 * </p> 2301 * 2302 * @param showingHintText {@code true} if the text in the node represents a hint to the user, 2303 * {@code false} otherwise. 2304 */ 2305 public void setShowingHintText(boolean showingHintText) { 2306 setBooleanProperty(BOOLEAN_PROPERTY_IS_SHOWING_HINT, showingHintText); 2307 } 2308 2309 /** 2310 * Gets the package this node comes from. 2311 * 2312 * @return The package name. 2313 */ 2314 public CharSequence getPackageName() { 2315 return mPackageName; 2316 } 2317 2318 /** 2319 * Sets the package this node comes from. 2320 * <p> 2321 * <strong>Note:</strong> Cannot be called from an 2322 * {@link android.accessibilityservice.AccessibilityService}. 2323 * This class is made immutable before being delivered to an AccessibilityService. 2324 * </p> 2325 * 2326 * @param packageName The package name. 2327 * 2328 * @throws IllegalStateException If called from an AccessibilityService. 2329 */ 2330 public void setPackageName(CharSequence packageName) { 2331 enforceNotSealed(); 2332 mPackageName = packageName; 2333 } 2334 2335 /** 2336 * Gets the class this node comes from. 2337 * 2338 * @return The class name. 2339 */ 2340 public CharSequence getClassName() { 2341 return mClassName; 2342 } 2343 2344 /** 2345 * Sets the class this node comes from. 2346 * <p> 2347 * <strong>Note:</strong> Cannot be called from an 2348 * {@link android.accessibilityservice.AccessibilityService}. 2349 * This class is made immutable before being delivered to an AccessibilityService. 2350 * </p> 2351 * 2352 * @param className The class name. 2353 * 2354 * @throws IllegalStateException If called from an AccessibilityService. 2355 */ 2356 public void setClassName(CharSequence className) { 2357 enforceNotSealed(); 2358 mClassName = className; 2359 } 2360 2361 /** 2362 * Gets the text of this node. 2363 * <p> 2364 * <strong>Note:</strong> If the text contains {@link ClickableSpan}s or {@link URLSpan}s, 2365 * these spans will have been replaced with ones whose {@link ClickableSpan#onClick(View)} 2366 * can be called from an {@link AccessibilityService}. When called from a service, the 2367 * {@link View} argument is ignored and the corresponding span will be found on the view that 2368 * this {@code AccessibilityNodeInfo} represents and called with that view as its argument. 2369 * <p> 2370 * This treatment of {@link ClickableSpan}s means that the text returned from this method may 2371 * different slightly one passed to {@link #setText(CharSequence)}, although they will be 2372 * equivalent according to {@link TextUtils#equals(CharSequence, CharSequence)}. The 2373 * {@link ClickableSpan#onClick(View)} of any spans, however, will generally not work outside 2374 * of an accessibility service. 2375 * </p> 2376 * 2377 * @return The text. 2378 */ 2379 public CharSequence getText() { 2380 // Attach this node to any spans that need it 2381 if (mText instanceof Spanned) { 2382 Spanned spanned = (Spanned) mText; 2383 AccessibilityClickableSpan[] clickableSpans = 2384 spanned.getSpans(0, mText.length(), AccessibilityClickableSpan.class); 2385 for (int i = 0; i < clickableSpans.length; i++) { 2386 clickableSpans[i].setAccessibilityNodeInfo(this); 2387 } 2388 AccessibilityURLSpan[] urlSpans = 2389 spanned.getSpans(0, mText.length(), AccessibilityURLSpan.class); 2390 for (int i = 0; i < urlSpans.length; i++) { 2391 urlSpans[i].setAccessibilityNodeInfo(this); 2392 } 2393 } 2394 return mText; 2395 } 2396 2397 /** 2398 * Get the text passed to setText before any changes to the spans. 2399 * @hide 2400 */ 2401 public CharSequence getOriginalText() { 2402 return mOriginalText; 2403 } 2404 2405 /** 2406 * Sets the text of this node. 2407 * <p> 2408 * <strong>Note:</strong> Cannot be called from an 2409 * {@link android.accessibilityservice.AccessibilityService}. 2410 * This class is made immutable before being delivered to an AccessibilityService. 2411 * </p> 2412 * 2413 * @param text The text. 2414 * 2415 * @throws IllegalStateException If called from an AccessibilityService. 2416 */ 2417 public void setText(CharSequence text) { 2418 enforceNotSealed(); 2419 mOriginalText = text; 2420 // Replace any ClickableSpans in mText with placeholders 2421 if (text instanceof Spanned) { 2422 ClickableSpan[] spans = 2423 ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class); 2424 if (spans.length > 0) { 2425 Spannable spannable = new SpannableStringBuilder(text); 2426 for (int i = 0; i < spans.length; i++) { 2427 ClickableSpan span = spans[i]; 2428 if ((span instanceof AccessibilityClickableSpan) 2429 || (span instanceof AccessibilityURLSpan)) { 2430 // We've already done enough 2431 break; 2432 } 2433 int spanToReplaceStart = spannable.getSpanStart(span); 2434 int spanToReplaceEnd = spannable.getSpanEnd(span); 2435 int spanToReplaceFlags = spannable.getSpanFlags(span); 2436 spannable.removeSpan(span); 2437 ClickableSpan replacementSpan = (span instanceof URLSpan) 2438 ? new AccessibilityURLSpan((URLSpan) span) 2439 : new AccessibilityClickableSpan(span.getId()); 2440 spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd, 2441 spanToReplaceFlags); 2442 } 2443 mText = spannable; 2444 return; 2445 } 2446 } 2447 mText = (text == null) ? null : text.subSequence(0, text.length()); 2448 } 2449 2450 /** 2451 * Gets the hint text of this node. Only applies to nodes where text can be entered. 2452 * 2453 * @return The hint text. 2454 */ 2455 public CharSequence getHintText() { 2456 return mHintText; 2457 } 2458 2459 /** 2460 * Sets the hint text of this node. Only applies to nodes where text can be entered. 2461 * <p> 2462 * <strong>Note:</strong> Cannot be called from an 2463 * {@link android.accessibilityservice.AccessibilityService}. 2464 * This class is made immutable before being delivered to an AccessibilityService. 2465 * </p> 2466 * 2467 * @param hintText The hint text for this mode. 2468 * 2469 * @throws IllegalStateException If called from an AccessibilityService. 2470 */ 2471 public void setHintText(CharSequence hintText) { 2472 enforceNotSealed(); 2473 mHintText = (hintText == null) ? null : hintText.subSequence(0, hintText.length()); 2474 } 2475 2476 /** 2477 * Sets the error text of this node. 2478 * <p> 2479 * <strong>Note:</strong> Cannot be called from an 2480 * {@link android.accessibilityservice.AccessibilityService}. 2481 * This class is made immutable before being delivered to an AccessibilityService. 2482 * </p> 2483 * 2484 * @param error The error text. 2485 * 2486 * @throws IllegalStateException If called from an AccessibilityService. 2487 */ 2488 public void setError(CharSequence error) { 2489 enforceNotSealed(); 2490 mError = (error == null) ? null : error.subSequence(0, error.length()); 2491 } 2492 2493 /** 2494 * Gets the error text of this node. 2495 * 2496 * @return The error text. 2497 */ 2498 public CharSequence getError() { 2499 return mError; 2500 } 2501 2502 /** 2503 * Gets the content description of this node. 2504 * 2505 * @return The content description. 2506 */ 2507 public CharSequence getContentDescription() { 2508 return mContentDescription; 2509 } 2510 2511 /** 2512 * Sets the content description of this node. 2513 * <p> 2514 * <strong>Note:</strong> Cannot be called from an 2515 * {@link android.accessibilityservice.AccessibilityService}. 2516 * This class is made immutable before being delivered to an AccessibilityService. 2517 * </p> 2518 * 2519 * @param contentDescription The content description. 2520 * 2521 * @throws IllegalStateException If called from an AccessibilityService. 2522 */ 2523 public void setContentDescription(CharSequence contentDescription) { 2524 enforceNotSealed(); 2525 mContentDescription = (contentDescription == null) ? null 2526 : contentDescription.subSequence(0, contentDescription.length()); 2527 } 2528 2529 /** 2530 * Sets the view for which the view represented by this info serves as a 2531 * label for accessibility purposes. 2532 * 2533 * @param labeled The view for which this info serves as a label. 2534 */ 2535 public void setLabelFor(View labeled) { 2536 setLabelFor(labeled, UNDEFINED_ITEM_ID); 2537 } 2538 2539 /** 2540 * Sets the view for which the view represented by this info serves as a 2541 * label for accessibility purposes. If <code>virtualDescendantId</code> 2542 * is {@link View#NO_ID} the root is set as the labeled. 2543 * <p> 2544 * A virtual descendant is an imaginary View that is reported as a part of the view 2545 * hierarchy for accessibility purposes. This enables custom views that draw complex 2546 * content to report themselves as a tree of virtual views, thus conveying their 2547 * logical structure. 2548 * </p> 2549 * <p> 2550 * <strong>Note:</strong> Cannot be called from an 2551 * {@link android.accessibilityservice.AccessibilityService}. 2552 * This class is made immutable before being delivered to an AccessibilityService. 2553 * </p> 2554 * 2555 * @param root The root whose virtual descendant serves as a label. 2556 * @param virtualDescendantId The id of the virtual descendant. 2557 */ 2558 public void setLabelFor(View root, int virtualDescendantId) { 2559 enforceNotSealed(); 2560 final int rootAccessibilityViewId = (root != null) 2561 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; 2562 mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 2563 } 2564 2565 /** 2566 * Gets the node info for which the view represented by this info serves as 2567 * a label for accessibility purposes. 2568 * <p> 2569 * <strong>Note:</strong> It is a client responsibility to recycle the 2570 * received info by calling {@link AccessibilityNodeInfo#recycle()} 2571 * to avoid creating of multiple instances. 2572 * </p> 2573 * 2574 * @return The labeled info. 2575 */ 2576 public AccessibilityNodeInfo getLabelFor() { 2577 enforceSealed(); 2578 return getNodeForAccessibilityId(mLabelForId); 2579 } 2580 2581 /** 2582 * Sets the view which serves as the label of the view represented by 2583 * this info for accessibility purposes. 2584 * 2585 * @param label The view that labels this node's source. 2586 */ 2587 public void setLabeledBy(View label) { 2588 setLabeledBy(label, UNDEFINED_ITEM_ID); 2589 } 2590 2591 /** 2592 * Sets the view which serves as the label of the view represented by 2593 * this info for accessibility purposes. If <code>virtualDescendantId</code> 2594 * is {@link View#NO_ID} the root is set as the label. 2595 * <p> 2596 * A virtual descendant is an imaginary View that is reported as a part of the view 2597 * hierarchy for accessibility purposes. This enables custom views that draw complex 2598 * content to report themselves as a tree of virtual views, thus conveying their 2599 * logical structure. 2600 * </p> 2601 * <p> 2602 * <strong>Note:</strong> Cannot be called from an 2603 * {@link android.accessibilityservice.AccessibilityService}. 2604 * This class is made immutable before being delivered to an AccessibilityService. 2605 * </p> 2606 * 2607 * @param root The root whose virtual descendant labels this node's source. 2608 * @param virtualDescendantId The id of the virtual descendant. 2609 */ 2610 public void setLabeledBy(View root, int virtualDescendantId) { 2611 enforceNotSealed(); 2612 final int rootAccessibilityViewId = (root != null) 2613 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; 2614 mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId); 2615 } 2616 2617 /** 2618 * Gets the node info which serves as the label of the view represented by 2619 * this info for accessibility purposes. 2620 * <p> 2621 * <strong>Note:</strong> It is a client responsibility to recycle the 2622 * received info by calling {@link AccessibilityNodeInfo#recycle()} 2623 * to avoid creating of multiple instances. 2624 * </p> 2625 * 2626 * @return The label. 2627 */ 2628 public AccessibilityNodeInfo getLabeledBy() { 2629 enforceSealed(); 2630 return getNodeForAccessibilityId(mLabeledById); 2631 } 2632 2633 /** 2634 * Sets the fully qualified resource name of the source view's id. 2635 * 2636 * <p> 2637 * <strong>Note:</strong> Cannot be called from an 2638 * {@link android.accessibilityservice.AccessibilityService}. 2639 * This class is made immutable before being delivered to an AccessibilityService. 2640 * </p> 2641 * 2642 * @param viewIdResName The id resource name. 2643 */ 2644 public void setViewIdResourceName(String viewIdResName) { 2645 enforceNotSealed(); 2646 mViewIdResourceName = viewIdResName; 2647 } 2648 2649 /** 2650 * Gets the fully qualified resource name of the source view's id. 2651 * 2652 * <p> 2653 * <strong>Note:</strong> The primary usage of this API is for UI test automation 2654 * and in order to report the source view id of an {@link AccessibilityNodeInfo} the 2655 * client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} 2656 * flag when configuring his {@link android.accessibilityservice.AccessibilityService}. 2657 * </p> 2658 2659 * @return The id resource name. 2660 */ 2661 public String getViewIdResourceName() { 2662 return mViewIdResourceName; 2663 } 2664 2665 /** 2666 * Gets the text selection start or the cursor position. 2667 * <p> 2668 * If no text is selected, both this method and 2669 * {@link AccessibilityNodeInfo#getTextSelectionEnd()} return the same value: 2670 * the current location of the cursor. 2671 * </p> 2672 * 2673 * @return The text selection start, the cursor location if there is no selection, or -1 if 2674 * there is no text selection and no cursor. 2675 */ 2676 public int getTextSelectionStart() { 2677 return mTextSelectionStart; 2678 } 2679 2680 /** 2681 * Gets the text selection end if text is selected. 2682 * <p> 2683 * If no text is selected, both this method and 2684 * {@link AccessibilityNodeInfo#getTextSelectionStart()} return the same value: 2685 * the current location of the cursor. 2686 * </p> 2687 * 2688 * @return The text selection end, the cursor location if there is no selection, or -1 if 2689 * there is no text selection and no cursor. 2690 */ 2691 public int getTextSelectionEnd() { 2692 return mTextSelectionEnd; 2693 } 2694 2695 /** 2696 * Sets the text selection start and end. 2697 * <p> 2698 * <strong>Note:</strong> Cannot be called from an 2699 * {@link android.accessibilityservice.AccessibilityService}. 2700 * This class is made immutable before being delivered to an AccessibilityService. 2701 * </p> 2702 * 2703 * @param start The text selection start. 2704 * @param end The text selection end. 2705 * 2706 * @throws IllegalStateException If called from an AccessibilityService. 2707 */ 2708 public void setTextSelection(int start, int end) { 2709 enforceNotSealed(); 2710 mTextSelectionStart = start; 2711 mTextSelectionEnd = end; 2712 } 2713 2714 /** 2715 * Gets the input type of the source as defined by {@link InputType}. 2716 * 2717 * @return The input type. 2718 */ 2719 public int getInputType() { 2720 return mInputType; 2721 } 2722 2723 /** 2724 * Sets the input type of the source as defined by {@link InputType}. 2725 * <p> 2726 * <strong>Note:</strong> Cannot be called from an 2727 * {@link android.accessibilityservice.AccessibilityService}. 2728 * This class is made immutable before being delivered to an 2729 * AccessibilityService. 2730 * </p> 2731 * 2732 * @param inputType The input type. 2733 * 2734 * @throws IllegalStateException If called from an AccessibilityService. 2735 */ 2736 public void setInputType(int inputType) { 2737 enforceNotSealed(); 2738 mInputType = inputType; 2739 } 2740 2741 /** 2742 * Gets an optional bundle with extra data. The bundle 2743 * is lazily created and never <code>null</code>. 2744 * <p> 2745 * <strong>Note:</strong> It is recommended to use the package 2746 * name of your application as a prefix for the keys to avoid 2747 * collisions which may confuse an accessibility service if the 2748 * same key has different meaning when emitted from different 2749 * applications. 2750 * </p> 2751 * 2752 * @return The bundle. 2753 */ 2754 public Bundle getExtras() { 2755 if (mExtras == null) { 2756 mExtras = new Bundle(); 2757 } 2758 return mExtras; 2759 } 2760 2761 /** 2762 * Check if a node has an extras bundle 2763 * @hide 2764 */ 2765 public boolean hasExtras() { 2766 return mExtras != null; 2767 } 2768 2769 /** 2770 * Gets the value of a boolean property. 2771 * 2772 * @param property The property. 2773 * @return The value. 2774 */ 2775 private boolean getBooleanProperty(int property) { 2776 return (mBooleanProperties & property) != 0; 2777 } 2778 2779 /** 2780 * Sets a boolean property. 2781 * 2782 * @param property The property. 2783 * @param value The value. 2784 * 2785 * @throws IllegalStateException If called from an AccessibilityService. 2786 */ 2787 private void setBooleanProperty(int property, boolean value) { 2788 enforceNotSealed(); 2789 if (value) { 2790 mBooleanProperties |= property; 2791 } else { 2792 mBooleanProperties &= ~property; 2793 } 2794 } 2795 2796 /** 2797 * Sets the unique id of the IAccessibilityServiceConnection over which 2798 * this instance can send requests to the system. 2799 * 2800 * @param connectionId The connection id. 2801 * 2802 * @hide 2803 */ 2804 public void setConnectionId(int connectionId) { 2805 enforceNotSealed(); 2806 mConnectionId = connectionId; 2807 } 2808 2809 /** 2810 * {@inheritDoc} 2811 */ 2812 @Override 2813 public int describeContents() { 2814 return 0; 2815 } 2816 2817 /** 2818 * Gets the id of the source node. 2819 * 2820 * @return The id. 2821 * 2822 * @hide 2823 */ 2824 public long getSourceNodeId() { 2825 return mSourceNodeId; 2826 } 2827 2828 /** 2829 * Sets if this instance is sealed. 2830 * 2831 * @param sealed Whether is sealed. 2832 * 2833 * @hide 2834 */ 2835 public void setSealed(boolean sealed) { 2836 mSealed = sealed; 2837 } 2838 2839 /** 2840 * Gets if this instance is sealed. 2841 * 2842 * @return Whether is sealed. 2843 * 2844 * @hide 2845 */ 2846 public boolean isSealed() { 2847 return mSealed; 2848 } 2849 2850 /** 2851 * Enforces that this instance is sealed. 2852 * 2853 * @throws IllegalStateException If this instance is not sealed. 2854 * 2855 * @hide 2856 */ 2857 protected void enforceSealed() { 2858 if (!isSealed()) { 2859 throw new IllegalStateException("Cannot perform this " 2860 + "action on a not sealed instance."); 2861 } 2862 } 2863 2864 private void enforceValidFocusDirection(int direction) { 2865 switch (direction) { 2866 case View.FOCUS_DOWN: 2867 case View.FOCUS_UP: 2868 case View.FOCUS_LEFT: 2869 case View.FOCUS_RIGHT: 2870 case View.FOCUS_FORWARD: 2871 case View.FOCUS_BACKWARD: 2872 return; 2873 default: 2874 throw new IllegalArgumentException("Unknown direction: " + direction); 2875 } 2876 } 2877 2878 private void enforceValidFocusType(int focusType) { 2879 switch (focusType) { 2880 case FOCUS_INPUT: 2881 case FOCUS_ACCESSIBILITY: 2882 return; 2883 default: 2884 throw new IllegalArgumentException("Unknown focus type: " + focusType); 2885 } 2886 } 2887 2888 /** 2889 * Enforces that this instance is not sealed. 2890 * 2891 * @throws IllegalStateException If this instance is sealed. 2892 * 2893 * @hide 2894 */ 2895 protected void enforceNotSealed() { 2896 if (isSealed()) { 2897 throw new IllegalStateException("Cannot perform this " 2898 + "action on a sealed instance."); 2899 } 2900 } 2901 2902 /** 2903 * Returns a cached instance if such is available otherwise a new one 2904 * and sets the source. 2905 * 2906 * @param source The source view. 2907 * @return An instance. 2908 * 2909 * @see #setSource(View) 2910 */ 2911 public static AccessibilityNodeInfo obtain(View source) { 2912 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 2913 info.setSource(source); 2914 return info; 2915 } 2916 2917 /** 2918 * Returns a cached instance if such is available otherwise a new one 2919 * and sets the source. 2920 * 2921 * @param root The root of the virtual subtree. 2922 * @param virtualDescendantId The id of the virtual descendant. 2923 * @return An instance. 2924 * 2925 * @see #setSource(View, int) 2926 */ 2927 public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) { 2928 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 2929 info.setSource(root, virtualDescendantId); 2930 return info; 2931 } 2932 2933 /** 2934 * Returns a cached instance if such is available otherwise a new one. 2935 * 2936 * @return An instance. 2937 */ 2938 public static AccessibilityNodeInfo obtain() { 2939 AccessibilityNodeInfo info = sPool.acquire(); 2940 if (sNumInstancesInUse != null) { 2941 sNumInstancesInUse.incrementAndGet(); 2942 } 2943 return (info != null) ? info : new AccessibilityNodeInfo(); 2944 } 2945 2946 /** 2947 * Returns a cached instance if such is available or a new one is 2948 * create. The returned instance is initialized from the given 2949 * <code>info</code>. 2950 * 2951 * @param info The other info. 2952 * @return An instance. 2953 */ 2954 public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) { 2955 AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain(); 2956 infoClone.init(info); 2957 return infoClone; 2958 } 2959 2960 /** 2961 * Return an instance back to be reused. 2962 * <p> 2963 * <strong>Note:</strong> You must not touch the object after calling this function. 2964 * 2965 * @throws IllegalStateException If the info is already recycled. 2966 */ 2967 public void recycle() { 2968 clear(); 2969 sPool.release(this); 2970 if (sNumInstancesInUse != null) { 2971 sNumInstancesInUse.decrementAndGet(); 2972 } 2973 } 2974 2975 /** 2976 * Specify a counter that will be incremented on obtain() and decremented on recycle() 2977 * 2978 * @hide 2979 */ 2980 @TestApi 2981 public static void setNumInstancesInUseCounter(AtomicInteger counter) { 2982 sNumInstancesInUse = counter; 2983 } 2984 2985 /** 2986 * {@inheritDoc} 2987 * <p> 2988 * <strong>Note:</strong> After the instance is written to a parcel it 2989 * is recycled. You must not touch the object after calling this function. 2990 * </p> 2991 */ 2992 @Override 2993 public void writeToParcel(Parcel parcel, int flags) { 2994 parcel.writeInt(isSealed() ? 1 : 0); 2995 parcel.writeLong(mSourceNodeId); 2996 parcel.writeInt(mWindowId); 2997 parcel.writeLong(mParentNodeId); 2998 parcel.writeLong(mLabelForId); 2999 parcel.writeLong(mLabeledById); 3000 parcel.writeLong(mTraversalBefore); 3001 parcel.writeLong(mTraversalAfter); 3002 3003 parcel.writeInt(mConnectionId); 3004 3005 final LongArray childIds = mChildNodeIds; 3006 if (childIds == null) { 3007 parcel.writeInt(0); 3008 } else { 3009 final int childIdsSize = childIds.size(); 3010 parcel.writeInt(childIdsSize); 3011 for (int i = 0; i < childIdsSize; i++) { 3012 parcel.writeLong(childIds.get(i)); 3013 } 3014 } 3015 3016 parcel.writeInt(mBoundsInParent.top); 3017 parcel.writeInt(mBoundsInParent.bottom); 3018 parcel.writeInt(mBoundsInParent.left); 3019 parcel.writeInt(mBoundsInParent.right); 3020 3021 parcel.writeInt(mBoundsInScreen.top); 3022 parcel.writeInt(mBoundsInScreen.bottom); 3023 parcel.writeInt(mBoundsInScreen.left); 3024 parcel.writeInt(mBoundsInScreen.right); 3025 3026 if (mActions != null && !mActions.isEmpty()) { 3027 final int actionCount = mActions.size(); 3028 parcel.writeInt(actionCount); 3029 3030 int defaultLegacyStandardActions = 0; 3031 for (int i = 0; i < actionCount; i++) { 3032 AccessibilityAction action = mActions.get(i); 3033 if (isDefaultLegacyStandardAction(action)) { 3034 defaultLegacyStandardActions |= action.getId(); 3035 } 3036 } 3037 parcel.writeInt(defaultLegacyStandardActions); 3038 3039 for (int i = 0; i < actionCount; i++) { 3040 AccessibilityAction action = mActions.get(i); 3041 if (!isDefaultLegacyStandardAction(action)) { 3042 parcel.writeInt(action.getId()); 3043 parcel.writeCharSequence(action.getLabel()); 3044 } 3045 } 3046 } else { 3047 parcel.writeInt(0); 3048 } 3049 3050 parcel.writeInt(mMaxTextLength); 3051 parcel.writeInt(mMovementGranularities); 3052 parcel.writeInt(mBooleanProperties); 3053 3054 parcel.writeCharSequence(mPackageName); 3055 parcel.writeCharSequence(mClassName); 3056 parcel.writeCharSequence(mText); 3057 parcel.writeCharSequence(mHintText); 3058 parcel.writeCharSequence(mError); 3059 parcel.writeCharSequence(mContentDescription); 3060 parcel.writeString(mViewIdResourceName); 3061 3062 parcel.writeInt(mTextSelectionStart); 3063 parcel.writeInt(mTextSelectionEnd); 3064 parcel.writeInt(mInputType); 3065 parcel.writeInt(mLiveRegion); 3066 parcel.writeInt(mDrawingOrderInParent); 3067 if (mExtraDataKeys != null) { 3068 parcel.writeInt(1); 3069 parcel.writeStringList(mExtraDataKeys); 3070 } else { 3071 parcel.writeInt(0); 3072 } 3073 3074 if (mExtras != null) { 3075 parcel.writeInt(1); 3076 parcel.writeBundle(mExtras); 3077 } else { 3078 parcel.writeInt(0); 3079 } 3080 3081 if (mRangeInfo != null) { 3082 parcel.writeInt(1); 3083 parcel.writeInt(mRangeInfo.getType()); 3084 parcel.writeFloat(mRangeInfo.getMin()); 3085 parcel.writeFloat(mRangeInfo.getMax()); 3086 parcel.writeFloat(mRangeInfo.getCurrent()); 3087 } else { 3088 parcel.writeInt(0); 3089 } 3090 3091 if (mCollectionInfo != null) { 3092 parcel.writeInt(1); 3093 parcel.writeInt(mCollectionInfo.getRowCount()); 3094 parcel.writeInt(mCollectionInfo.getColumnCount()); 3095 parcel.writeInt(mCollectionInfo.isHierarchical() ? 1 : 0); 3096 parcel.writeInt(mCollectionInfo.getSelectionMode()); 3097 } else { 3098 parcel.writeInt(0); 3099 } 3100 3101 if (mCollectionItemInfo != null) { 3102 parcel.writeInt(1); 3103 parcel.writeInt(mCollectionItemInfo.getRowIndex()); 3104 parcel.writeInt(mCollectionItemInfo.getRowSpan()); 3105 parcel.writeInt(mCollectionItemInfo.getColumnIndex()); 3106 parcel.writeInt(mCollectionItemInfo.getColumnSpan()); 3107 parcel.writeInt(mCollectionItemInfo.isHeading() ? 1 : 0); 3108 parcel.writeInt(mCollectionItemInfo.isSelected() ? 1 : 0); 3109 } else { 3110 parcel.writeInt(0); 3111 } 3112 3113 // Since instances of this class are fetched via synchronous i.e. blocking 3114 // calls in IPCs we always recycle as soon as the instance is marshaled. 3115 recycle(); 3116 } 3117 3118 /** 3119 * Initializes this instance from another one. 3120 * 3121 * @param other The other instance. 3122 */ 3123 private void init(AccessibilityNodeInfo other) { 3124 mSealed = other.mSealed; 3125 mSourceNodeId = other.mSourceNodeId; 3126 mParentNodeId = other.mParentNodeId; 3127 mLabelForId = other.mLabelForId; 3128 mLabeledById = other.mLabeledById; 3129 mTraversalBefore = other.mTraversalBefore; 3130 mTraversalAfter = other.mTraversalAfter; 3131 mWindowId = other.mWindowId; 3132 mConnectionId = other.mConnectionId; 3133 mBoundsInParent.set(other.mBoundsInParent); 3134 mBoundsInScreen.set(other.mBoundsInScreen); 3135 mPackageName = other.mPackageName; 3136 mClassName = other.mClassName; 3137 mText = other.mText; 3138 mHintText = other.mHintText; 3139 mError = other.mError; 3140 mContentDescription = other.mContentDescription; 3141 mViewIdResourceName = other.mViewIdResourceName; 3142 3143 final ArrayList<AccessibilityAction> otherActions = other.mActions; 3144 if (otherActions != null && otherActions.size() > 0) { 3145 if (mActions == null) { 3146 mActions = new ArrayList(otherActions); 3147 } else { 3148 mActions.clear(); 3149 mActions.addAll(other.mActions); 3150 } 3151 } 3152 3153 mBooleanProperties = other.mBooleanProperties; 3154 mMaxTextLength = other.mMaxTextLength; 3155 mMovementGranularities = other.mMovementGranularities; 3156 3157 final LongArray otherChildNodeIds = other.mChildNodeIds; 3158 if (otherChildNodeIds != null && otherChildNodeIds.size() > 0) { 3159 if (mChildNodeIds == null) { 3160 mChildNodeIds = otherChildNodeIds.clone(); 3161 } else { 3162 mChildNodeIds.clear(); 3163 mChildNodeIds.addAll(otherChildNodeIds); 3164 } 3165 } 3166 3167 mTextSelectionStart = other.mTextSelectionStart; 3168 mTextSelectionEnd = other.mTextSelectionEnd; 3169 mInputType = other.mInputType; 3170 mLiveRegion = other.mLiveRegion; 3171 mDrawingOrderInParent = other.mDrawingOrderInParent; 3172 mExtraDataKeys = other.mExtraDataKeys; 3173 3174 if (other.mExtras != null) { 3175 mExtras = new Bundle(other.mExtras); 3176 } else { 3177 mExtras = null; 3178 } 3179 mRangeInfo = (other.mRangeInfo != null) 3180 ? RangeInfo.obtain(other.mRangeInfo) : null; 3181 mCollectionInfo = (other.mCollectionInfo != null) 3182 ? CollectionInfo.obtain(other.mCollectionInfo) : null; 3183 mCollectionItemInfo = (other.mCollectionItemInfo != null) 3184 ? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null; 3185 } 3186 3187 /** 3188 * Creates a new instance from a {@link Parcel}. 3189 * 3190 * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}. 3191 */ 3192 private void initFromParcel(Parcel parcel) { 3193 final boolean sealed = (parcel.readInt() == 1); 3194 mSourceNodeId = parcel.readLong(); 3195 mWindowId = parcel.readInt(); 3196 mParentNodeId = parcel.readLong(); 3197 mLabelForId = parcel.readLong(); 3198 mLabeledById = parcel.readLong(); 3199 mTraversalBefore = parcel.readLong(); 3200 mTraversalAfter = parcel.readLong(); 3201 3202 mConnectionId = parcel.readInt(); 3203 3204 final int childrenSize = parcel.readInt(); 3205 if (childrenSize <= 0) { 3206 mChildNodeIds = null; 3207 } else { 3208 mChildNodeIds = new LongArray(childrenSize); 3209 for (int i = 0; i < childrenSize; i++) { 3210 final long childId = parcel.readLong(); 3211 mChildNodeIds.add(childId); 3212 } 3213 } 3214 3215 mBoundsInParent.top = parcel.readInt(); 3216 mBoundsInParent.bottom = parcel.readInt(); 3217 mBoundsInParent.left = parcel.readInt(); 3218 mBoundsInParent.right = parcel.readInt(); 3219 3220 mBoundsInScreen.top = parcel.readInt(); 3221 mBoundsInScreen.bottom = parcel.readInt(); 3222 mBoundsInScreen.left = parcel.readInt(); 3223 mBoundsInScreen.right = parcel.readInt(); 3224 3225 final int actionCount = parcel.readInt(); 3226 if (actionCount > 0) { 3227 final int legacyStandardActions = parcel.readInt(); 3228 addLegacyStandardActions(legacyStandardActions); 3229 final int nonLegacyActionCount = actionCount - Integer.bitCount(legacyStandardActions); 3230 for (int i = 0; i < nonLegacyActionCount; i++) { 3231 final AccessibilityAction action = new AccessibilityAction( 3232 parcel.readInt(), parcel.readCharSequence()); 3233 addActionUnchecked(action); 3234 } 3235 } 3236 3237 mMaxTextLength = parcel.readInt(); 3238 mMovementGranularities = parcel.readInt(); 3239 mBooleanProperties = parcel.readInt(); 3240 3241 mPackageName = parcel.readCharSequence(); 3242 mClassName = parcel.readCharSequence(); 3243 mText = parcel.readCharSequence(); 3244 mHintText = parcel.readCharSequence(); 3245 mError = parcel.readCharSequence(); 3246 mContentDescription = parcel.readCharSequence(); 3247 mViewIdResourceName = parcel.readString(); 3248 3249 mTextSelectionStart = parcel.readInt(); 3250 mTextSelectionEnd = parcel.readInt(); 3251 3252 mInputType = parcel.readInt(); 3253 mLiveRegion = parcel.readInt(); 3254 mDrawingOrderInParent = parcel.readInt(); 3255 3256 if (parcel.readInt() == 1) { 3257 mExtraDataKeys = parcel.createStringArrayList(); 3258 } else { 3259 mExtraDataKeys = null; 3260 } 3261 3262 if (parcel.readInt() == 1) { 3263 mExtras = parcel.readBundle(); 3264 } else { 3265 mExtras = null; 3266 } 3267 3268 if (parcel.readInt() == 1) { 3269 mRangeInfo = RangeInfo.obtain( 3270 parcel.readInt(), 3271 parcel.readFloat(), 3272 parcel.readFloat(), 3273 parcel.readFloat()); 3274 } 3275 3276 if (parcel.readInt() == 1) { 3277 mCollectionInfo = CollectionInfo.obtain( 3278 parcel.readInt(), 3279 parcel.readInt(), 3280 parcel.readInt() == 1, 3281 parcel.readInt()); 3282 } 3283 3284 if (parcel.readInt() == 1) { 3285 mCollectionItemInfo = CollectionItemInfo.obtain( 3286 parcel.readInt(), 3287 parcel.readInt(), 3288 parcel.readInt(), 3289 parcel.readInt(), 3290 parcel.readInt() == 1, 3291 parcel.readInt() == 1); 3292 } 3293 3294 mSealed = sealed; 3295 } 3296 3297 /** 3298 * Clears the state of this instance. 3299 */ 3300 private void clear() { 3301 mSealed = false; 3302 mSourceNodeId = ROOT_NODE_ID; 3303 mParentNodeId = ROOT_NODE_ID; 3304 mLabelForId = ROOT_NODE_ID; 3305 mLabeledById = ROOT_NODE_ID; 3306 mTraversalBefore = ROOT_NODE_ID; 3307 mTraversalAfter = ROOT_NODE_ID; 3308 mWindowId = UNDEFINED_ITEM_ID; 3309 mConnectionId = UNDEFINED_CONNECTION_ID; 3310 mMaxTextLength = -1; 3311 mMovementGranularities = 0; 3312 if (mChildNodeIds != null) { 3313 mChildNodeIds.clear(); 3314 } 3315 mBoundsInParent.set(0, 0, 0, 0); 3316 mBoundsInScreen.set(0, 0, 0, 0); 3317 mBooleanProperties = 0; 3318 mDrawingOrderInParent = 0; 3319 mExtraDataKeys = null; 3320 mPackageName = null; 3321 mClassName = null; 3322 mText = null; 3323 mHintText = null; 3324 mError = null; 3325 mContentDescription = null; 3326 mViewIdResourceName = null; 3327 if (mActions != null) { 3328 mActions.clear(); 3329 } 3330 mTextSelectionStart = UNDEFINED_SELECTION_INDEX; 3331 mTextSelectionEnd = UNDEFINED_SELECTION_INDEX; 3332 mInputType = InputType.TYPE_NULL; 3333 mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE; 3334 mExtras = null; 3335 if (mRangeInfo != null) { 3336 mRangeInfo.recycle(); 3337 mRangeInfo = null; 3338 } 3339 if (mCollectionInfo != null) { 3340 mCollectionInfo.recycle(); 3341 mCollectionInfo = null; 3342 } 3343 if (mCollectionItemInfo != null) { 3344 mCollectionItemInfo.recycle(); 3345 mCollectionItemInfo = null; 3346 } 3347 } 3348 3349 private static boolean isDefaultLegacyStandardAction(AccessibilityAction action) { 3350 return (action.getId() <= LAST_LEGACY_STANDARD_ACTION 3351 && TextUtils.isEmpty(action.getLabel())); 3352 } 3353 3354 private static AccessibilityAction getActionSingleton(int actionId) { 3355 final int actions = AccessibilityAction.sStandardActions.size(); 3356 for (int i = 0; i < actions; i++) { 3357 AccessibilityAction currentAction = AccessibilityAction.sStandardActions.valueAt(i); 3358 if (actionId == currentAction.getId()) { 3359 return currentAction; 3360 } 3361 } 3362 3363 return null; 3364 } 3365 3366 private void addLegacyStandardActions(int actionMask) { 3367 int remainingIds = actionMask; 3368 while (remainingIds > 0) { 3369 final int id = 1 << Integer.numberOfTrailingZeros(remainingIds); 3370 remainingIds &= ~id; 3371 AccessibilityAction action = getActionSingleton(id); 3372 addAction(action); 3373 } 3374 } 3375 3376 /** 3377 * Gets the human readable action symbolic name. 3378 * 3379 * @param action The action. 3380 * @return The symbolic name. 3381 */ 3382 private static String getActionSymbolicName(int action) { 3383 switch (action) { 3384 case ACTION_FOCUS: 3385 return "ACTION_FOCUS"; 3386 case ACTION_CLEAR_FOCUS: 3387 return "ACTION_CLEAR_FOCUS"; 3388 case ACTION_SELECT: 3389 return "ACTION_SELECT"; 3390 case ACTION_CLEAR_SELECTION: 3391 return "ACTION_CLEAR_SELECTION"; 3392 case ACTION_CLICK: 3393 return "ACTION_CLICK"; 3394 case ACTION_LONG_CLICK: 3395 return "ACTION_LONG_CLICK"; 3396 case ACTION_ACCESSIBILITY_FOCUS: 3397 return "ACTION_ACCESSIBILITY_FOCUS"; 3398 case ACTION_CLEAR_ACCESSIBILITY_FOCUS: 3399 return "ACTION_CLEAR_ACCESSIBILITY_FOCUS"; 3400 case ACTION_NEXT_AT_MOVEMENT_GRANULARITY: 3401 return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY"; 3402 case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: 3403 return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY"; 3404 case ACTION_NEXT_HTML_ELEMENT: 3405 return "ACTION_NEXT_HTML_ELEMENT"; 3406 case ACTION_PREVIOUS_HTML_ELEMENT: 3407 return "ACTION_PREVIOUS_HTML_ELEMENT"; 3408 case ACTION_SCROLL_FORWARD: 3409 return "ACTION_SCROLL_FORWARD"; 3410 case ACTION_SCROLL_BACKWARD: 3411 return "ACTION_SCROLL_BACKWARD"; 3412 case ACTION_CUT: 3413 return "ACTION_CUT"; 3414 case ACTION_COPY: 3415 return "ACTION_COPY"; 3416 case ACTION_PASTE: 3417 return "ACTION_PASTE"; 3418 case ACTION_SET_SELECTION: 3419 return "ACTION_SET_SELECTION"; 3420 case ACTION_EXPAND: 3421 return "ACTION_EXPAND"; 3422 case ACTION_COLLAPSE: 3423 return "ACTION_COLLAPSE"; 3424 case ACTION_DISMISS: 3425 return "ACTION_DISMISS"; 3426 case ACTION_SET_TEXT: 3427 return "ACTION_SET_TEXT"; 3428 case R.id.accessibilityActionShowOnScreen: 3429 return "ACTION_SHOW_ON_SCREEN"; 3430 case R.id.accessibilityActionScrollToPosition: 3431 return "ACTION_SCROLL_TO_POSITION"; 3432 case R.id.accessibilityActionScrollUp: 3433 return "ACTION_SCROLL_UP"; 3434 case R.id.accessibilityActionScrollLeft: 3435 return "ACTION_SCROLL_LEFT"; 3436 case R.id.accessibilityActionScrollDown: 3437 return "ACTION_SCROLL_DOWN"; 3438 case R.id.accessibilityActionScrollRight: 3439 return "ACTION_SCROLL_RIGHT"; 3440 case R.id.accessibilityActionSetProgress: 3441 return "ACTION_SET_PROGRESS"; 3442 case R.id.accessibilityActionContextClick: 3443 return "ACTION_CONTEXT_CLICK"; 3444 default: 3445 return "ACTION_UNKNOWN"; 3446 } 3447 } 3448 3449 /** 3450 * Gets the human readable movement granularity symbolic name. 3451 * 3452 * @param granularity The granularity. 3453 * @return The symbolic name. 3454 */ 3455 private static String getMovementGranularitySymbolicName(int granularity) { 3456 switch (granularity) { 3457 case MOVEMENT_GRANULARITY_CHARACTER: 3458 return "MOVEMENT_GRANULARITY_CHARACTER"; 3459 case MOVEMENT_GRANULARITY_WORD: 3460 return "MOVEMENT_GRANULARITY_WORD"; 3461 case MOVEMENT_GRANULARITY_LINE: 3462 return "MOVEMENT_GRANULARITY_LINE"; 3463 case MOVEMENT_GRANULARITY_PARAGRAPH: 3464 return "MOVEMENT_GRANULARITY_PARAGRAPH"; 3465 case MOVEMENT_GRANULARITY_PAGE: 3466 return "MOVEMENT_GRANULARITY_PAGE"; 3467 default: 3468 throw new IllegalArgumentException("Unknown movement granularity: " + granularity); 3469 } 3470 } 3471 3472 private boolean canPerformRequestOverConnection(long accessibilityNodeId) { 3473 return (mWindowId != UNDEFINED_ITEM_ID 3474 && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID 3475 && mConnectionId != UNDEFINED_CONNECTION_ID); 3476 } 3477 3478 @Override 3479 public boolean equals(Object object) { 3480 if (this == object) { 3481 return true; 3482 } 3483 if (object == null) { 3484 return false; 3485 } 3486 if (getClass() != object.getClass()) { 3487 return false; 3488 } 3489 AccessibilityNodeInfo other = (AccessibilityNodeInfo) object; 3490 if (mSourceNodeId != other.mSourceNodeId) { 3491 return false; 3492 } 3493 if (mWindowId != other.mWindowId) { 3494 return false; 3495 } 3496 return true; 3497 } 3498 3499 @Override 3500 public int hashCode() { 3501 final int prime = 31; 3502 int result = 1; 3503 result = prime * result + getAccessibilityViewId(mSourceNodeId); 3504 result = prime * result + getVirtualDescendantId(mSourceNodeId); 3505 result = prime * result + mWindowId; 3506 return result; 3507 } 3508 3509 @Override 3510 public String toString() { 3511 StringBuilder builder = new StringBuilder(); 3512 builder.append(super.toString()); 3513 3514 if (DEBUG) { 3515 builder.append("; sourceNodeId: " + mSourceNodeId); 3516 builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId)); 3517 builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId)); 3518 builder.append("; mParentNodeId: " + mParentNodeId); 3519 builder.append("; traversalBefore: ").append(mTraversalBefore); 3520 builder.append("; traversalAfter: ").append(mTraversalAfter); 3521 3522 int granularities = mMovementGranularities; 3523 builder.append("; MovementGranularities: ["); 3524 while (granularities != 0) { 3525 final int granularity = 1 << Integer.numberOfTrailingZeros(granularities); 3526 granularities &= ~granularity; 3527 builder.append(getMovementGranularitySymbolicName(granularity)); 3528 if (granularities != 0) { 3529 builder.append(", "); 3530 } 3531 } 3532 builder.append("]"); 3533 3534 builder.append("; childAccessibilityIds: ["); 3535 final LongArray childIds = mChildNodeIds; 3536 if (childIds != null) { 3537 for (int i = 0, count = childIds.size(); i < count; i++) { 3538 builder.append(childIds.get(i)); 3539 if (i < count - 1) { 3540 builder.append(", "); 3541 } 3542 } 3543 } 3544 builder.append("]"); 3545 } 3546 3547 builder.append("; boundsInParent: " + mBoundsInParent); 3548 builder.append("; boundsInScreen: " + mBoundsInScreen); 3549 3550 builder.append("; packageName: ").append(mPackageName); 3551 builder.append("; className: ").append(mClassName); 3552 builder.append("; text: ").append(mText); 3553 builder.append("; error: ").append(mError); 3554 builder.append("; maxTextLength: ").append(mMaxTextLength); 3555 builder.append("; contentDescription: ").append(mContentDescription); 3556 builder.append("; viewIdResName: ").append(mViewIdResourceName); 3557 3558 builder.append("; checkable: ").append(isCheckable()); 3559 builder.append("; checked: ").append(isChecked()); 3560 builder.append("; focusable: ").append(isFocusable()); 3561 builder.append("; focused: ").append(isFocused()); 3562 builder.append("; selected: ").append(isSelected()); 3563 builder.append("; clickable: ").append(isClickable()); 3564 builder.append("; longClickable: ").append(isLongClickable()); 3565 builder.append("; contextClickable: ").append(isContextClickable()); 3566 builder.append("; enabled: ").append(isEnabled()); 3567 builder.append("; password: ").append(isPassword()); 3568 builder.append("; scrollable: ").append(isScrollable()); 3569 builder.append("; importantForAccessibility: ").append(isImportantForAccessibility()); 3570 builder.append("; actions: ").append(mActions); 3571 3572 return builder.toString(); 3573 } 3574 3575 private AccessibilityNodeInfo getNodeForAccessibilityId(long accessibilityId) { 3576 if (!canPerformRequestOverConnection(accessibilityId)) { 3577 return null; 3578 } 3579 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 3580 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, 3581 mWindowId, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS 3582 | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS, null); 3583 } 3584 3585 /** 3586 * A class defining an action that can be performed on an {@link AccessibilityNodeInfo}. 3587 * Each action has a unique id that is mandatory and optional data. 3588 * <p> 3589 * There are three categories of actions: 3590 * <ul> 3591 * <li><strong>Standard actions</strong> - These are actions that are reported and 3592 * handled by the standard UI widgets in the platform. For each standard action 3593 * there is a static constant defined in this class, e.g. {@link #ACTION_FOCUS}. 3594 * </li> 3595 * <li><strong>Custom actions action</strong> - These are actions that are reported 3596 * and handled by custom widgets. i.e. ones that are not part of the UI toolkit. For 3597 * example, an application may define a custom action for clearing the user history. 3598 * </li> 3599 * <li><strong>Overriden standard actions</strong> - These are actions that override 3600 * standard actions to customize them. For example, an app may add a label to the 3601 * standard {@link #ACTION_CLICK} action to announce that this action clears browsing history. 3602 * </ul> 3603 * </p> 3604 * <p> 3605 * Actions are typically added to an {@link AccessibilityNodeInfo} by using 3606 * {@link AccessibilityNodeInfo#addAction(AccessibilityAction)} within 3607 * {@link View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} and are performed 3608 * within {@link View#performAccessibilityAction(int, Bundle)}. 3609 * </p> 3610 * <p class="note"> 3611 * <strong>Note:</strong> Views which support these actions should invoke 3612 * {@link View#setImportantForAccessibility(int)} with 3613 * {@link View#IMPORTANT_FOR_ACCESSIBILITY_YES} to ensure an {@link AccessibilityService} 3614 * can discover the set of supported actions. 3615 * </p> 3616 */ 3617 public static final class AccessibilityAction { 3618 3619 /** 3620 * Action that gives input focus to the node. 3621 */ 3622 public static final AccessibilityAction ACTION_FOCUS = 3623 new AccessibilityAction( 3624 AccessibilityNodeInfo.ACTION_FOCUS, null); 3625 3626 /** 3627 * Action that clears input focus of the node. 3628 */ 3629 public static final AccessibilityAction ACTION_CLEAR_FOCUS = 3630 new AccessibilityAction( 3631 AccessibilityNodeInfo.ACTION_CLEAR_FOCUS, null); 3632 3633 /** 3634 * Action that selects the node. 3635 */ 3636 public static final AccessibilityAction ACTION_SELECT = 3637 new AccessibilityAction( 3638 AccessibilityNodeInfo.ACTION_SELECT, null); 3639 3640 /** 3641 * Action that deselects the node. 3642 */ 3643 public static final AccessibilityAction ACTION_CLEAR_SELECTION = 3644 new AccessibilityAction( 3645 AccessibilityNodeInfo.ACTION_CLEAR_SELECTION, null); 3646 3647 /** 3648 * Action that clicks on the node info. 3649 */ 3650 public static final AccessibilityAction ACTION_CLICK = 3651 new AccessibilityAction( 3652 AccessibilityNodeInfo.ACTION_CLICK, null); 3653 3654 /** 3655 * Action that long clicks on the node. 3656 */ 3657 public static final AccessibilityAction ACTION_LONG_CLICK = 3658 new AccessibilityAction( 3659 AccessibilityNodeInfo.ACTION_LONG_CLICK, null); 3660 3661 /** 3662 * Action that gives accessibility focus to the node. 3663 */ 3664 public static final AccessibilityAction ACTION_ACCESSIBILITY_FOCUS = 3665 new AccessibilityAction( 3666 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); 3667 3668 /** 3669 * Action that clears accessibility focus of the node. 3670 */ 3671 public static final AccessibilityAction ACTION_CLEAR_ACCESSIBILITY_FOCUS = 3672 new AccessibilityAction( 3673 AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null); 3674 3675 /** 3676 * Action that requests to go to the next entity in this node's text 3677 * at a given movement granularity. For example, move to the next character, 3678 * word, etc. 3679 * <p> 3680 * <strong>Arguments:</strong> 3681 * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT 3682 * AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}, 3683 * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 3684 * AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br> 3685 * <strong>Example:</strong> Move to the previous character and do not extend selection. 3686 * <code><pre><p> 3687 * Bundle arguments = new Bundle(); 3688 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, 3689 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); 3690 * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, 3691 * false); 3692 * info.performAction(AccessibilityAction.ACTION_NEXT_AT_MOVEMENT_GRANULARITY.getId(), 3693 * arguments); 3694 * </code></pre></p> 3695 * </p> 3696 * 3697 * @see AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT 3698 * AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT 3699 * @see AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 3700 * AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 3701 * 3702 * @see AccessibilityNodeInfo#setMovementGranularities(int) 3703 * AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 3704 * @see AccessibilityNodeInfo#getMovementGranularities() 3705 * AccessibilityNodeInfo.getMovementGranularities() 3706 * 3707 * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_CHARACTER 3708 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER 3709 * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_WORD 3710 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD 3711 * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_LINE 3712 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE 3713 * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PARAGRAPH 3714 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH 3715 * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PAGE 3716 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE 3717 */ 3718 public static final AccessibilityAction ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 3719 new AccessibilityAction( 3720 AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, null); 3721 3722 /** 3723 * Action that requests to go to the previous entity in this node's text 3724 * at a given movement granularity. For example, move to the next character, 3725 * word, etc. 3726 * <p> 3727 * <strong>Arguments:</strong> 3728 * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT 3729 * AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}, 3730 * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 3731 * AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br> 3732 * <strong>Example:</strong> Move to the next character and do not extend selection. 3733 * <code><pre><p> 3734 * Bundle arguments = new Bundle(); 3735 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, 3736 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); 3737 * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, 3738 * false); 3739 * info.performAction(AccessibilityAction.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY.getId(), 3740 * arguments); 3741 * </code></pre></p> 3742 * </p> 3743 * 3744 * @see AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT 3745 * AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT 3746 * @see AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 3747 * AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 3748 * 3749 * @see AccessibilityNodeInfo#setMovementGranularities(int) 3750 * AccessibilityNodeInfo.setMovementGranularities(int) 3751 * @see AccessibilityNodeInfo#getMovementGranularities() 3752 * AccessibilityNodeInfo.getMovementGranularities() 3753 * 3754 * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_CHARACTER 3755 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER 3756 * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_WORD 3757 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD 3758 * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_LINE 3759 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE 3760 * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PARAGRAPH 3761 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH 3762 * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PAGE 3763 * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE 3764 */ 3765 public static final AccessibilityAction ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 3766 new AccessibilityAction( 3767 AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, null); 3768 3769 /** 3770 * Action to move to the next HTML element of a given type. For example, move 3771 * to the BUTTON, INPUT, TABLE, etc. 3772 * <p> 3773 * <strong>Arguments:</strong> 3774 * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING 3775 * AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> 3776 * <strong>Example:</strong> 3777 * <code><pre><p> 3778 * Bundle arguments = new Bundle(); 3779 * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); 3780 * info.performAction(AccessibilityAction.ACTION_NEXT_HTML_ELEMENT.getId(), arguments); 3781 * </code></pre></p> 3782 * </p> 3783 */ 3784 public static final AccessibilityAction ACTION_NEXT_HTML_ELEMENT = 3785 new AccessibilityAction( 3786 AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, null); 3787 3788 /** 3789 * Action to move to the previous HTML element of a given type. For example, move 3790 * to the BUTTON, INPUT, TABLE, etc. 3791 * <p> 3792 * <strong>Arguments:</strong> 3793 * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING 3794 * AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> 3795 * <strong>Example:</strong> 3796 * <code><pre><p> 3797 * Bundle arguments = new Bundle(); 3798 * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); 3799 * info.performAction(AccessibilityAction.ACTION_PREVIOUS_HTML_ELEMENT.getId(), arguments); 3800 * </code></pre></p> 3801 * </p> 3802 */ 3803 public static final AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT = 3804 new AccessibilityAction( 3805 AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, null); 3806 3807 /** 3808 * Action to scroll the node content forward. 3809 */ 3810 public static final AccessibilityAction ACTION_SCROLL_FORWARD = 3811 new AccessibilityAction( 3812 AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, null); 3813 3814 /** 3815 * Action to scroll the node content backward. 3816 */ 3817 public static final AccessibilityAction ACTION_SCROLL_BACKWARD = 3818 new AccessibilityAction( 3819 AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD, null); 3820 3821 /** 3822 * Action to copy the current selection to the clipboard. 3823 */ 3824 public static final AccessibilityAction ACTION_COPY = 3825 new AccessibilityAction( 3826 AccessibilityNodeInfo.ACTION_COPY, null); 3827 3828 /** 3829 * Action to paste the current clipboard content. 3830 */ 3831 public static final AccessibilityAction ACTION_PASTE = 3832 new AccessibilityAction( 3833 AccessibilityNodeInfo.ACTION_PASTE, null); 3834 3835 /** 3836 * Action to cut the current selection and place it to the clipboard. 3837 */ 3838 public static final AccessibilityAction ACTION_CUT = 3839 new AccessibilityAction( 3840 AccessibilityNodeInfo.ACTION_CUT, null); 3841 3842 /** 3843 * Action to set the selection. Performing this action with no arguments 3844 * clears the selection. 3845 * <p> 3846 * <strong>Arguments:</strong> 3847 * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT 3848 * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT}, 3849 * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT 3850 * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT}<br> 3851 * <strong>Example:</strong> 3852 * <code><pre><p> 3853 * Bundle arguments = new Bundle(); 3854 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1); 3855 * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2); 3856 * info.performAction(AccessibilityAction.ACTION_SET_SELECTION.getId(), arguments); 3857 * </code></pre></p> 3858 * </p> 3859 * 3860 * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT 3861 * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT 3862 * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT 3863 * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT 3864 */ 3865 public static final AccessibilityAction ACTION_SET_SELECTION = 3866 new AccessibilityAction( 3867 AccessibilityNodeInfo.ACTION_SET_SELECTION, null); 3868 3869 /** 3870 * Action to expand an expandable node. 3871 */ 3872 public static final AccessibilityAction ACTION_EXPAND = 3873 new AccessibilityAction( 3874 AccessibilityNodeInfo.ACTION_EXPAND, null); 3875 3876 /** 3877 * Action to collapse an expandable node. 3878 */ 3879 public static final AccessibilityAction ACTION_COLLAPSE = 3880 new AccessibilityAction( 3881 AccessibilityNodeInfo.ACTION_COLLAPSE, null); 3882 3883 /** 3884 * Action to dismiss a dismissable node. 3885 */ 3886 public static final AccessibilityAction ACTION_DISMISS = 3887 new AccessibilityAction( 3888 AccessibilityNodeInfo.ACTION_DISMISS, null); 3889 3890 /** 3891 * Action that sets the text of the node. Performing the action without argument, 3892 * using <code> null</code> or empty {@link CharSequence} will clear the text. This 3893 * action will also put the cursor at the end of text. 3894 * <p> 3895 * <strong>Arguments:</strong> 3896 * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE 3897 * AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br> 3898 * <strong>Example:</strong> 3899 * <code><pre><p> 3900 * Bundle arguments = new Bundle(); 3901 * arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, 3902 * "android"); 3903 * info.performAction(AccessibilityAction.ACTION_SET_TEXT.getId(), arguments); 3904 * </code></pre></p> 3905 */ 3906 public static final AccessibilityAction ACTION_SET_TEXT = 3907 new AccessibilityAction( 3908 AccessibilityNodeInfo.ACTION_SET_TEXT, null); 3909 3910 /** 3911 * Action that requests the node make its bounding rectangle visible 3912 * on the screen, scrolling if necessary just enough. 3913 * 3914 * @see View#requestRectangleOnScreen(Rect) 3915 */ 3916 public static final AccessibilityAction ACTION_SHOW_ON_SCREEN = 3917 new AccessibilityAction(R.id.accessibilityActionShowOnScreen, null); 3918 3919 /** 3920 * Action that scrolls the node to make the specified collection 3921 * position visible on screen. 3922 * <p> 3923 * <strong>Arguments:</strong> 3924 * <ul> 3925 * <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_ROW_INT}</li> 3926 * <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_COLUMN_INT}</li> 3927 * <ul> 3928 * 3929 * @see AccessibilityNodeInfo#getCollectionInfo() 3930 */ 3931 public static final AccessibilityAction ACTION_SCROLL_TO_POSITION = 3932 new AccessibilityAction(R.id.accessibilityActionScrollToPosition, null); 3933 3934 /** 3935 * Action to scroll the node content up. 3936 */ 3937 public static final AccessibilityAction ACTION_SCROLL_UP = 3938 new AccessibilityAction(R.id.accessibilityActionScrollUp, null); 3939 3940 /** 3941 * Action to scroll the node content left. 3942 */ 3943 public static final AccessibilityAction ACTION_SCROLL_LEFT = 3944 new AccessibilityAction(R.id.accessibilityActionScrollLeft, null); 3945 3946 /** 3947 * Action to scroll the node content down. 3948 */ 3949 public static final AccessibilityAction ACTION_SCROLL_DOWN = 3950 new AccessibilityAction(R.id.accessibilityActionScrollDown, null); 3951 3952 /** 3953 * Action to scroll the node content right. 3954 */ 3955 public static final AccessibilityAction ACTION_SCROLL_RIGHT = 3956 new AccessibilityAction(R.id.accessibilityActionScrollRight, null); 3957 3958 /** 3959 * Action that context clicks the node. 3960 */ 3961 public static final AccessibilityAction ACTION_CONTEXT_CLICK = 3962 new AccessibilityAction(R.id.accessibilityActionContextClick, null); 3963 3964 /** 3965 * Action that sets progress between {@link RangeInfo#getMin() RangeInfo.getMin()} and 3966 * {@link RangeInfo#getMax() RangeInfo.getMax()}. It should use the same value type as 3967 * {@link RangeInfo#getType() RangeInfo.getType()} 3968 * <p> 3969 * <strong>Arguments:</strong> 3970 * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_PROGRESS_VALUE} 3971 * 3972 * @see RangeInfo 3973 */ 3974 public static final AccessibilityAction ACTION_SET_PROGRESS = 3975 new AccessibilityAction(R.id.accessibilityActionSetProgress, null); 3976 3977 private static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<>(); 3978 static { 3979 sStandardActions.add(ACTION_FOCUS); 3980 sStandardActions.add(ACTION_CLEAR_FOCUS); 3981 sStandardActions.add(ACTION_SELECT); 3982 sStandardActions.add(ACTION_CLEAR_SELECTION); 3983 sStandardActions.add(ACTION_CLICK); 3984 sStandardActions.add(ACTION_LONG_CLICK); 3985 sStandardActions.add(ACTION_ACCESSIBILITY_FOCUS); 3986 sStandardActions.add(ACTION_CLEAR_ACCESSIBILITY_FOCUS); 3987 sStandardActions.add(ACTION_NEXT_AT_MOVEMENT_GRANULARITY); 3988 sStandardActions.add(ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); 3989 sStandardActions.add(ACTION_NEXT_HTML_ELEMENT); 3990 sStandardActions.add(ACTION_PREVIOUS_HTML_ELEMENT); 3991 sStandardActions.add(ACTION_SCROLL_FORWARD); 3992 sStandardActions.add(ACTION_SCROLL_BACKWARD); 3993 sStandardActions.add(ACTION_COPY); 3994 sStandardActions.add(ACTION_PASTE); 3995 sStandardActions.add(ACTION_CUT); 3996 sStandardActions.add(ACTION_SET_SELECTION); 3997 sStandardActions.add(ACTION_EXPAND); 3998 sStandardActions.add(ACTION_COLLAPSE); 3999 sStandardActions.add(ACTION_DISMISS); 4000 sStandardActions.add(ACTION_SET_TEXT); 4001 sStandardActions.add(ACTION_SHOW_ON_SCREEN); 4002 sStandardActions.add(ACTION_SCROLL_TO_POSITION); 4003 sStandardActions.add(ACTION_SCROLL_UP); 4004 sStandardActions.add(ACTION_SCROLL_LEFT); 4005 sStandardActions.add(ACTION_SCROLL_DOWN); 4006 sStandardActions.add(ACTION_SCROLL_RIGHT); 4007 sStandardActions.add(ACTION_SET_PROGRESS); 4008 sStandardActions.add(ACTION_CONTEXT_CLICK); 4009 } 4010 4011 private final int mActionId; 4012 private final CharSequence mLabel; 4013 4014 /** 4015 * Creates a new AccessibilityAction. For adding a standard action without a specific label, 4016 * use the static constants. 4017 * 4018 * You can also override the description for one the standard actions. Below is an example 4019 * how to override the standard click action by adding a custom label: 4020 * <pre> 4021 * AccessibilityAction action = new AccessibilityAction( 4022 * AccessibilityAction.ACTION_CLICK.getId(), getLocalizedLabel()); 4023 * node.addAction(action); 4024 * </pre> 4025 * 4026 * @param actionId The id for this action. This should either be one of the 4027 * standard actions or a specific action for your app. In that case it is 4028 * required to use a resource identifier. 4029 * @param label The label for the new AccessibilityAction. 4030 */ 4031 public AccessibilityAction(int actionId, @Nullable CharSequence label) { 4032 if ((actionId & ACTION_TYPE_MASK) == 0 && Integer.bitCount(actionId) != 1) { 4033 throw new IllegalArgumentException("Invalid standard action id"); 4034 } 4035 4036 mActionId = actionId; 4037 mLabel = label; 4038 } 4039 4040 /** 4041 * Gets the id for this action. 4042 * 4043 * @return The action id. 4044 */ 4045 public int getId() { 4046 return mActionId; 4047 } 4048 4049 /** 4050 * Gets the label for this action. Its purpose is to describe the 4051 * action to user. 4052 * 4053 * @return The label. 4054 */ 4055 public CharSequence getLabel() { 4056 return mLabel; 4057 } 4058 4059 @Override 4060 public int hashCode() { 4061 return mActionId; 4062 } 4063 4064 @Override 4065 public boolean equals(Object other) { 4066 if (other == null) { 4067 return false; 4068 } 4069 4070 if (other == this) { 4071 return true; 4072 } 4073 4074 if (getClass() != other.getClass()) { 4075 return false; 4076 } 4077 4078 return mActionId == ((AccessibilityAction)other).mActionId; 4079 } 4080 4081 @Override 4082 public String toString() { 4083 return "AccessibilityAction: " + getActionSymbolicName(mActionId) + " - " + mLabel; 4084 } 4085 } 4086 4087 /** 4088 * Class with information if a node is a range. Use 4089 * {@link RangeInfo#obtain(int, float, float, float)} to get an instance. Recycling is 4090 * handled by the {@link AccessibilityNodeInfo} to which this object is attached. 4091 */ 4092 public static final class RangeInfo { 4093 private static final int MAX_POOL_SIZE = 10; 4094 4095 /** Range type: integer. */ 4096 public static final int RANGE_TYPE_INT = 0; 4097 /** Range type: float. */ 4098 public static final int RANGE_TYPE_FLOAT = 1; 4099 /** Range type: percent with values from zero to one.*/ 4100 public static final int RANGE_TYPE_PERCENT = 2; 4101 4102 private static final SynchronizedPool<RangeInfo> sPool = 4103 new SynchronizedPool<AccessibilityNodeInfo.RangeInfo>(MAX_POOL_SIZE); 4104 4105 private int mType; 4106 private float mMin; 4107 private float mMax; 4108 private float mCurrent; 4109 4110 /** 4111 * Obtains a pooled instance that is a clone of another one. 4112 * 4113 * @param other The instance to clone. 4114 * 4115 * @hide 4116 */ 4117 public static RangeInfo obtain(RangeInfo other) { 4118 return obtain(other.mType, other.mMin, other.mMax, other.mCurrent); 4119 } 4120 4121 /** 4122 * Obtains a pooled instance. 4123 * 4124 * @param type The type of the range. 4125 * @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no 4126 * minimum. 4127 * @param max The maximum value. Use {@code Float.POSITIVE_INFINITY} if the range has no 4128 * maximum. 4129 * @param current The current value. 4130 */ 4131 public static RangeInfo obtain(int type, float min, float max, float current) { 4132 RangeInfo info = sPool.acquire(); 4133 if (info == null) { 4134 return new RangeInfo(type, min, max, current); 4135 } 4136 4137 info.mType = type; 4138 info.mMin = min; 4139 info.mMax = max; 4140 info.mCurrent = current; 4141 return info; 4142 } 4143 4144 /** 4145 * Creates a new range. 4146 * 4147 * @param type The type of the range. 4148 * @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no 4149 * minimum. 4150 * @param max The maximum value. Use {@code Float.POSITIVE_INFINITY} if the range has no 4151 * maximum. 4152 * @param current The current value. 4153 */ 4154 private RangeInfo(int type, float min, float max, float current) { 4155 mType = type; 4156 mMin = min; 4157 mMax = max; 4158 mCurrent = current; 4159 } 4160 4161 /** 4162 * Gets the range type. 4163 * 4164 * @return The range type. 4165 * 4166 * @see #RANGE_TYPE_INT 4167 * @see #RANGE_TYPE_FLOAT 4168 * @see #RANGE_TYPE_PERCENT 4169 */ 4170 public int getType() { 4171 return mType; 4172 } 4173 4174 /** 4175 * Gets the minimum value. 4176 * 4177 * @return The minimum value, or {@code Float.NEGATIVE_INFINITY} if no minimum exists. 4178 */ 4179 public float getMin() { 4180 return mMin; 4181 } 4182 4183 /** 4184 * Gets the maximum value. 4185 * 4186 * @return The maximum value, or {@code Float.POSITIVE_INFINITY} if no maximum exists. 4187 */ 4188 public float getMax() { 4189 return mMax; 4190 } 4191 4192 /** 4193 * Gets the current value. 4194 * 4195 * @return The current value. 4196 */ 4197 public float getCurrent() { 4198 return mCurrent; 4199 } 4200 4201 /** 4202 * Recycles this instance. 4203 */ 4204 void recycle() { 4205 clear(); 4206 sPool.release(this); 4207 } 4208 4209 private void clear() { 4210 mType = 0; 4211 mMin = 0; 4212 mMax = 0; 4213 mCurrent = 0; 4214 } 4215 } 4216 4217 /** 4218 * Class with information if a node is a collection. Use 4219 * {@link CollectionInfo#obtain(int, int, boolean)} to get an instance. Recycling is 4220 * handled by the {@link AccessibilityNodeInfo} to which this object is attached. 4221 * <p> 4222 * A collection of items has rows and columns and may be hierarchical. 4223 * For example, a horizontal list is a collection with one column, as 4224 * many rows as the list items, and is not hierarchical; A table is a 4225 * collection with several rows, several columns, and is not hierarchical; 4226 * A vertical tree is a hierarchical collection with one column and 4227 * as many rows as the first level children. 4228 * </p> 4229 */ 4230 public static final class CollectionInfo { 4231 /** Selection mode where items are not selectable. */ 4232 public static final int SELECTION_MODE_NONE = 0; 4233 4234 /** Selection mode where a single item may be selected. */ 4235 public static final int SELECTION_MODE_SINGLE = 1; 4236 4237 /** Selection mode where multiple items may be selected. */ 4238 public static final int SELECTION_MODE_MULTIPLE = 2; 4239 4240 private static final int MAX_POOL_SIZE = 20; 4241 4242 private static final SynchronizedPool<CollectionInfo> sPool = 4243 new SynchronizedPool<>(MAX_POOL_SIZE); 4244 4245 private int mRowCount; 4246 private int mColumnCount; 4247 private boolean mHierarchical; 4248 private int mSelectionMode; 4249 4250 /** 4251 * Obtains a pooled instance that is a clone of another one. 4252 * 4253 * @param other The instance to clone. 4254 * @hide 4255 */ 4256 public static CollectionInfo obtain(CollectionInfo other) { 4257 return CollectionInfo.obtain(other.mRowCount, other.mColumnCount, other.mHierarchical, 4258 other.mSelectionMode); 4259 } 4260 4261 /** 4262 * Obtains a pooled instance. 4263 * 4264 * @param rowCount The number of rows. 4265 * @param columnCount The number of columns. 4266 * @param hierarchical Whether the collection is hierarchical. 4267 */ 4268 public static CollectionInfo obtain(int rowCount, int columnCount, 4269 boolean hierarchical) { 4270 return obtain(rowCount, columnCount, hierarchical, SELECTION_MODE_NONE); 4271 } 4272 4273 /** 4274 * Obtains a pooled instance. 4275 * 4276 * @param rowCount The number of rows. 4277 * @param columnCount The number of columns. 4278 * @param hierarchical Whether the collection is hierarchical. 4279 * @param selectionMode The collection's selection mode, one of: 4280 * <ul> 4281 * <li>{@link #SELECTION_MODE_NONE} 4282 * <li>{@link #SELECTION_MODE_SINGLE} 4283 * <li>{@link #SELECTION_MODE_MULTIPLE} 4284 * </ul> 4285 */ 4286 public static CollectionInfo obtain(int rowCount, int columnCount, 4287 boolean hierarchical, int selectionMode) { 4288 final CollectionInfo info = sPool.acquire(); 4289 if (info == null) { 4290 return new CollectionInfo(rowCount, columnCount, hierarchical, selectionMode); 4291 } 4292 4293 info.mRowCount = rowCount; 4294 info.mColumnCount = columnCount; 4295 info.mHierarchical = hierarchical; 4296 info.mSelectionMode = selectionMode; 4297 return info; 4298 } 4299 4300 /** 4301 * Creates a new instance. 4302 * 4303 * @param rowCount The number of rows. 4304 * @param columnCount The number of columns. 4305 * @param hierarchical Whether the collection is hierarchical. 4306 * @param selectionMode The collection's selection mode. 4307 */ 4308 private CollectionInfo(int rowCount, int columnCount, boolean hierarchical, 4309 int selectionMode) { 4310 mRowCount = rowCount; 4311 mColumnCount = columnCount; 4312 mHierarchical = hierarchical; 4313 mSelectionMode = selectionMode; 4314 } 4315 4316 /** 4317 * Gets the number of rows. 4318 * 4319 * @return The row count. 4320 */ 4321 public int getRowCount() { 4322 return mRowCount; 4323 } 4324 4325 /** 4326 * Gets the number of columns. 4327 * 4328 * @return The column count. 4329 */ 4330 public int getColumnCount() { 4331 return mColumnCount; 4332 } 4333 4334 /** 4335 * Gets if the collection is a hierarchically ordered. 4336 * 4337 * @return Whether the collection is hierarchical. 4338 */ 4339 public boolean isHierarchical() { 4340 return mHierarchical; 4341 } 4342 4343 /** 4344 * Gets the collection's selection mode. 4345 * 4346 * @return The collection's selection mode, one of: 4347 * <ul> 4348 * <li>{@link #SELECTION_MODE_NONE} 4349 * <li>{@link #SELECTION_MODE_SINGLE} 4350 * <li>{@link #SELECTION_MODE_MULTIPLE} 4351 * </ul> 4352 */ 4353 public int getSelectionMode() { 4354 return mSelectionMode; 4355 } 4356 4357 /** 4358 * Recycles this instance. 4359 */ 4360 void recycle() { 4361 clear(); 4362 sPool.release(this); 4363 } 4364 4365 private void clear() { 4366 mRowCount = 0; 4367 mColumnCount = 0; 4368 mHierarchical = false; 4369 mSelectionMode = SELECTION_MODE_NONE; 4370 } 4371 } 4372 4373 /** 4374 * Class with information if a node is a collection item. Use 4375 * {@link CollectionItemInfo#obtain(int, int, int, int, boolean)} 4376 * to get an instance. Recycling is handled by the {@link AccessibilityNodeInfo} to which this 4377 * object is attached. 4378 * <p> 4379 * A collection item is contained in a collection, it starts at 4380 * a given row and column in the collection, and spans one or 4381 * more rows and columns. For example, a header of two related 4382 * table columns starts at the first row and the first column, 4383 * spans one row and two columns. 4384 * </p> 4385 */ 4386 public static final class CollectionItemInfo { 4387 private static final int MAX_POOL_SIZE = 20; 4388 4389 private static final SynchronizedPool<CollectionItemInfo> sPool = 4390 new SynchronizedPool<>(MAX_POOL_SIZE); 4391 4392 /** 4393 * Obtains a pooled instance that is a clone of another one. 4394 * 4395 * @param other The instance to clone. 4396 * @hide 4397 */ 4398 public static CollectionItemInfo obtain(CollectionItemInfo other) { 4399 return CollectionItemInfo.obtain(other.mRowIndex, other.mRowSpan, other.mColumnIndex, 4400 other.mColumnSpan, other.mHeading, other.mSelected); 4401 } 4402 4403 /** 4404 * Obtains a pooled instance. 4405 * 4406 * @param rowIndex The row index at which the item is located. 4407 * @param rowSpan The number of rows the item spans. 4408 * @param columnIndex The column index at which the item is located. 4409 * @param columnSpan The number of columns the item spans. 4410 * @param heading Whether the item is a heading. 4411 */ 4412 public static CollectionItemInfo obtain(int rowIndex, int rowSpan, 4413 int columnIndex, int columnSpan, boolean heading) { 4414 return obtain(rowIndex, rowSpan, columnIndex, columnSpan, heading, false); 4415 } 4416 4417 /** 4418 * Obtains a pooled instance. 4419 * 4420 * @param rowIndex The row index at which the item is located. 4421 * @param rowSpan The number of rows the item spans. 4422 * @param columnIndex The column index at which the item is located. 4423 * @param columnSpan The number of columns the item spans. 4424 * @param heading Whether the item is a heading. 4425 * @param selected Whether the item is selected. 4426 */ 4427 public static CollectionItemInfo obtain(int rowIndex, int rowSpan, 4428 int columnIndex, int columnSpan, boolean heading, boolean selected) { 4429 final CollectionItemInfo info = sPool.acquire(); 4430 if (info == null) { 4431 return new CollectionItemInfo( 4432 rowIndex, rowSpan, columnIndex, columnSpan, heading, selected); 4433 } 4434 4435 info.mRowIndex = rowIndex; 4436 info.mRowSpan = rowSpan; 4437 info.mColumnIndex = columnIndex; 4438 info.mColumnSpan = columnSpan; 4439 info.mHeading = heading; 4440 info.mSelected = selected; 4441 return info; 4442 } 4443 4444 private boolean mHeading; 4445 private int mColumnIndex; 4446 private int mRowIndex; 4447 private int mColumnSpan; 4448 private int mRowSpan; 4449 private boolean mSelected; 4450 4451 /** 4452 * Creates a new instance. 4453 * 4454 * @param rowIndex The row index at which the item is located. 4455 * @param rowSpan The number of rows the item spans. 4456 * @param columnIndex The column index at which the item is located. 4457 * @param columnSpan The number of columns the item spans. 4458 * @param heading Whether the item is a heading. 4459 */ 4460 private CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan, 4461 boolean heading, boolean selected) { 4462 mRowIndex = rowIndex; 4463 mRowSpan = rowSpan; 4464 mColumnIndex = columnIndex; 4465 mColumnSpan = columnSpan; 4466 mHeading = heading; 4467 mSelected = selected; 4468 } 4469 4470 /** 4471 * Gets the column index at which the item is located. 4472 * 4473 * @return The column index. 4474 */ 4475 public int getColumnIndex() { 4476 return mColumnIndex; 4477 } 4478 4479 /** 4480 * Gets the row index at which the item is located. 4481 * 4482 * @return The row index. 4483 */ 4484 public int getRowIndex() { 4485 return mRowIndex; 4486 } 4487 4488 /** 4489 * Gets the number of columns the item spans. 4490 * 4491 * @return The column span. 4492 */ 4493 public int getColumnSpan() { 4494 return mColumnSpan; 4495 } 4496 4497 /** 4498 * Gets the number of rows the item spans. 4499 * 4500 * @return The row span. 4501 */ 4502 public int getRowSpan() { 4503 return mRowSpan; 4504 } 4505 4506 /** 4507 * Gets if the collection item is a heading. For example, section 4508 * heading, table header, etc. 4509 * 4510 * @return If the item is a heading. 4511 */ 4512 public boolean isHeading() { 4513 return mHeading; 4514 } 4515 4516 /** 4517 * Gets if the collection item is selected. 4518 * 4519 * @return If the item is selected. 4520 */ 4521 public boolean isSelected() { 4522 return mSelected; 4523 } 4524 4525 /** 4526 * Recycles this instance. 4527 */ 4528 void recycle() { 4529 clear(); 4530 sPool.release(this); 4531 } 4532 4533 private void clear() { 4534 mColumnIndex = 0; 4535 mColumnSpan = 0; 4536 mRowIndex = 0; 4537 mRowSpan = 0; 4538 mHeading = false; 4539 mSelected = false; 4540 } 4541 } 4542 4543 /** 4544 * @see android.os.Parcelable.Creator 4545 */ 4546 public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR = 4547 new Parcelable.Creator<AccessibilityNodeInfo>() { 4548 @Override 4549 public AccessibilityNodeInfo createFromParcel(Parcel parcel) { 4550 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 4551 info.initFromParcel(parcel); 4552 return info; 4553 } 4554 4555 @Override 4556 public AccessibilityNodeInfo[] newArray(int size) { 4557 return new AccessibilityNodeInfo[size]; 4558 } 4559 }; 4560} 4561