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