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