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