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