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