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