• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.graphics.Rect;
21 import android.os.Bundle;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.text.InputType;
25 import android.util.Pools.SynchronizedPool;
26 import android.util.SparseLongArray;
27 import android.view.View;
28 
29 import java.util.Collections;
30 import java.util.List;
31 
32 /**
33  * This class represents a node of the window content as well as actions that
34  * can be requested from its source. From the point of view of an
35  * {@link android.accessibilityservice.AccessibilityService} a window content is
36  * presented as tree of accessibility node info which may or may not map one-to-one
37  * to the view hierarchy. In other words, a custom view is free to report itself as
38  * a tree of accessibility node info.
39  * </p>
40  * <p>
41  * Once an accessibility node info is delivered to an accessibility service it is
42  * made immutable and calling a state mutation method generates an error.
43  * </p>
44  * <p>
45  * Please refer to {@link android.accessibilityservice.AccessibilityService} for
46  * details about how to obtain a handle to window content as a tree of accessibility
47  * node info as well as familiarizing with the security model.
48  * </p>
49  * <div class="special reference">
50  * <h3>Developer Guides</h3>
51  * <p>For more information about making applications accessible, read the
52  * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
53  * developer guide.</p>
54  * </div>
55  *
56  * @see android.accessibilityservice.AccessibilityService
57  * @see AccessibilityEvent
58  * @see AccessibilityManager
59  */
60 public class AccessibilityNodeInfo implements Parcelable {
61 
62     private static final boolean DEBUG = false;
63 
64     /** @hide */
65     public static final int UNDEFINED = -1;
66 
67     /** @hide */
68     public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED, UNDEFINED);
69 
70     /** @hide */
71     public static final int ACTIVE_WINDOW_ID = UNDEFINED;
72 
73     /** @hide */
74     public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
75 
76     /** @hide */
77     public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002;
78 
79     /** @hide */
80     public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004;
81 
82     /** @hide */
83     public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008;
84 
85     /** @hide */
86     public static final int FLAG_REPORT_VIEW_IDS = 0x00000010;
87 
88     // Actions.
89 
90     /**
91      * Action that gives input focus to the node.
92      */
93     public static final int ACTION_FOCUS =  0x00000001;
94 
95     /**
96      * Action that clears input focus of the node.
97      */
98     public static final int ACTION_CLEAR_FOCUS = 0x00000002;
99 
100     /**
101      * Action that selects the node.
102      */
103     public static final int ACTION_SELECT = 0x00000004;
104 
105     /**
106      * Action that unselects the node.
107      */
108     public static final int ACTION_CLEAR_SELECTION = 0x00000008;
109 
110     /**
111      * Action that clicks on the node info.
112      */
113     public static final int ACTION_CLICK = 0x00000010;
114 
115     /**
116      * Action that long clicks on the node.
117      */
118     public static final int ACTION_LONG_CLICK = 0x00000020;
119 
120     /**
121      * Action that gives accessibility focus to the node.
122      */
123     public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040;
124 
125     /**
126      * Action that clears accessibility focus of the node.
127      */
128     public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080;
129 
130     /**
131      * Action that requests to go to the next entity in this node's text
132      * at a given movement granularity. For example, move to the next character,
133      * word, etc.
134      * <p>
135      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
136      * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
137      * <strong>Example:</strong> Move to the previous character and do not extend selection.
138      * <code><pre><p>
139      *   Bundle arguments = new Bundle();
140      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
141      *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
142      *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
143      *           false);
144      *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
145      * </code></pre></p>
146      * </p>
147      *
148      * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
149      * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
150      *
151      * @see #setMovementGranularities(int)
152      * @see #getMovementGranularities()
153      *
154      * @see #MOVEMENT_GRANULARITY_CHARACTER
155      * @see #MOVEMENT_GRANULARITY_WORD
156      * @see #MOVEMENT_GRANULARITY_LINE
157      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
158      * @see #MOVEMENT_GRANULARITY_PAGE
159      */
160     public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100;
161 
162     /**
163      * Action that requests to go to the previous entity in this node's text
164      * at a given movement granularity. For example, move to the next character,
165      * word, etc.
166      * <p>
167      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
168      * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
169      * <strong>Example:</strong> Move to the next character and do not extend selection.
170      * <code><pre><p>
171      *   Bundle arguments = new Bundle();
172      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
173      *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
174      *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
175      *           false);
176      *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
177      *           arguments);
178      * </code></pre></p>
179      * </p>
180      *
181      * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
182      * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
183      *
184      * @see #setMovementGranularities(int)
185      * @see #getMovementGranularities()
186      *
187      * @see #MOVEMENT_GRANULARITY_CHARACTER
188      * @see #MOVEMENT_GRANULARITY_WORD
189      * @see #MOVEMENT_GRANULARITY_LINE
190      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
191      * @see #MOVEMENT_GRANULARITY_PAGE
192      */
193     public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200;
194 
195     /**
196      * Action to move to the next HTML element of a given type. For example, move
197      * to the BUTTON, INPUT, TABLE, etc.
198      * <p>
199      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
200      * <strong>Example:</strong>
201      * <code><pre><p>
202      *   Bundle arguments = new Bundle();
203      *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
204      *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
205      * </code></pre></p>
206      * </p>
207      */
208     public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;
209 
210     /**
211      * Action to move to the previous HTML element of a given type. For example, move
212      * to the BUTTON, INPUT, TABLE, etc.
213      * <p>
214      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
215      * <strong>Example:</strong>
216      * <code><pre><p>
217      *   Bundle arguments = new Bundle();
218      *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
219      *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
220      * </code></pre></p>
221      * </p>
222      */
223     public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
224 
225     /**
226      * Action to scroll the node content forward.
227      */
228     public static final int ACTION_SCROLL_FORWARD = 0x00001000;
229 
230     /**
231      * Action to scroll the node content backward.
232      */
233     public static final int ACTION_SCROLL_BACKWARD = 0x00002000;
234 
235     /**
236      * Action to copy the current selection to the clipboard.
237      */
238     public static final int ACTION_COPY = 0x00004000;
239 
240     /**
241      * Action to paste the current clipboard content.
242      */
243     public static final int ACTION_PASTE = 0x00008000;
244 
245     /**
246      * Action to cut the current selection and place it to the clipboard.
247      */
248     public static final int ACTION_CUT = 0x00010000;
249 
250     /**
251      * Action to set the selection. Performing this action with no arguments
252      * clears the selection.
253      * <p>
254      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SELECTION_START_INT},
255      * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br>
256      * <strong>Example:</strong>
257      * <code><pre><p>
258      *   Bundle arguments = new Bundle();
259      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1);
260      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2);
261      *   info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments);
262      * </code></pre></p>
263      * </p>
264      *
265      * @see #ACTION_ARGUMENT_SELECTION_START_INT
266      * @see #ACTION_ARGUMENT_SELECTION_END_INT
267      */
268     public static final int ACTION_SET_SELECTION = 0x00020000;
269 
270     /**
271      * Action to expand an expandable node.
272      */
273     public static final int ACTION_EXPAND = 0x00040000;
274 
275     /**
276      * Action to collapse an expandable node.
277      */
278     public static final int ACTION_COLLAPSE = 0x00080000;
279 
280     /**
281      * Action to dismiss a dismissable node.
282      */
283     public static final int ACTION_DISMISS = 0x00100000;
284 
285     // Action arguments
286 
287     /**
288      * Argument for which movement granularity to be used when traversing the node text.
289      * <p>
290      * <strong>Type:</strong> int<br>
291      * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
292      * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
293      * </p>
294      *
295      * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
296      * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
297      */
298     public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
299             "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
300 
301     /**
302      * Argument for which HTML element to get moving to the next/previous HTML element.
303      * <p>
304      * <strong>Type:</strong> String<br>
305      * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
306      *         {@link #ACTION_PREVIOUS_HTML_ELEMENT}
307      * </p>
308      *
309      * @see #ACTION_NEXT_HTML_ELEMENT
310      * @see #ACTION_PREVIOUS_HTML_ELEMENT
311      */
312     public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
313             "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
314 
315     /**
316      * Argument for whether when moving at granularity to extend the selection
317      * or to move it otherwise.
318      * <p>
319      * <strong>Type:</strong> boolean<br>
320      * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
321      * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
322      * </p>
323      *
324      * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
325      * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
326      */
327     public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN =
328             "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
329 
330     /**
331      * Argument for specifying the selection start.
332      * <p>
333      * <strong>Type:</strong> int<br>
334      * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
335      * </p>
336      *
337      * @see #ACTION_SET_SELECTION
338      */
339     public static final String ACTION_ARGUMENT_SELECTION_START_INT =
340             "ACTION_ARGUMENT_SELECTION_START_INT";
341 
342     /**
343      * Argument for specifying the selection end.
344      * <p>
345      * <strong>Type:</strong> int<br>
346      * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
347      * </p>
348      *
349      * @see #ACTION_SET_SELECTION
350      */
351     public static final String ACTION_ARGUMENT_SELECTION_END_INT =
352             "ACTION_ARGUMENT_SELECTION_END_INT";
353 
354     // Focus types
355 
356     /**
357      * The input focus.
358      */
359     public static final int FOCUS_INPUT = 1;
360 
361     /**
362      * The accessibility focus.
363      */
364     public static final int FOCUS_ACCESSIBILITY = 2;
365 
366     // Movement granularities
367 
368     /**
369      * Movement granularity bit for traversing the text of a node by character.
370      */
371     public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001;
372 
373     /**
374      * Movement granularity bit for traversing the text of a node by word.
375      */
376     public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002;
377 
378     /**
379      * Movement granularity bit for traversing the text of a node by line.
380      */
381     public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004;
382 
383     /**
384      * Movement granularity bit for traversing the text of a node by paragraph.
385      */
386     public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008;
387 
388     /**
389      * Movement granularity bit for traversing the text of a node by page.
390      */
391     public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010;
392 
393     // Boolean attributes.
394 
395     private static final int BOOLEAN_PROPERTY_CHECKABLE = 0x00000001;
396 
397     private static final int BOOLEAN_PROPERTY_CHECKED = 0x00000002;
398 
399     private static final int BOOLEAN_PROPERTY_FOCUSABLE = 0x00000004;
400 
401     private static final int BOOLEAN_PROPERTY_FOCUSED = 0x00000008;
402 
403     private static final int BOOLEAN_PROPERTY_SELECTED = 0x00000010;
404 
405     private static final int BOOLEAN_PROPERTY_CLICKABLE = 0x00000020;
406 
407     private static final int BOOLEAN_PROPERTY_LONG_CLICKABLE = 0x00000040;
408 
409     private static final int BOOLEAN_PROPERTY_ENABLED = 0x00000080;
410 
411     private static final int BOOLEAN_PROPERTY_PASSWORD = 0x00000100;
412 
413     private static final int BOOLEAN_PROPERTY_SCROLLABLE = 0x00000200;
414 
415     private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
416 
417     private static final int BOOLEAN_PROPERTY_VISIBLE_TO_USER = 0x00000800;
418 
419     private static final int BOOLEAN_PROPERTY_EDITABLE = 0x00001000;
420 
421     private static final int BOOLEAN_PROPERTY_OPENS_POPUP = 0x00002000;
422 
423     private static final int BOOLEAN_PROPERTY_DISMISSABLE = 0x00004000;
424 
425     private static final int BOOLEAN_PROPERTY_MULTI_LINE = 0x00008000;
426 
427     private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 0x00010000;
428 
429     /**
430      * Bits that provide the id of a virtual descendant of a view.
431      */
432     private static final long VIRTUAL_DESCENDANT_ID_MASK = 0xffffffff00000000L;
433 
434     /**
435      * Bit shift of {@link #VIRTUAL_DESCENDANT_ID_MASK} to get to the id for a
436      * virtual descendant of a view. Such a descendant does not exist in the view
437      * hierarchy and is only reported via the accessibility APIs.
438      */
439     private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32;
440 
441     /**
442      * Gets the accessibility view id which identifies a View in the view three.
443      *
444      * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
445      * @return The accessibility view id part of the node id.
446      *
447      * @hide
448      */
getAccessibilityViewId(long accessibilityNodeId)449     public static int getAccessibilityViewId(long accessibilityNodeId) {
450         return (int) accessibilityNodeId;
451     }
452 
453     /**
454      * Gets the virtual descendant id which identifies an imaginary view in a
455      * containing View.
456      *
457      * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
458      * @return The virtual view id part of the node id.
459      *
460      * @hide
461      */
getVirtualDescendantId(long accessibilityNodeId)462     public static int getVirtualDescendantId(long accessibilityNodeId) {
463         return (int) ((accessibilityNodeId & VIRTUAL_DESCENDANT_ID_MASK)
464                 >> VIRTUAL_DESCENDANT_ID_SHIFT);
465     }
466 
467     /**
468      * Makes a node id by shifting the <code>virtualDescendantId</code>
469      * by {@link #VIRTUAL_DESCENDANT_ID_SHIFT} and taking
470      * the bitwise or with the <code>accessibilityViewId</code>.
471      *
472      * @param accessibilityViewId A View accessibility id.
473      * @param virtualDescendantId A virtual descendant id.
474      * @return The node id.
475      *
476      * @hide
477      */
makeNodeId(int accessibilityViewId, int virtualDescendantId)478     public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) {
479         return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
480     }
481 
482     // Housekeeping.
483     private static final int MAX_POOL_SIZE = 50;
484     private static final SynchronizedPool<AccessibilityNodeInfo> sPool =
485             new SynchronizedPool<AccessibilityNodeInfo>(MAX_POOL_SIZE);
486 
487     private boolean mSealed;
488 
489     // Data.
490     private int mWindowId = UNDEFINED;
491     private long mSourceNodeId = ROOT_NODE_ID;
492     private long mParentNodeId = ROOT_NODE_ID;
493     private long mLabelForId = ROOT_NODE_ID;
494     private long mLabeledById = ROOT_NODE_ID;
495 
496     private int mBooleanProperties;
497     private final Rect mBoundsInParent = new Rect();
498     private final Rect mBoundsInScreen = new Rect();
499 
500     private CharSequence mPackageName;
501     private CharSequence mClassName;
502     private CharSequence mText;
503     private CharSequence mContentDescription;
504     private String mViewIdResourceName;
505 
506     private final SparseLongArray mChildNodeIds = new SparseLongArray();
507     private int mActions;
508 
509     private int mMovementGranularities;
510 
511     private int mTextSelectionStart = UNDEFINED;
512     private int mTextSelectionEnd = UNDEFINED;
513     private int mInputType = InputType.TYPE_NULL;
514     private int mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
515 
516     private Bundle mExtras;
517 
518     private int mConnectionId = UNDEFINED;
519 
520     private RangeInfo mRangeInfo;
521     private CollectionInfo mCollectionInfo;
522     private CollectionItemInfo mCollectionItemInfo;
523 
524     /**
525      * Hide constructor from clients.
526      */
AccessibilityNodeInfo()527     private AccessibilityNodeInfo() {
528         /* do nothing */
529     }
530 
531     /**
532      * Sets the source.
533      * <p>
534      *   <strong>Note:</strong> Cannot be called from an
535      *   {@link android.accessibilityservice.AccessibilityService}.
536      *   This class is made immutable before being delivered to an AccessibilityService.
537      * </p>
538      *
539      * @param source The info source.
540      */
setSource(View source)541     public void setSource(View source) {
542         setSource(source, UNDEFINED);
543     }
544 
545     /**
546      * Sets the source to be a virtual descendant of the given <code>root</code>.
547      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
548      * is set as the source.
549      * <p>
550      * A virtual descendant is an imaginary View that is reported as a part of the view
551      * hierarchy for accessibility purposes. This enables custom views that draw complex
552      * content to report themselves as a tree of virtual views, thus conveying their
553      * logical structure.
554      * </p>
555      * <p>
556      *   <strong>Note:</strong> Cannot be called from an
557      *   {@link android.accessibilityservice.AccessibilityService}.
558      *   This class is made immutable before being delivered to an AccessibilityService.
559      * </p>
560      *
561      * @param root The root of the virtual subtree.
562      * @param virtualDescendantId The id of the virtual descendant.
563      */
setSource(View root, int virtualDescendantId)564     public void setSource(View root, int virtualDescendantId) {
565         enforceNotSealed();
566         mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED;
567         final int rootAccessibilityViewId =
568             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
569         mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
570     }
571 
572     /**
573      * Find the view that has the specified focus type. The search starts from
574      * the view represented by this node info.
575      *
576      * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
577      *         {@link #FOCUS_ACCESSIBILITY}.
578      * @return The node info of the focused view or null.
579      *
580      * @see #FOCUS_INPUT
581      * @see #FOCUS_ACCESSIBILITY
582      */
findFocus(int focus)583     public AccessibilityNodeInfo findFocus(int focus) {
584         enforceSealed();
585         enforceValidFocusType(focus);
586         if (!canPerformRequestOverConnection(mSourceNodeId)) {
587             return null;
588         }
589         return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId,
590                 mSourceNodeId, focus);
591     }
592 
593     /**
594      * Searches for the nearest view in the specified direction that can take
595      * the input focus.
596      *
597      * @param direction The direction. Can be one of:
598      *     {@link View#FOCUS_DOWN},
599      *     {@link View#FOCUS_UP},
600      *     {@link View#FOCUS_LEFT},
601      *     {@link View#FOCUS_RIGHT},
602      *     {@link View#FOCUS_FORWARD},
603      *     {@link View#FOCUS_BACKWARD}.
604      *
605      * @return The node info for the view that can take accessibility focus.
606      */
focusSearch(int direction)607     public AccessibilityNodeInfo focusSearch(int direction) {
608         enforceSealed();
609         enforceValidFocusDirection(direction);
610         if (!canPerformRequestOverConnection(mSourceNodeId)) {
611             return null;
612         }
613         return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId,
614                 mSourceNodeId, direction);
615     }
616 
617     /**
618      * Gets the id of the window from which the info comes from.
619      *
620      * @return The window id.
621      */
getWindowId()622     public int getWindowId() {
623         return mWindowId;
624     }
625 
626     /**
627      * Refreshes this info with the latest state of the view it represents.
628      * <p>
629      * <strong>Note:</strong> If this method returns false this info is obsolete
630      * since it represents a view that is no longer in the view tree and should
631      * be recycled.
632      * </p>
633      *
634      * @param bypassCache Whether to bypass the cache.
635      * @return Whether the refresh succeeded.
636      *
637      * @hide
638      */
refresh(boolean bypassCache)639     public boolean refresh(boolean bypassCache) {
640         enforceSealed();
641         if (!canPerformRequestOverConnection(mSourceNodeId)) {
642             return false;
643         }
644         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
645         AccessibilityNodeInfo refreshedInfo = client.findAccessibilityNodeInfoByAccessibilityId(
646                 mConnectionId, mWindowId, mSourceNodeId, bypassCache, 0);
647         if (refreshedInfo == null) {
648             return false;
649         }
650         init(refreshedInfo);
651         refreshedInfo.recycle();
652         return true;
653     }
654 
655     /**
656      * Refreshes this info with the latest state of the view it represents.
657      * <p>
658      * <strong>Note:</strong> If this method returns false this info is obsolete
659      * since it represents a view that is no longer in the view tree and should
660      * be recycled.
661      * </p>
662      * @return Whether the refresh succeeded.
663      */
refresh()664     public boolean refresh() {
665         return refresh(false);
666     }
667 
668     /**
669      * @return The ids of the children.
670      *
671      * @hide
672      */
getChildNodeIds()673     public SparseLongArray getChildNodeIds() {
674         return mChildNodeIds;
675     }
676 
677     /**
678      * Gets the number of children.
679      *
680      * @return The child count.
681      */
getChildCount()682     public int getChildCount() {
683         return mChildNodeIds.size();
684     }
685 
686     /**
687      * Get the child at given index.
688      * <p>
689      *   <strong>Note:</strong> It is a client responsibility to recycle the
690      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
691      *     to avoid creating of multiple instances.
692      * </p>
693      *
694      * @param index The child index.
695      * @return The child node.
696      *
697      * @throws IllegalStateException If called outside of an AccessibilityService.
698      *
699      */
getChild(int index)700     public AccessibilityNodeInfo getChild(int index) {
701         enforceSealed();
702         if (!canPerformRequestOverConnection(mSourceNodeId)) {
703             return null;
704         }
705         final long childId = mChildNodeIds.get(index);
706         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
707         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
708                 childId, false, FLAG_PREFETCH_DESCENDANTS);
709     }
710 
711     /**
712      * Adds a child.
713      * <p>
714      * <strong>Note:</strong> Cannot be called from an
715      * {@link android.accessibilityservice.AccessibilityService}.
716      * This class is made immutable before being delivered to an AccessibilityService.
717      * </p>
718      *
719      * @param child The child.
720      *
721      * @throws IllegalStateException If called from an AccessibilityService.
722      */
addChild(View child)723     public void addChild(View child) {
724         addChild(child, UNDEFINED);
725     }
726 
727     /**
728      * Adds a virtual child which is a descendant of the given <code>root</code>.
729      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
730      * is added as a child.
731      * <p>
732      * A virtual descendant is an imaginary View that is reported as a part of the view
733      * hierarchy for accessibility purposes. This enables custom views that draw complex
734      * content to report them selves as a tree of virtual views, thus conveying their
735      * logical structure.
736      * </p>
737      *
738      * @param root The root of the virtual subtree.
739      * @param virtualDescendantId The id of the virtual child.
740      */
addChild(View root, int virtualDescendantId)741     public void addChild(View root, int virtualDescendantId) {
742         enforceNotSealed();
743         final int index = mChildNodeIds.size();
744         final int rootAccessibilityViewId =
745             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
746         final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
747         mChildNodeIds.put(index, childNodeId);
748     }
749 
750     /**
751      * Gets the actions that can be performed on the node.
752      *
753      * @return The bit mask of with actions.
754      *
755      * @see AccessibilityNodeInfo#ACTION_FOCUS
756      * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
757      * @see AccessibilityNodeInfo#ACTION_SELECT
758      * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
759      * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS
760      * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS
761      * @see AccessibilityNodeInfo#ACTION_CLICK
762      * @see AccessibilityNodeInfo#ACTION_LONG_CLICK
763      * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
764      * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
765      * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT
766      * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT
767      * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD
768      * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD
769      */
getActions()770     public int getActions() {
771         return mActions;
772     }
773 
774     /**
775      * Adds an action that can be performed on the node.
776      * <p>
777      *   <strong>Note:</strong> Cannot be called from an
778      *   {@link android.accessibilityservice.AccessibilityService}.
779      *   This class is made immutable before being delivered to an AccessibilityService.
780      * </p>
781      *
782      * @param action The action.
783      *
784      * @throws IllegalStateException If called from an AccessibilityService.
785      */
addAction(int action)786     public void addAction(int action) {
787         enforceNotSealed();
788         mActions |= action;
789     }
790 
791     /**
792      * Sets the movement granularities for traversing the text of this node.
793      * <p>
794      *   <strong>Note:</strong> Cannot be called from an
795      *   {@link android.accessibilityservice.AccessibilityService}.
796      *   This class is made immutable before being delivered to an AccessibilityService.
797      * </p>
798      *
799      * @param granularities The bit mask with granularities.
800      *
801      * @throws IllegalStateException If called from an AccessibilityService.
802      */
setMovementGranularities(int granularities)803     public void setMovementGranularities(int granularities) {
804         enforceNotSealed();
805         mMovementGranularities = granularities;
806     }
807 
808     /**
809      * Gets the movement granularities for traversing the text of this node.
810      *
811      * @return The bit mask with granularities.
812      */
getMovementGranularities()813     public int getMovementGranularities() {
814         return mMovementGranularities;
815     }
816 
817     /**
818      * Performs an action on the node.
819      * <p>
820      *   <strong>Note:</strong> An action can be performed only if the request is made
821      *   from an {@link android.accessibilityservice.AccessibilityService}.
822      * </p>
823      *
824      * @param action The action to perform.
825      * @return True if the action was performed.
826      *
827      * @throws IllegalStateException If called outside of an AccessibilityService.
828      */
performAction(int action)829     public boolean performAction(int action) {
830         enforceSealed();
831         if (!canPerformRequestOverConnection(mSourceNodeId)) {
832             return false;
833         }
834         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
835         return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
836                 action, null);
837     }
838 
839     /**
840      * Performs an action on the node.
841      * <p>
842      *   <strong>Note:</strong> An action can be performed only if the request is made
843      *   from an {@link android.accessibilityservice.AccessibilityService}.
844      * </p>
845      *
846      * @param action The action to perform.
847      * @param arguments A bundle with additional arguments.
848      * @return True if the action was performed.
849      *
850      * @throws IllegalStateException If called outside of an AccessibilityService.
851      */
performAction(int action, Bundle arguments)852     public boolean performAction(int action, Bundle arguments) {
853         enforceSealed();
854         if (!canPerformRequestOverConnection(mSourceNodeId)) {
855             return false;
856         }
857         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
858         return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
859                 action, arguments);
860     }
861 
862     /**
863      * Finds {@link AccessibilityNodeInfo}s by text. The match is case
864      * insensitive containment. The search is relative to this info i.e.
865      * this info is the root of the traversed tree.
866      *
867      * <p>
868      *   <strong>Note:</strong> It is a client responsibility to recycle the
869      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
870      *     to avoid creating of multiple instances.
871      * </p>
872      *
873      * @param text The searched text.
874      * @return A list of node info.
875      */
findAccessibilityNodeInfosByText(String text)876     public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
877         enforceSealed();
878         if (!canPerformRequestOverConnection(mSourceNodeId)) {
879             return Collections.emptyList();
880         }
881         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
882         return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,
883                 text);
884     }
885 
886     /**
887      * Finds {@link AccessibilityNodeInfo}s by the fully qualified view id's resource
888      * name where a fully qualified id is of the from "package:id/id_resource_name".
889      * For example, if the target application's package is "foo.bar" and the id
890      * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz".
891      *
892      * <p>
893      *   <strong>Note:</strong> It is a client responsibility to recycle the
894      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
895      *     to avoid creating of multiple instances.
896      * </p>
897      * <p>
898      *   <strong>Note:</strong> The primary usage of this API is for UI test automation
899      *   and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo}
900      *   the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
901      *   flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
902      * </p>
903      *
904      * @param viewId The fully qualified resource name of the view id to find.
905      * @return A list of node info.
906      */
findAccessibilityNodeInfosByViewId(String viewId)907     public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) {
908         enforceSealed();
909         if (!canPerformRequestOverConnection(mSourceNodeId)) {
910             return Collections.emptyList();
911         }
912         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
913         return client.findAccessibilityNodeInfosByViewId(mConnectionId, mWindowId, mSourceNodeId,
914                 viewId);
915     }
916 
917     /**
918      * Gets the parent.
919      * <p>
920      *   <strong>Note:</strong> It is a client responsibility to recycle the
921      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
922      *     to avoid creating of multiple instances.
923      * </p>
924      *
925      * @return The parent.
926      */
getParent()927     public AccessibilityNodeInfo getParent() {
928         enforceSealed();
929         if (!canPerformRequestOverConnection(mParentNodeId)) {
930             return null;
931         }
932         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
933         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
934                 mWindowId, mParentNodeId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
935     }
936 
937     /**
938      * @return The parent node id.
939      *
940      * @hide
941      */
getParentNodeId()942     public long getParentNodeId() {
943         return mParentNodeId;
944     }
945 
946     /**
947      * Sets the parent.
948      * <p>
949      *   <strong>Note:</strong> Cannot be called from an
950      *   {@link android.accessibilityservice.AccessibilityService}.
951      *   This class is made immutable before being delivered to an AccessibilityService.
952      * </p>
953      *
954      * @param parent The parent.
955      *
956      * @throws IllegalStateException If called from an AccessibilityService.
957      */
setParent(View parent)958     public void setParent(View parent) {
959         setParent(parent, UNDEFINED);
960     }
961 
962     /**
963      * Sets the parent to be a virtual descendant of the given <code>root</code>.
964      * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
965      * is set as the parent.
966      * <p>
967      * A virtual descendant is an imaginary View that is reported as a part of the view
968      * hierarchy for accessibility purposes. This enables custom views that draw complex
969      * content to report them selves as a tree of virtual views, thus conveying their
970      * logical structure.
971      * </p>
972      * <p>
973      *   <strong>Note:</strong> Cannot be called from an
974      *   {@link android.accessibilityservice.AccessibilityService}.
975      *   This class is made immutable before being delivered to an AccessibilityService.
976      * </p>
977      *
978      * @param root The root of the virtual subtree.
979      * @param virtualDescendantId The id of the virtual descendant.
980      */
setParent(View root, int virtualDescendantId)981     public void setParent(View root, int virtualDescendantId) {
982         enforceNotSealed();
983         final int rootAccessibilityViewId =
984             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
985         mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
986     }
987 
988     /**
989      * Gets the node bounds in parent coordinates.
990      *
991      * @param outBounds The output node bounds.
992      */
getBoundsInParent(Rect outBounds)993     public void getBoundsInParent(Rect outBounds) {
994         outBounds.set(mBoundsInParent.left, mBoundsInParent.top,
995                 mBoundsInParent.right, mBoundsInParent.bottom);
996     }
997 
998     /**
999      * Sets the node bounds in parent coordinates.
1000      * <p>
1001      *   <strong>Note:</strong> Cannot be called from an
1002      *   {@link android.accessibilityservice.AccessibilityService}.
1003      *   This class is made immutable before being delivered to an AccessibilityService.
1004      * </p>
1005      *
1006      * @param bounds The node bounds.
1007      *
1008      * @throws IllegalStateException If called from an AccessibilityService.
1009      */
setBoundsInParent(Rect bounds)1010     public void setBoundsInParent(Rect bounds) {
1011         enforceNotSealed();
1012         mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
1013     }
1014 
1015     /**
1016      * Gets the node bounds in screen coordinates.
1017      *
1018      * @param outBounds The output node bounds.
1019      */
getBoundsInScreen(Rect outBounds)1020     public void getBoundsInScreen(Rect outBounds) {
1021         outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top,
1022                 mBoundsInScreen.right, mBoundsInScreen.bottom);
1023     }
1024 
1025     /**
1026      * Sets the node bounds in screen coordinates.
1027      * <p>
1028      *   <strong>Note:</strong> Cannot be called from an
1029      *   {@link android.accessibilityservice.AccessibilityService}.
1030      *   This class is made immutable before being delivered to an AccessibilityService.
1031      * </p>
1032      *
1033      * @param bounds The node bounds.
1034      *
1035      * @throws IllegalStateException If called from an AccessibilityService.
1036      */
setBoundsInScreen(Rect bounds)1037     public void setBoundsInScreen(Rect bounds) {
1038         enforceNotSealed();
1039         mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
1040     }
1041 
1042     /**
1043      * Gets whether this node is checkable.
1044      *
1045      * @return True if the node is checkable.
1046      */
isCheckable()1047     public boolean isCheckable() {
1048         return getBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE);
1049     }
1050 
1051     /**
1052      * Sets whether this node is checkable.
1053      * <p>
1054      *   <strong>Note:</strong> Cannot be called from an
1055      *   {@link android.accessibilityservice.AccessibilityService}.
1056      *   This class is made immutable before being delivered to an AccessibilityService.
1057      * </p>
1058      *
1059      * @param checkable True if the node is checkable.
1060      *
1061      * @throws IllegalStateException If called from an AccessibilityService.
1062      */
setCheckable(boolean checkable)1063     public void setCheckable(boolean checkable) {
1064         setBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE, checkable);
1065     }
1066 
1067     /**
1068      * Gets whether this node is checked.
1069      *
1070      * @return True if the node is checked.
1071      */
isChecked()1072     public boolean isChecked() {
1073         return getBooleanProperty(BOOLEAN_PROPERTY_CHECKED);
1074     }
1075 
1076     /**
1077      * Sets whether this node is checked.
1078      * <p>
1079      *   <strong>Note:</strong> Cannot be called from an
1080      *   {@link android.accessibilityservice.AccessibilityService}.
1081      *   This class is made immutable before being delivered to an AccessibilityService.
1082      * </p>
1083      *
1084      * @param checked True if the node is checked.
1085      *
1086      * @throws IllegalStateException If called from an AccessibilityService.
1087      */
setChecked(boolean checked)1088     public void setChecked(boolean checked) {
1089         setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked);
1090     }
1091 
1092     /**
1093      * Gets whether this node is focusable.
1094      *
1095      * @return True if the node is focusable.
1096      */
isFocusable()1097     public boolean isFocusable() {
1098         return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE);
1099     }
1100 
1101     /**
1102      * Sets whether this node is focusable.
1103      * <p>
1104      *   <strong>Note:</strong> Cannot be called from an
1105      *   {@link android.accessibilityservice.AccessibilityService}.
1106      *   This class is made immutable before being delivered to an AccessibilityService.
1107      * </p>
1108      *
1109      * @param focusable True if the node is focusable.
1110      *
1111      * @throws IllegalStateException If called from an AccessibilityService.
1112      */
setFocusable(boolean focusable)1113     public void setFocusable(boolean focusable) {
1114         setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable);
1115     }
1116 
1117     /**
1118      * Gets whether this node is focused.
1119      *
1120      * @return True if the node is focused.
1121      */
isFocused()1122     public boolean isFocused() {
1123         return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED);
1124     }
1125 
1126     /**
1127      * Sets whether this node is focused.
1128      * <p>
1129      *   <strong>Note:</strong> Cannot be called from an
1130      *   {@link android.accessibilityservice.AccessibilityService}.
1131      *   This class is made immutable before being delivered to an AccessibilityService.
1132      * </p>
1133      *
1134      * @param focused True if the node is focused.
1135      *
1136      * @throws IllegalStateException If called from an AccessibilityService.
1137      */
setFocused(boolean focused)1138     public void setFocused(boolean focused) {
1139         setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused);
1140     }
1141 
1142     /**
1143      * Sets whether this node is visible to the user.
1144      *
1145      * @return Whether the node is visible to the user.
1146      */
isVisibleToUser()1147     public boolean isVisibleToUser() {
1148         return getBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER);
1149     }
1150 
1151     /**
1152      * Sets whether this node is visible to the user.
1153      * <p>
1154      *   <strong>Note:</strong> Cannot be called from an
1155      *   {@link android.accessibilityservice.AccessibilityService}.
1156      *   This class is made immutable before being delivered to an AccessibilityService.
1157      * </p>
1158      *
1159      * @param visibleToUser Whether the node is visible to the user.
1160      *
1161      * @throws IllegalStateException If called from an AccessibilityService.
1162      */
setVisibleToUser(boolean visibleToUser)1163     public void setVisibleToUser(boolean visibleToUser) {
1164         setBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER, visibleToUser);
1165     }
1166 
1167     /**
1168      * Gets whether this node is accessibility focused.
1169      *
1170      * @return True if the node is accessibility focused.
1171      */
isAccessibilityFocused()1172     public boolean isAccessibilityFocused() {
1173         return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED);
1174     }
1175 
1176     /**
1177      * Sets whether this node is accessibility focused.
1178      * <p>
1179      *   <strong>Note:</strong> Cannot be called from an
1180      *   {@link android.accessibilityservice.AccessibilityService}.
1181      *   This class is made immutable before being delivered to an AccessibilityService.
1182      * </p>
1183      *
1184      * @param focused True if the node is accessibility focused.
1185      *
1186      * @throws IllegalStateException If called from an AccessibilityService.
1187      */
setAccessibilityFocused(boolean focused)1188     public void setAccessibilityFocused(boolean focused) {
1189         setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused);
1190     }
1191 
1192     /**
1193      * Gets whether this node is selected.
1194      *
1195      * @return True if the node is selected.
1196      */
isSelected()1197     public boolean isSelected() {
1198         return getBooleanProperty(BOOLEAN_PROPERTY_SELECTED);
1199     }
1200 
1201     /**
1202      * Sets whether this node is selected.
1203      * <p>
1204      *   <strong>Note:</strong> Cannot be called from an
1205      *   {@link android.accessibilityservice.AccessibilityService}.
1206      *   This class is made immutable before being delivered to an AccessibilityService.
1207      * </p>
1208      *
1209      * @param selected True if the node is selected.
1210      *
1211      * @throws IllegalStateException If called from an AccessibilityService.
1212      */
setSelected(boolean selected)1213     public void setSelected(boolean selected) {
1214         setBooleanProperty(BOOLEAN_PROPERTY_SELECTED, selected);
1215     }
1216 
1217     /**
1218      * Gets whether this node is clickable.
1219      *
1220      * @return True if the node is clickable.
1221      */
isClickable()1222     public boolean isClickable() {
1223         return getBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE);
1224     }
1225 
1226     /**
1227      * Sets whether this node is clickable.
1228      * <p>
1229      *   <strong>Note:</strong> Cannot be called from an
1230      *   {@link android.accessibilityservice.AccessibilityService}.
1231      *   This class is made immutable before being delivered to an AccessibilityService.
1232      * </p>
1233      *
1234      * @param clickable True if the node is clickable.
1235      *
1236      * @throws IllegalStateException If called from an AccessibilityService.
1237      */
setClickable(boolean clickable)1238     public void setClickable(boolean clickable) {
1239         setBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE, clickable);
1240     }
1241 
1242     /**
1243      * Gets whether this node is long clickable.
1244      *
1245      * @return True if the node is long clickable.
1246      */
isLongClickable()1247     public boolean isLongClickable() {
1248         return getBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE);
1249     }
1250 
1251     /**
1252      * Sets whether this node is long clickable.
1253      * <p>
1254      *   <strong>Note:</strong> Cannot be called from an
1255      *   {@link android.accessibilityservice.AccessibilityService}.
1256      *   This class is made immutable before being delivered to an AccessibilityService.
1257      * </p>
1258      *
1259      * @param longClickable True if the node is long clickable.
1260      *
1261      * @throws IllegalStateException If called from an AccessibilityService.
1262      */
setLongClickable(boolean longClickable)1263     public void setLongClickable(boolean longClickable) {
1264         setBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE, longClickable);
1265     }
1266 
1267     /**
1268      * Gets whether this node is enabled.
1269      *
1270      * @return True if the node is enabled.
1271      */
isEnabled()1272     public boolean isEnabled() {
1273         return getBooleanProperty(BOOLEAN_PROPERTY_ENABLED);
1274     }
1275 
1276     /**
1277      * Sets whether this node is enabled.
1278      * <p>
1279      *   <strong>Note:</strong> Cannot be called from an
1280      *   {@link android.accessibilityservice.AccessibilityService}.
1281      *   This class is made immutable before being delivered to an AccessibilityService.
1282      * </p>
1283      *
1284      * @param enabled True if the node is enabled.
1285      *
1286      * @throws IllegalStateException If called from an AccessibilityService.
1287      */
setEnabled(boolean enabled)1288     public void setEnabled(boolean enabled) {
1289         setBooleanProperty(BOOLEAN_PROPERTY_ENABLED, enabled);
1290     }
1291 
1292     /**
1293      * Gets whether this node is a password.
1294      *
1295      * @return True if the node is a password.
1296      */
isPassword()1297     public boolean isPassword() {
1298         return getBooleanProperty(BOOLEAN_PROPERTY_PASSWORD);
1299     }
1300 
1301     /**
1302      * Sets whether this node is a password.
1303      * <p>
1304      *   <strong>Note:</strong> Cannot be called from an
1305      *   {@link android.accessibilityservice.AccessibilityService}.
1306      *   This class is made immutable before being delivered to an AccessibilityService.
1307      * </p>
1308      *
1309      * @param password True if the node is a password.
1310      *
1311      * @throws IllegalStateException If called from an AccessibilityService.
1312      */
setPassword(boolean password)1313     public void setPassword(boolean password) {
1314         setBooleanProperty(BOOLEAN_PROPERTY_PASSWORD, password);
1315     }
1316 
1317     /**
1318      * Gets if the node is scrollable.
1319      *
1320      * @return True if the node is scrollable, false otherwise.
1321      */
isScrollable()1322     public boolean isScrollable() {
1323         return getBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE);
1324     }
1325 
1326     /**
1327      * Sets if the node is scrollable.
1328      * <p>
1329      *   <strong>Note:</strong> Cannot be called from an
1330      *   {@link android.accessibilityservice.AccessibilityService}.
1331      *   This class is made immutable before being delivered to an AccessibilityService.
1332      * </p>
1333      *
1334      * @param scrollable True if the node is scrollable, false otherwise.
1335      *
1336      * @throws IllegalStateException If called from an AccessibilityService.
1337      */
setScrollable(boolean scrollable)1338     public void setScrollable(boolean scrollable) {
1339         setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable);
1340     }
1341 
1342     /**
1343      * Gets if the node is editable.
1344      *
1345      * @return True if the node is editable, false otherwise.
1346      */
isEditable()1347     public boolean isEditable() {
1348         return getBooleanProperty(BOOLEAN_PROPERTY_EDITABLE);
1349     }
1350 
1351     /**
1352      * Sets whether this node is editable.
1353      * <p>
1354      *   <strong>Note:</strong> Cannot be called from an
1355      *   {@link android.accessibilityservice.AccessibilityService}.
1356      *   This class is made immutable before being delivered to an AccessibilityService.
1357      * </p>
1358      *
1359      * @param editable True if the node is editable.
1360      *
1361      * @throws IllegalStateException If called from an AccessibilityService.
1362      */
setEditable(boolean editable)1363     public void setEditable(boolean editable) {
1364         setBooleanProperty(BOOLEAN_PROPERTY_EDITABLE, editable);
1365     }
1366 
1367     /**
1368      * Gets the collection info if the node is a collection. A collection
1369      * child is always a collection item.
1370      *
1371      * @return The collection info.
1372      */
getCollectionInfo()1373     public CollectionInfo getCollectionInfo() {
1374         return mCollectionInfo;
1375     }
1376 
1377     /**
1378      * Sets the collection info if the node is a collection. A collection
1379      * child is always a collection item.
1380      * <p>
1381      *   <strong>Note:</strong> Cannot be called from an
1382      *   {@link android.accessibilityservice.AccessibilityService}.
1383      *   This class is made immutable before being delivered to an AccessibilityService.
1384      * </p>
1385      *
1386      * @param collectionInfo The collection info.
1387      */
setCollectionInfo(CollectionInfo collectionInfo)1388     public void setCollectionInfo(CollectionInfo collectionInfo) {
1389         enforceNotSealed();
1390         mCollectionInfo = collectionInfo;
1391     }
1392 
1393     /**
1394      * Gets the collection item info if the node is a collection item. A collection
1395      * item is always a child of a collection.
1396      *
1397      * @return The collection item info.
1398      */
getCollectionItemInfo()1399     public CollectionItemInfo getCollectionItemInfo() {
1400         return mCollectionItemInfo;
1401     }
1402 
1403     /**
1404      * Sets the collection item info if the node is a collection item. A collection
1405      * item is always a child of a collection.
1406      * <p>
1407      *   <strong>Note:</strong> Cannot be called from an
1408      *   {@link android.accessibilityservice.AccessibilityService}.
1409      *   This class is made immutable before being delivered to an AccessibilityService.
1410      * </p>
1411      *
1412      * @return collectionItem True if the node is an item.
1413      */
setCollectionItemInfo(CollectionItemInfo collectionItemInfo)1414     public void setCollectionItemInfo(CollectionItemInfo collectionItemInfo) {
1415         enforceNotSealed();
1416         mCollectionItemInfo = collectionItemInfo;
1417     }
1418 
1419     /**
1420      * Gets the range info if this node is a range.
1421      *
1422      * @return The range.
1423      */
getRangeInfo()1424     public RangeInfo getRangeInfo() {
1425         return mRangeInfo;
1426     }
1427 
1428     /**
1429      * Sets the range info if this node is a range.
1430      * <p>
1431      *   <strong>Note:</strong> Cannot be called from an
1432      *   {@link android.accessibilityservice.AccessibilityService}.
1433      *   This class is made immutable before being delivered to an AccessibilityService.
1434      * </p>
1435      *
1436      * @param rangeInfo The range info.
1437      */
setRangeInfo(RangeInfo rangeInfo)1438     public void setRangeInfo(RangeInfo rangeInfo) {
1439         enforceNotSealed();
1440         mRangeInfo = rangeInfo;
1441     }
1442 
1443     /**
1444      * Gets if the content of this node is invalid. For example,
1445      * a date is not well-formed.
1446      *
1447      * @return If the node content is invalid.
1448      */
isContentInvalid()1449     public boolean isContentInvalid() {
1450         return getBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID);
1451     }
1452 
1453     /**
1454      * Sets if the content of this node is invalid. For example,
1455      * a date is not well-formed.
1456      * <p>
1457      *   <strong>Note:</strong> Cannot be called from an
1458      *   {@link android.accessibilityservice.AccessibilityService}.
1459      *   This class is made immutable before being delivered to an AccessibilityService.
1460      * </p>
1461      *
1462      * @param contentInvalid If the node content is invalid.
1463      */
setContentInvalid(boolean contentInvalid)1464     public void setContentInvalid(boolean contentInvalid) {
1465         setBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID, contentInvalid);
1466     }
1467 
1468     /**
1469      * Gets the node's live region mode.
1470      * <p>
1471      * A live region is a node that contains information that is important for
1472      * the user and when it changes the user should be notified. For example,
1473      * in a login screen with a TextView that displays an "incorrect password"
1474      * notification, that view should be marked as a live region with mode
1475      * {@link View#ACCESSIBILITY_LIVE_REGION_POLITE}.
1476      * <p>
1477      * It is the responsibility of the accessibility service to monitor
1478      * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events indicating
1479      * changes to live region nodes and their children.
1480      *
1481      * @return The live region mode, or
1482      *         {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a
1483      *         live region.
1484      * @see android.view.View#getAccessibilityLiveRegion()
1485      */
getLiveRegion()1486     public int getLiveRegion() {
1487         return mLiveRegion;
1488     }
1489 
1490     /**
1491      * Sets the node's live region mode.
1492      * <p>
1493      * <strong>Note:</strong> Cannot be called from an
1494      * {@link android.accessibilityservice.AccessibilityService}. This class is
1495      * made immutable before being delivered to an AccessibilityService.
1496      *
1497      * @param mode The live region mode, or
1498      *        {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a
1499      *        live region.
1500      * @see android.view.View#setAccessibilityLiveRegion(int)
1501      */
setLiveRegion(int mode)1502     public void setLiveRegion(int mode) {
1503         enforceNotSealed();
1504         mLiveRegion = mode;
1505     }
1506 
1507     /**
1508      * Gets if the node is a multi line editable text.
1509      *
1510      * @return True if the node is multi line.
1511      */
isMultiLine()1512     public boolean isMultiLine() {
1513         return getBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE);
1514     }
1515 
1516     /**
1517      * Sets if the node is a multi line editable text.
1518      * <p>
1519      *   <strong>Note:</strong> Cannot be called from an
1520      *   {@link android.accessibilityservice.AccessibilityService}.
1521      *   This class is made immutable before being delivered to an AccessibilityService.
1522      * </p>
1523      *
1524      * @param multiLine True if the node is multi line.
1525      */
setMultiLine(boolean multiLine)1526     public void setMultiLine(boolean multiLine) {
1527         setBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE, multiLine);
1528     }
1529 
1530     /**
1531      * Gets if this node opens a popup or a dialog.
1532      *
1533      * @return If the the node opens a popup.
1534      */
canOpenPopup()1535     public boolean canOpenPopup() {
1536         return getBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP);
1537     }
1538 
1539     /**
1540      * Sets if this node opens a popup or a dialog.
1541      * <p>
1542      *   <strong>Note:</strong> Cannot be called from an
1543      *   {@link android.accessibilityservice.AccessibilityService}.
1544      *   This class is made immutable before being delivered to an AccessibilityService.
1545      * </p>
1546      *
1547      * @param opensPopup If the the node opens a popup.
1548      */
setCanOpenPopup(boolean opensPopup)1549     public void setCanOpenPopup(boolean opensPopup) {
1550         enforceNotSealed();
1551         setBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP, opensPopup);
1552     }
1553 
1554     /**
1555      * Gets if the node can be dismissed.
1556      *
1557      * @return If the node can be dismissed.
1558      */
isDismissable()1559     public boolean isDismissable() {
1560         return getBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE);
1561     }
1562 
1563     /**
1564      * Sets if the node can be dismissed.
1565      * <p>
1566      *   <strong>Note:</strong> Cannot be called from an
1567      *   {@link android.accessibilityservice.AccessibilityService}.
1568      *   This class is made immutable before being delivered to an AccessibilityService.
1569      * </p>
1570      *
1571      * @param dismissable If the node can be dismissed.
1572      */
setDismissable(boolean dismissable)1573     public void setDismissable(boolean dismissable) {
1574         setBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE, dismissable);
1575     }
1576 
1577     /**
1578      * Gets the package this node comes from.
1579      *
1580      * @return The package name.
1581      */
getPackageName()1582     public CharSequence getPackageName() {
1583         return mPackageName;
1584     }
1585 
1586     /**
1587      * Sets the package this node comes from.
1588      * <p>
1589      *   <strong>Note:</strong> Cannot be called from an
1590      *   {@link android.accessibilityservice.AccessibilityService}.
1591      *   This class is made immutable before being delivered to an AccessibilityService.
1592      * </p>
1593      *
1594      * @param packageName The package name.
1595      *
1596      * @throws IllegalStateException If called from an AccessibilityService.
1597      */
setPackageName(CharSequence packageName)1598     public void setPackageName(CharSequence packageName) {
1599         enforceNotSealed();
1600         mPackageName = packageName;
1601     }
1602 
1603     /**
1604      * Gets the class this node comes from.
1605      *
1606      * @return The class name.
1607      */
getClassName()1608     public CharSequence getClassName() {
1609         return mClassName;
1610     }
1611 
1612     /**
1613      * Sets the class this node comes from.
1614      * <p>
1615      *   <strong>Note:</strong> Cannot be called from an
1616      *   {@link android.accessibilityservice.AccessibilityService}.
1617      *   This class is made immutable before being delivered to an AccessibilityService.
1618      * </p>
1619      *
1620      * @param className The class name.
1621      *
1622      * @throws IllegalStateException If called from an AccessibilityService.
1623      */
setClassName(CharSequence className)1624     public void setClassName(CharSequence className) {
1625         enforceNotSealed();
1626         mClassName = className;
1627     }
1628 
1629     /**
1630      * Gets the text of this node.
1631      *
1632      * @return The text.
1633      */
getText()1634     public CharSequence getText() {
1635         return mText;
1636     }
1637 
1638     /**
1639      * Sets the text of this node.
1640      * <p>
1641      *   <strong>Note:</strong> Cannot be called from an
1642      *   {@link android.accessibilityservice.AccessibilityService}.
1643      *   This class is made immutable before being delivered to an AccessibilityService.
1644      * </p>
1645      *
1646      * @param text The text.
1647      *
1648      * @throws IllegalStateException If called from an AccessibilityService.
1649      */
setText(CharSequence text)1650     public void setText(CharSequence text) {
1651         enforceNotSealed();
1652         mText = text;
1653     }
1654 
1655     /**
1656      * Gets the content description of this node.
1657      *
1658      * @return The content description.
1659      */
getContentDescription()1660     public CharSequence getContentDescription() {
1661         return mContentDescription;
1662     }
1663 
1664     /**
1665      * Sets the content description of this node.
1666      * <p>
1667      *   <strong>Note:</strong> Cannot be called from an
1668      *   {@link android.accessibilityservice.AccessibilityService}.
1669      *   This class is made immutable before being delivered to an AccessibilityService.
1670      * </p>
1671      *
1672      * @param contentDescription The content description.
1673      *
1674      * @throws IllegalStateException If called from an AccessibilityService.
1675      */
setContentDescription(CharSequence contentDescription)1676     public void setContentDescription(CharSequence contentDescription) {
1677         enforceNotSealed();
1678         mContentDescription = contentDescription;
1679     }
1680 
1681     /**
1682      * Sets the view for which the view represented by this info serves as a
1683      * label for accessibility purposes.
1684      *
1685      * @param labeled The view for which this info serves as a label.
1686      */
setLabelFor(View labeled)1687     public void setLabelFor(View labeled) {
1688         setLabelFor(labeled, UNDEFINED);
1689     }
1690 
1691     /**
1692      * Sets the view for which the view represented by this info serves as a
1693      * label for accessibility purposes. If <code>virtualDescendantId</code>
1694      * is {@link View#NO_ID} the root is set as the labeled.
1695      * <p>
1696      * A virtual descendant is an imaginary View that is reported as a part of the view
1697      * hierarchy for accessibility purposes. This enables custom views that draw complex
1698      * content to report themselves as a tree of virtual views, thus conveying their
1699      * logical structure.
1700      * </p>
1701      * <p>
1702      *   <strong>Note:</strong> Cannot be called from an
1703      *   {@link android.accessibilityservice.AccessibilityService}.
1704      *   This class is made immutable before being delivered to an AccessibilityService.
1705      * </p>
1706      *
1707      * @param root The root whose virtual descendant serves as a label.
1708      * @param virtualDescendantId The id of the virtual descendant.
1709      */
setLabelFor(View root, int virtualDescendantId)1710     public void setLabelFor(View root, int virtualDescendantId) {
1711         enforceNotSealed();
1712         final int rootAccessibilityViewId = (root != null)
1713                 ? root.getAccessibilityViewId() : UNDEFINED;
1714         mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
1715     }
1716 
1717     /**
1718      * Gets the node info for which the view represented by this info serves as
1719      * a label for accessibility purposes.
1720      * <p>
1721      *   <strong>Note:</strong> It is a client responsibility to recycle the
1722      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
1723      *     to avoid creating of multiple instances.
1724      * </p>
1725      *
1726      * @return The labeled info.
1727      */
getLabelFor()1728     public AccessibilityNodeInfo getLabelFor() {
1729         enforceSealed();
1730         if (!canPerformRequestOverConnection(mLabelForId)) {
1731             return null;
1732         }
1733         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
1734         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
1735                 mWindowId, mLabelForId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
1736     }
1737 
1738     /**
1739      * Sets the view which serves as the label of the view represented by
1740      * this info for accessibility purposes.
1741      *
1742      * @param label The view that labels this node's source.
1743      */
setLabeledBy(View label)1744     public void setLabeledBy(View label) {
1745         setLabeledBy(label, UNDEFINED);
1746     }
1747 
1748     /**
1749      * Sets the view which serves as the label of the view represented by
1750      * this info for accessibility purposes. If <code>virtualDescendantId</code>
1751      * is {@link View#NO_ID} the root is set as the label.
1752      * <p>
1753      * A virtual descendant is an imaginary View that is reported as a part of the view
1754      * hierarchy for accessibility purposes. This enables custom views that draw complex
1755      * content to report themselves as a tree of virtual views, thus conveying their
1756      * logical structure.
1757      * </p>
1758      * <p>
1759      *   <strong>Note:</strong> Cannot be called from an
1760      *   {@link android.accessibilityservice.AccessibilityService}.
1761      *   This class is made immutable before being delivered to an AccessibilityService.
1762      * </p>
1763      *
1764      * @param root The root whose virtual descendant labels this node's source.
1765      * @param virtualDescendantId The id of the virtual descendant.
1766      */
setLabeledBy(View root, int virtualDescendantId)1767     public void setLabeledBy(View root, int virtualDescendantId) {
1768         enforceNotSealed();
1769         final int rootAccessibilityViewId = (root != null)
1770                 ? root.getAccessibilityViewId() : UNDEFINED;
1771         mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
1772     }
1773 
1774     /**
1775      * Gets the node info which serves as the label of the view represented by
1776      * this info for accessibility purposes.
1777      * <p>
1778      *   <strong>Note:</strong> It is a client responsibility to recycle the
1779      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
1780      *     to avoid creating of multiple instances.
1781      * </p>
1782      *
1783      * @return The label.
1784      */
getLabeledBy()1785     public AccessibilityNodeInfo getLabeledBy() {
1786         enforceSealed();
1787         if (!canPerformRequestOverConnection(mLabeledById)) {
1788             return null;
1789         }
1790         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
1791         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
1792                 mWindowId, mLabeledById, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
1793     }
1794 
1795     /**
1796      * Sets the fully qualified resource name of the source view's id.
1797      *
1798      * <p>
1799      *   <strong>Note:</strong> Cannot be called from an
1800      *   {@link android.accessibilityservice.AccessibilityService}.
1801      *   This class is made immutable before being delivered to an AccessibilityService.
1802      * </p>
1803      *
1804      * @param viewIdResName The id resource name.
1805      */
setViewIdResourceName(String viewIdResName)1806     public void setViewIdResourceName(String viewIdResName) {
1807         enforceNotSealed();
1808         mViewIdResourceName = viewIdResName;
1809     }
1810 
1811     /**
1812      * Gets the fully qualified resource name of the source view's id.
1813      *
1814      * <p>
1815      *   <strong>Note:</strong> The primary usage of this API is for UI test automation
1816      *   and in order to report the source view id of an {@link AccessibilityNodeInfo} the
1817      *   client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
1818      *   flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
1819      * </p>
1820 
1821      * @return The id resource name.
1822      */
getViewIdResourceName()1823     public String getViewIdResourceName() {
1824         return mViewIdResourceName;
1825     }
1826 
1827     /**
1828      * Gets the text selection start.
1829      *
1830      * @return The text selection start if there is selection or -1.
1831      */
getTextSelectionStart()1832     public int getTextSelectionStart() {
1833         return mTextSelectionStart;
1834     }
1835 
1836     /**
1837      * Gets the text selection end.
1838      *
1839      * @return The text selection end if there is selection or -1.
1840      */
getTextSelectionEnd()1841     public int getTextSelectionEnd() {
1842         return mTextSelectionEnd;
1843     }
1844 
1845     /**
1846      * Sets the text selection start and end.
1847      * <p>
1848      *   <strong>Note:</strong> Cannot be called from an
1849      *   {@link android.accessibilityservice.AccessibilityService}.
1850      *   This class is made immutable before being delivered to an AccessibilityService.
1851      * </p>
1852      *
1853      * @param start The text selection start.
1854      * @param end The text selection end.
1855      *
1856      * @throws IllegalStateException If called from an AccessibilityService.
1857      */
setTextSelection(int start, int end)1858     public void setTextSelection(int start, int end) {
1859         enforceNotSealed();
1860         mTextSelectionStart = start;
1861         mTextSelectionEnd = end;
1862     }
1863 
1864     /**
1865      * Gets the input type of the source as defined by {@link InputType}.
1866      *
1867      * @return The input type.
1868      */
getInputType()1869     public int getInputType() {
1870         return mInputType;
1871     }
1872 
1873     /**
1874      * Sets the input type of the source as defined by {@link InputType}.
1875      * <p>
1876      *   <strong>Note:</strong> Cannot be called from an
1877      *   {@link android.accessibilityservice.AccessibilityService}.
1878      *   This class is made immutable before being delivered to an
1879      *   AccessibilityService.
1880      * </p>
1881      *
1882      * @param inputType The input type.
1883      *
1884      * @throws IllegalStateException If called from an AccessibilityService.
1885      */
setInputType(int inputType)1886     public void setInputType(int inputType) {
1887         enforceNotSealed();
1888         mInputType = inputType;
1889     }
1890 
1891     /**
1892      * Gets an optional bundle with extra data. The bundle
1893      * is lazily created and never <code>null</code>.
1894      * <p>
1895      * <strong>Note:</strong> It is recommended to use the package
1896      * name of your application as a prefix for the keys to avoid
1897      * collisions which may confuse an accessibility service if the
1898      * same key has different meaning when emitted from different
1899      * applications.
1900      * </p>
1901      *
1902      * @return The bundle.
1903      */
getExtras()1904     public Bundle getExtras() {
1905         if (mExtras == null) {
1906             mExtras = new Bundle();
1907         }
1908         return mExtras;
1909     }
1910 
1911     /**
1912      * Gets the value of a boolean property.
1913      *
1914      * @param property The property.
1915      * @return The value.
1916      */
getBooleanProperty(int property)1917     private boolean getBooleanProperty(int property) {
1918         return (mBooleanProperties & property) != 0;
1919     }
1920 
1921     /**
1922      * Sets a boolean property.
1923      *
1924      * @param property The property.
1925      * @param value The value.
1926      *
1927      * @throws IllegalStateException If called from an AccessibilityService.
1928      */
setBooleanProperty(int property, boolean value)1929     private void setBooleanProperty(int property, boolean value) {
1930         enforceNotSealed();
1931         if (value) {
1932             mBooleanProperties |= property;
1933         } else {
1934             mBooleanProperties &= ~property;
1935         }
1936     }
1937 
1938     /**
1939      * Sets the unique id of the IAccessibilityServiceConnection over which
1940      * this instance can send requests to the system.
1941      *
1942      * @param connectionId The connection id.
1943      *
1944      * @hide
1945      */
setConnectionId(int connectionId)1946     public void setConnectionId(int connectionId) {
1947         enforceNotSealed();
1948         mConnectionId = connectionId;
1949     }
1950 
1951     /**
1952      * {@inheritDoc}
1953      */
describeContents()1954     public int describeContents() {
1955         return 0;
1956     }
1957 
1958     /**
1959      * Gets the id of the source node.
1960      *
1961      * @return The id.
1962      *
1963      * @hide
1964      */
getSourceNodeId()1965     public long getSourceNodeId() {
1966         return mSourceNodeId;
1967     }
1968 
1969     /**
1970      * Sets if this instance is sealed.
1971      *
1972      * @param sealed Whether is sealed.
1973      *
1974      * @hide
1975      */
setSealed(boolean sealed)1976     public void setSealed(boolean sealed) {
1977         mSealed = sealed;
1978     }
1979 
1980     /**
1981      * Gets if this instance is sealed.
1982      *
1983      * @return Whether is sealed.
1984      *
1985      * @hide
1986      */
isSealed()1987     public boolean isSealed() {
1988         return mSealed;
1989     }
1990 
1991     /**
1992      * Enforces that this instance is sealed.
1993      *
1994      * @throws IllegalStateException If this instance is not sealed.
1995      *
1996      * @hide
1997      */
enforceSealed()1998     protected void enforceSealed() {
1999         if (!isSealed()) {
2000             throw new IllegalStateException("Cannot perform this "
2001                     + "action on a not sealed instance.");
2002         }
2003     }
2004 
enforceValidFocusDirection(int direction)2005     private void enforceValidFocusDirection(int direction) {
2006         switch (direction) {
2007             case View.FOCUS_DOWN:
2008             case View.FOCUS_UP:
2009             case View.FOCUS_LEFT:
2010             case View.FOCUS_RIGHT:
2011             case View.FOCUS_FORWARD:
2012             case View.FOCUS_BACKWARD:
2013                 return;
2014             default:
2015                 throw new IllegalArgumentException("Unknown direction: " + direction);
2016         }
2017     }
2018 
enforceValidFocusType(int focusType)2019     private void enforceValidFocusType(int focusType) {
2020         switch (focusType) {
2021             case FOCUS_INPUT:
2022             case FOCUS_ACCESSIBILITY:
2023                 return;
2024             default:
2025                 throw new IllegalArgumentException("Unknown focus type: " + focusType);
2026         }
2027     }
2028 
2029     /**
2030      * Enforces that this instance is not sealed.
2031      *
2032      * @throws IllegalStateException If this instance is sealed.
2033      *
2034      * @hide
2035      */
enforceNotSealed()2036     protected void enforceNotSealed() {
2037         if (isSealed()) {
2038             throw new IllegalStateException("Cannot perform this "
2039                     + "action on a sealed instance.");
2040         }
2041     }
2042 
2043     /**
2044      * Returns a cached instance if such is available otherwise a new one
2045      * and sets the source.
2046      *
2047      * @param source The source view.
2048      * @return An instance.
2049      *
2050      * @see #setSource(View)
2051      */
obtain(View source)2052     public static AccessibilityNodeInfo obtain(View source) {
2053         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
2054         info.setSource(source);
2055         return info;
2056     }
2057 
2058     /**
2059      * Returns a cached instance if such is available otherwise a new one
2060      * and sets the source.
2061      *
2062      * @param root The root of the virtual subtree.
2063      * @param virtualDescendantId The id of the virtual descendant.
2064      * @return An instance.
2065      *
2066      * @see #setSource(View, int)
2067      */
obtain(View root, int virtualDescendantId)2068     public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
2069         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
2070         info.setSource(root, virtualDescendantId);
2071         return info;
2072     }
2073 
2074     /**
2075      * Returns a cached instance if such is available otherwise a new one.
2076      *
2077      * @return An instance.
2078      */
obtain()2079     public static AccessibilityNodeInfo obtain() {
2080         AccessibilityNodeInfo info = sPool.acquire();
2081         return (info != null) ? info : new AccessibilityNodeInfo();
2082     }
2083 
2084     /**
2085      * Returns a cached instance if such is available or a new one is
2086      * create. The returned instance is initialized from the given
2087      * <code>info</code>.
2088      *
2089      * @param info The other info.
2090      * @return An instance.
2091      */
obtain(AccessibilityNodeInfo info)2092     public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
2093         AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain();
2094         infoClone.init(info);
2095         return infoClone;
2096     }
2097 
2098     /**
2099      * Return an instance back to be reused.
2100      * <p>
2101      * <strong>Note:</strong> You must not touch the object after calling this function.
2102      *
2103      * @throws IllegalStateException If the info is already recycled.
2104      */
recycle()2105     public void recycle() {
2106         clear();
2107         sPool.release(this);
2108     }
2109 
2110     /**
2111      * {@inheritDoc}
2112      * <p>
2113      *   <strong>Note:</strong> After the instance is written to a parcel it
2114      *      is recycled. You must not touch the object after calling this function.
2115      * </p>
2116      */
writeToParcel(Parcel parcel, int flags)2117     public void writeToParcel(Parcel parcel, int flags) {
2118         parcel.writeInt(isSealed() ? 1 : 0);
2119         parcel.writeLong(mSourceNodeId);
2120         parcel.writeInt(mWindowId);
2121         parcel.writeLong(mParentNodeId);
2122         parcel.writeLong(mLabelForId);
2123         parcel.writeLong(mLabeledById);
2124         parcel.writeInt(mConnectionId);
2125 
2126         SparseLongArray childIds = mChildNodeIds;
2127         final int childIdsSize = childIds.size();
2128         parcel.writeInt(childIdsSize);
2129         for (int i = 0; i < childIdsSize; i++) {
2130             parcel.writeLong(childIds.valueAt(i));
2131         }
2132 
2133         parcel.writeInt(mBoundsInParent.top);
2134         parcel.writeInt(mBoundsInParent.bottom);
2135         parcel.writeInt(mBoundsInParent.left);
2136         parcel.writeInt(mBoundsInParent.right);
2137 
2138         parcel.writeInt(mBoundsInScreen.top);
2139         parcel.writeInt(mBoundsInScreen.bottom);
2140         parcel.writeInt(mBoundsInScreen.left);
2141         parcel.writeInt(mBoundsInScreen.right);
2142 
2143         parcel.writeInt(mActions);
2144 
2145         parcel.writeInt(mMovementGranularities);
2146 
2147         parcel.writeInt(mBooleanProperties);
2148 
2149         parcel.writeCharSequence(mPackageName);
2150         parcel.writeCharSequence(mClassName);
2151         parcel.writeCharSequence(mText);
2152         parcel.writeCharSequence(mContentDescription);
2153         parcel.writeString(mViewIdResourceName);
2154 
2155         parcel.writeInt(mTextSelectionStart);
2156         parcel.writeInt(mTextSelectionEnd);
2157         parcel.writeInt(mInputType);
2158         parcel.writeInt(mLiveRegion);
2159 
2160         if (mExtras != null) {
2161             parcel.writeInt(1);
2162             parcel.writeBundle(mExtras);
2163         } else {
2164             parcel.writeInt(0);
2165         }
2166 
2167         if (mRangeInfo != null) {
2168             parcel.writeInt(1);
2169             parcel.writeInt(mRangeInfo.getType());
2170             parcel.writeFloat(mRangeInfo.getMin());
2171             parcel.writeFloat(mRangeInfo.getMax());
2172             parcel.writeFloat(mRangeInfo.getCurrent());
2173         } else {
2174             parcel.writeInt(0);
2175         }
2176 
2177         if (mCollectionInfo != null) {
2178             parcel.writeInt(1);
2179             parcel.writeInt(mCollectionInfo.getRowCount());
2180             parcel.writeInt(mCollectionInfo.getColumnCount());
2181             parcel.writeInt(mCollectionInfo.isHierarchical() ? 1 : 0);
2182         } else {
2183             parcel.writeInt(0);
2184         }
2185 
2186         if (mCollectionItemInfo != null) {
2187             parcel.writeInt(1);
2188             parcel.writeInt(mCollectionItemInfo.getColumnIndex());
2189             parcel.writeInt(mCollectionItemInfo.getColumnSpan());
2190             parcel.writeInt(mCollectionItemInfo.getRowIndex());
2191             parcel.writeInt(mCollectionItemInfo.getRowSpan());
2192             parcel.writeInt(mCollectionItemInfo.isHeading() ? 1 : 0);
2193         } else {
2194             parcel.writeInt(0);
2195         }
2196 
2197         // Since instances of this class are fetched via synchronous i.e. blocking
2198         // calls in IPCs we always recycle as soon as the instance is marshaled.
2199         recycle();
2200     }
2201 
2202     /**
2203      * Initializes this instance from another one.
2204      *
2205      * @param other The other instance.
2206      */
init(AccessibilityNodeInfo other)2207     private void init(AccessibilityNodeInfo other) {
2208         mSealed = other.mSealed;
2209         mSourceNodeId = other.mSourceNodeId;
2210         mParentNodeId = other.mParentNodeId;
2211         mLabelForId = other.mLabelForId;
2212         mLabeledById = other.mLabeledById;
2213         mWindowId = other.mWindowId;
2214         mConnectionId = other.mConnectionId;
2215         mBoundsInParent.set(other.mBoundsInParent);
2216         mBoundsInScreen.set(other.mBoundsInScreen);
2217         mPackageName = other.mPackageName;
2218         mClassName = other.mClassName;
2219         mText = other.mText;
2220         mContentDescription = other.mContentDescription;
2221         mViewIdResourceName = other.mViewIdResourceName;
2222         mActions= other.mActions;
2223         mBooleanProperties = other.mBooleanProperties;
2224         mMovementGranularities = other.mMovementGranularities;
2225         final int otherChildIdCount = other.mChildNodeIds.size();
2226         for (int i = 0; i < otherChildIdCount; i++) {
2227             mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i));
2228         }
2229         mTextSelectionStart = other.mTextSelectionStart;
2230         mTextSelectionEnd = other.mTextSelectionEnd;
2231         mInputType = other.mInputType;
2232         mLiveRegion = other.mLiveRegion;
2233         if (other.mExtras != null && !other.mExtras.isEmpty()) {
2234             getExtras().putAll(other.mExtras);
2235         }
2236         mRangeInfo = (other.mRangeInfo != null)
2237                 ? RangeInfo.obtain(other.mRangeInfo) : null;
2238         mCollectionInfo = (other.mCollectionInfo != null)
2239                 ? CollectionInfo.obtain(other.mCollectionInfo) : null;
2240         mCollectionItemInfo =  (other.mCollectionItemInfo != null)
2241                 ? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null;
2242     }
2243 
2244     /**
2245      * Creates a new instance from a {@link Parcel}.
2246      *
2247      * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
2248      */
initFromParcel(Parcel parcel)2249     private void initFromParcel(Parcel parcel) {
2250         mSealed = (parcel.readInt()  == 1);
2251         mSourceNodeId = parcel.readLong();
2252         mWindowId = parcel.readInt();
2253         mParentNodeId = parcel.readLong();
2254         mLabelForId = parcel.readLong();
2255         mLabeledById = parcel.readLong();
2256         mConnectionId = parcel.readInt();
2257 
2258         SparseLongArray childIds = mChildNodeIds;
2259         final int childrenSize = parcel.readInt();
2260         for (int i = 0; i < childrenSize; i++) {
2261             final long childId = parcel.readLong();
2262             childIds.put(i, childId);
2263         }
2264 
2265         mBoundsInParent.top = parcel.readInt();
2266         mBoundsInParent.bottom = parcel.readInt();
2267         mBoundsInParent.left = parcel.readInt();
2268         mBoundsInParent.right = parcel.readInt();
2269 
2270         mBoundsInScreen.top = parcel.readInt();
2271         mBoundsInScreen.bottom = parcel.readInt();
2272         mBoundsInScreen.left = parcel.readInt();
2273         mBoundsInScreen.right = parcel.readInt();
2274 
2275         mActions = parcel.readInt();
2276 
2277         mMovementGranularities = parcel.readInt();
2278 
2279         mBooleanProperties = parcel.readInt();
2280 
2281         mPackageName = parcel.readCharSequence();
2282         mClassName = parcel.readCharSequence();
2283         mText = parcel.readCharSequence();
2284         mContentDescription = parcel.readCharSequence();
2285         mViewIdResourceName = parcel.readString();
2286 
2287         mTextSelectionStart = parcel.readInt();
2288         mTextSelectionEnd = parcel.readInt();
2289 
2290         mInputType = parcel.readInt();
2291         mLiveRegion = parcel.readInt();
2292 
2293         if (parcel.readInt() == 1) {
2294             getExtras().putAll(parcel.readBundle());
2295         }
2296 
2297         if (parcel.readInt() == 1) {
2298             mRangeInfo = RangeInfo.obtain(
2299                     parcel.readInt(),
2300                     parcel.readFloat(),
2301                     parcel.readFloat(),
2302                     parcel.readFloat());
2303         }
2304 
2305         if (parcel.readInt() == 1) {
2306             mCollectionInfo = CollectionInfo.obtain(
2307                     parcel.readInt(),
2308                     parcel.readInt(),
2309                     parcel.readInt() == 1);
2310         }
2311 
2312         if (parcel.readInt() == 1) {
2313             mCollectionItemInfo = CollectionItemInfo.obtain(
2314                     parcel.readInt(),
2315                     parcel.readInt(),
2316                     parcel.readInt(),
2317                     parcel.readInt(),
2318                     parcel.readInt() == 1);
2319         }
2320     }
2321 
2322     /**
2323      * Clears the state of this instance.
2324      */
clear()2325     private void clear() {
2326         mSealed = false;
2327         mSourceNodeId = ROOT_NODE_ID;
2328         mParentNodeId = ROOT_NODE_ID;
2329         mLabelForId = ROOT_NODE_ID;
2330         mLabeledById = ROOT_NODE_ID;
2331         mWindowId = UNDEFINED;
2332         mConnectionId = UNDEFINED;
2333         mMovementGranularities = 0;
2334         mChildNodeIds.clear();
2335         mBoundsInParent.set(0, 0, 0, 0);
2336         mBoundsInScreen.set(0, 0, 0, 0);
2337         mBooleanProperties = 0;
2338         mPackageName = null;
2339         mClassName = null;
2340         mText = null;
2341         mContentDescription = null;
2342         mViewIdResourceName = null;
2343         mActions = 0;
2344         mTextSelectionStart = UNDEFINED;
2345         mTextSelectionEnd = UNDEFINED;
2346         mInputType = InputType.TYPE_NULL;
2347         mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
2348         if (mExtras != null) {
2349             mExtras.clear();
2350         }
2351         if (mRangeInfo != null) {
2352             mRangeInfo.recycle();
2353             mRangeInfo = null;
2354         }
2355         if (mCollectionInfo != null) {
2356             mCollectionInfo.recycle();
2357             mCollectionInfo = null;
2358         }
2359         if (mCollectionItemInfo != null) {
2360             mCollectionItemInfo.recycle();
2361             mCollectionItemInfo = null;
2362         }
2363     }
2364 
2365     /**
2366      * Gets the human readable action symbolic name.
2367      *
2368      * @param action The action.
2369      * @return The symbolic name.
2370      */
getActionSymbolicName(int action)2371     private static String getActionSymbolicName(int action) {
2372         switch (action) {
2373             case ACTION_FOCUS:
2374                 return "ACTION_FOCUS";
2375             case ACTION_CLEAR_FOCUS:
2376                 return "ACTION_CLEAR_FOCUS";
2377             case ACTION_SELECT:
2378                 return "ACTION_SELECT";
2379             case ACTION_CLEAR_SELECTION:
2380                 return "ACTION_CLEAR_SELECTION";
2381             case ACTION_CLICK:
2382                 return "ACTION_CLICK";
2383             case ACTION_LONG_CLICK:
2384                 return "ACTION_LONG_CLICK";
2385             case ACTION_ACCESSIBILITY_FOCUS:
2386                 return "ACTION_ACCESSIBILITY_FOCUS";
2387             case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
2388                 return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
2389             case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
2390                 return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
2391             case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
2392                 return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
2393             case ACTION_NEXT_HTML_ELEMENT:
2394                 return "ACTION_NEXT_HTML_ELEMENT";
2395             case ACTION_PREVIOUS_HTML_ELEMENT:
2396                 return "ACTION_PREVIOUS_HTML_ELEMENT";
2397             case ACTION_SCROLL_FORWARD:
2398                 return "ACTION_SCROLL_FORWARD";
2399             case ACTION_SCROLL_BACKWARD:
2400                 return "ACTION_SCROLL_BACKWARD";
2401             case ACTION_CUT:
2402                 return "ACTION_CUT";
2403             case ACTION_COPY:
2404                 return "ACTION_COPY";
2405             case ACTION_PASTE:
2406                 return "ACTION_PASTE";
2407             case ACTION_SET_SELECTION:
2408                 return "ACTION_SET_SELECTION";
2409             default:
2410                 return"ACTION_UNKNOWN";
2411         }
2412     }
2413 
2414     /**
2415      * Gets the human readable movement granularity symbolic name.
2416      *
2417      * @param granularity The granularity.
2418      * @return The symbolic name.
2419      */
getMovementGranularitySymbolicName(int granularity)2420     private static String getMovementGranularitySymbolicName(int granularity) {
2421         switch (granularity) {
2422             case MOVEMENT_GRANULARITY_CHARACTER:
2423                 return "MOVEMENT_GRANULARITY_CHARACTER";
2424             case MOVEMENT_GRANULARITY_WORD:
2425                 return "MOVEMENT_GRANULARITY_WORD";
2426             case MOVEMENT_GRANULARITY_LINE:
2427                 return "MOVEMENT_GRANULARITY_LINE";
2428             case MOVEMENT_GRANULARITY_PARAGRAPH:
2429                 return "MOVEMENT_GRANULARITY_PARAGRAPH";
2430             case MOVEMENT_GRANULARITY_PAGE:
2431                 return "MOVEMENT_GRANULARITY_PAGE";
2432             default:
2433                 throw new IllegalArgumentException("Unknown movement granularity: " + granularity);
2434         }
2435     }
2436 
canPerformRequestOverConnection(long accessibilityNodeId)2437     private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
2438         return (mWindowId != UNDEFINED
2439                 && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED
2440                 && mConnectionId != UNDEFINED);
2441     }
2442 
2443     @Override
equals(Object object)2444     public boolean equals(Object object) {
2445         if (this == object) {
2446             return true;
2447         }
2448         if (object == null) {
2449             return false;
2450         }
2451         if (getClass() != object.getClass()) {
2452             return false;
2453         }
2454         AccessibilityNodeInfo other = (AccessibilityNodeInfo) object;
2455         if (mSourceNodeId != other.mSourceNodeId) {
2456             return false;
2457         }
2458         if (mWindowId != other.mWindowId) {
2459             return false;
2460         }
2461         return true;
2462     }
2463 
2464     @Override
hashCode()2465     public int hashCode() {
2466         final int prime = 31;
2467         int result = 1;
2468         result = prime * result + getAccessibilityViewId(mSourceNodeId);
2469         result = prime * result + getVirtualDescendantId(mSourceNodeId);
2470         result = prime * result + mWindowId;
2471         return result;
2472     }
2473 
2474     @Override
toString()2475     public String toString() {
2476         StringBuilder builder = new StringBuilder();
2477         builder.append(super.toString());
2478 
2479         if (DEBUG) {
2480             builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
2481             builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
2482             builder.append("; mParentNodeId: " + mParentNodeId);
2483 
2484             int granularities = mMovementGranularities;
2485             builder.append("; MovementGranularities: [");
2486             while (granularities != 0) {
2487                 final int granularity = 1 << Integer.numberOfTrailingZeros(granularities);
2488                 granularities &= ~granularity;
2489                 builder.append(getMovementGranularitySymbolicName(granularity));
2490                 if (granularities != 0) {
2491                     builder.append(", ");
2492                 }
2493             }
2494             builder.append("]");
2495 
2496             SparseLongArray childIds = mChildNodeIds;
2497             builder.append("; childAccessibilityIds: [");
2498             for (int i = 0, count = childIds.size(); i < count; i++) {
2499                 builder.append(childIds.valueAt(i));
2500                 if (i < count - 1) {
2501                     builder.append(", ");
2502                 }
2503             }
2504             builder.append("]");
2505         }
2506 
2507         builder.append("; boundsInParent: " + mBoundsInParent);
2508         builder.append("; boundsInScreen: " + mBoundsInScreen);
2509 
2510         builder.append("; packageName: ").append(mPackageName);
2511         builder.append("; className: ").append(mClassName);
2512         builder.append("; text: ").append(mText);
2513         builder.append("; contentDescription: ").append(mContentDescription);
2514         builder.append("; viewIdResName: ").append(mViewIdResourceName);
2515 
2516         builder.append("; checkable: ").append(isCheckable());
2517         builder.append("; checked: ").append(isChecked());
2518         builder.append("; focusable: ").append(isFocusable());
2519         builder.append("; focused: ").append(isFocused());
2520         builder.append("; selected: ").append(isSelected());
2521         builder.append("; clickable: ").append(isClickable());
2522         builder.append("; longClickable: ").append(isLongClickable());
2523         builder.append("; enabled: ").append(isEnabled());
2524         builder.append("; password: ").append(isPassword());
2525         builder.append("; scrollable: " + isScrollable());
2526 
2527         builder.append("; [");
2528         for (int actionBits = mActions; actionBits != 0;) {
2529             final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
2530             actionBits &= ~action;
2531             builder.append(getActionSymbolicName(action));
2532             if (actionBits != 0) {
2533                 builder.append(", ");
2534             }
2535         }
2536         builder.append("]");
2537 
2538         return builder.toString();
2539     }
2540 
2541     /**
2542      * Class with information if a node is a range. Use
2543      * {@link RangeInfo#obtain(int, float, float, float)} to get an instance.
2544      */
2545     public static final class RangeInfo {
2546         private static final int MAX_POOL_SIZE = 10;
2547 
2548         /** Range type: integer. */
2549         public static final int RANGE_TYPE_INT = 0;
2550         /** Range type: float. */
2551         public static final int RANGE_TYPE_FLOAT = 1;
2552         /** Range type: percent with values from zero to one.*/
2553         public static final int RANGE_TYPE_PERCENT = 2;
2554 
2555         private static final SynchronizedPool<RangeInfo> sPool =
2556                 new SynchronizedPool<AccessibilityNodeInfo.RangeInfo>(MAX_POOL_SIZE);
2557 
2558         private int mType;
2559         private float mMin;
2560         private float mMax;
2561         private float mCurrent;
2562 
2563         /**
2564          * Obtains a pooled instance that is a clone of another one.
2565          *
2566          * @param other The instance to clone.
2567          *
2568          * @hide
2569          */
obtain(RangeInfo other)2570         public static RangeInfo obtain(RangeInfo other) {
2571             return obtain(other.mType, other.mMin, other.mMax, other.mCurrent);
2572         }
2573 
2574         /**
2575          * Obtains a pooled instance.
2576          *
2577          * @param type The type of the range.
2578          * @param min The min value.
2579          * @param max The max value.
2580          * @param current The current value.
2581          */
obtain(int type, float min, float max, float current)2582         public static RangeInfo obtain(int type, float min, float max, float current) {
2583             RangeInfo info = sPool.acquire();
2584             return (info != null) ? info : new RangeInfo(type, min, max, current);
2585         }
2586 
2587         /**
2588          * Creates a new range.
2589          *
2590          * @param type The type of the range.
2591          * @param min The min value.
2592          * @param max The max value.
2593          * @param current The current value.
2594          */
RangeInfo(int type, float min, float max, float current)2595         private RangeInfo(int type, float min, float max, float current) {
2596             mType = type;
2597             mMin = min;
2598             mMax = max;
2599             mCurrent = current;
2600         }
2601 
2602         /**
2603          * Gets the range type.
2604          *
2605          * @return The range type.
2606          *
2607          * @see #RANGE_TYPE_INT
2608          * @see #RANGE_TYPE_FLOAT
2609          * @see #RANGE_TYPE_PERCENT
2610          */
getType()2611         public int getType() {
2612             return mType;
2613         }
2614 
2615         /**
2616          * Gets the min value.
2617          *
2618          * @return The min value.
2619          */
getMin()2620         public float getMin() {
2621             return mMin;
2622         }
2623 
2624         /**
2625          * Gets the max value.
2626          *
2627          * @return The max value.
2628          */
getMax()2629         public float getMax() {
2630             return mMax;
2631         }
2632 
2633         /**
2634          * Gets the current value.
2635          *
2636          * @return The current value.
2637          */
getCurrent()2638         public float getCurrent() {
2639             return mCurrent;
2640         }
2641 
2642         /**
2643          * Recycles this instance.
2644          */
recycle()2645         void recycle() {
2646             clear();
2647             sPool.release(this);
2648         }
2649 
clear()2650         private void clear() {
2651             mType = 0;
2652             mMin = 0;
2653             mMax = 0;
2654             mCurrent = 0;
2655         }
2656     }
2657 
2658     /**
2659      * Class with information if a node is a collection. Use
2660      * {@link CollectionInfo#obtain(int, int, boolean)} to get an instance.
2661      * <p>
2662      * A collection of items has rows and columns and may be hierarchical.
2663      * For example, a horizontal list is a collection with one column, as
2664      * many rows as the list items, and is not hierarchical; A table is a
2665      * collection with several rows, several columns, and is not hierarchical;
2666      * A vertical tree is a hierarchical collection with one column and
2667      * as many rows as the first level children.
2668      * </p>
2669      */
2670     public static final class CollectionInfo {
2671         private static final int MAX_POOL_SIZE = 20;
2672 
2673         private static final SynchronizedPool<CollectionInfo> sPool =
2674                 new SynchronizedPool<CollectionInfo>(MAX_POOL_SIZE);
2675 
2676         private int mRowCount;
2677         private int mColumnCount;
2678         private boolean mHierarchical;
2679 
2680         /**
2681          * Obtains a pooled instance that is a clone of another one.
2682          *
2683          * @param other The instance to clone.
2684          *
2685          * @hide
2686          */
obtain(CollectionInfo other)2687         public static CollectionInfo obtain(CollectionInfo other) {
2688             return CollectionInfo.obtain(other.mRowCount, other.mColumnCount,
2689                     other.mHierarchical);
2690         }
2691 
2692         /**
2693          * Obtains a pooled instance.
2694          *
2695          * @param rowCount The number of rows.
2696          * @param columnCount The number of columns.
2697          * @param hierarchical Whether the collection is hierarchical.
2698          */
obtain(int rowCount, int columnCount, boolean hierarchical)2699         public static CollectionInfo obtain(int rowCount, int columnCount,
2700                 boolean hierarchical) {
2701             CollectionInfo info = sPool.acquire();
2702             return (info != null) ? info : new CollectionInfo(rowCount,
2703                     columnCount, hierarchical);
2704         }
2705 
2706         /**
2707          * Creates a new instance.
2708          *
2709          * @param rowCount The number of rows.
2710          * @param columnCount The number of columns.
2711          * @param hierarchical Whether the collection is hierarchical.
2712          */
CollectionInfo(int rowCount, int columnCount, boolean hierarchical)2713         private CollectionInfo(int rowCount, int columnCount,
2714                 boolean hierarchical) {
2715             mRowCount = rowCount;
2716             mColumnCount = columnCount;
2717             mHierarchical = hierarchical;
2718         }
2719 
2720         /**
2721          * Gets the number of rows.
2722          *
2723          * @return The row count.
2724          */
getRowCount()2725         public int getRowCount() {
2726             return mRowCount;
2727         }
2728 
2729         /**
2730          * Gets the number of columns.
2731          *
2732          * @return The column count.
2733          */
getColumnCount()2734         public int getColumnCount() {
2735             return mColumnCount;
2736         }
2737 
2738         /**
2739          * Gets if the collection is a hierarchically ordered.
2740          *
2741          * @return Whether the collection is hierarchical.
2742          */
isHierarchical()2743         public boolean isHierarchical() {
2744             return mHierarchical;
2745         }
2746 
2747         /**
2748          * Recycles this instance.
2749          */
recycle()2750         void recycle() {
2751             clear();
2752             sPool.release(this);
2753         }
2754 
clear()2755         private void clear() {
2756             mRowCount = 0;
2757             mColumnCount = 0;
2758             mHierarchical = false;
2759         }
2760     }
2761 
2762     /**
2763      * Class with information if a node is a collection item. Use
2764      * {@link CollectionItemInfo#obtain(int, int, int, int, boolean)}
2765      * to get an instance.
2766      * <p>
2767      * A collection item is contained in a collection, it starts at
2768      * a given row and column in the collection, and spans one or
2769      * more rows and columns. For example, a header of two related
2770      * table columns starts at the first row and the first column,
2771      * spans one row and two columns.
2772      * </p>
2773      */
2774     public static final class CollectionItemInfo {
2775         private static final int MAX_POOL_SIZE = 20;
2776 
2777         private static final SynchronizedPool<CollectionItemInfo> sPool =
2778                 new SynchronizedPool<CollectionItemInfo>(MAX_POOL_SIZE);
2779 
2780         /**
2781          * Obtains a pooled instance that is a clone of another one.
2782          *
2783          * @param other The instance to clone.
2784          *
2785          * @hide
2786          */
obtain(CollectionItemInfo other)2787         public static CollectionItemInfo obtain(CollectionItemInfo other) {
2788             return CollectionItemInfo.obtain(other.mRowIndex, other.mRowSpan,
2789                     other.mColumnIndex, other.mColumnSpan, other.mHeading);
2790         }
2791 
2792         /**
2793          * Obtains a pooled instance.
2794          *
2795          * @param rowIndex The row index at which the item is located.
2796          * @param rowSpan The number of rows the item spans.
2797          * @param columnIndex The column index at which the item is located.
2798          * @param columnSpan The number of columns the item spans.
2799          * @param heading Whether the item is a heading.
2800          */
obtain(int rowIndex, int rowSpan, int columnIndex, int columnSpan, boolean heading)2801         public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
2802                 int columnIndex, int columnSpan, boolean heading) {
2803             CollectionItemInfo info = sPool.acquire();
2804             return (info != null) ? info : new CollectionItemInfo(rowIndex,
2805                     rowSpan, columnIndex, columnSpan, heading);
2806         }
2807 
2808         private boolean mHeading;
2809         private int mColumnIndex;
2810         private int mRowIndex;
2811         private int mColumnSpan;
2812         private int mRowSpan;
2813 
2814         /**
2815          * Creates a new instance.
2816          *
2817          * @param rowIndex The row index at which the item is located.
2818          * @param rowSpan The number of rows the item spans.
2819          * @param columnIndex The column index at which the item is located.
2820          * @param columnSpan The number of columns the item spans.
2821          * @param heading Whether the item is a heading.
2822          */
CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan, boolean heading)2823         private CollectionItemInfo(int rowIndex, int rowSpan,
2824                 int columnIndex, int columnSpan, boolean heading) {
2825             mRowIndex = rowIndex;
2826             mRowSpan = rowSpan;
2827             mColumnIndex = columnIndex;
2828             mColumnSpan = columnSpan;
2829             mHeading = heading;
2830         }
2831 
2832         /**
2833          * Gets the column index at which the item is located.
2834          *
2835          * @return The column index.
2836          */
getColumnIndex()2837         public int getColumnIndex() {
2838             return mColumnIndex;
2839         }
2840 
2841         /**
2842          * Gets the row index at which the item is located.
2843          *
2844          * @return The row index.
2845          */
getRowIndex()2846         public int getRowIndex() {
2847             return mRowIndex;
2848         }
2849 
2850         /**
2851          * Gets the number of columns the item spans.
2852          *
2853          * @return The column span.
2854          */
getColumnSpan()2855         public int getColumnSpan() {
2856             return mColumnSpan;
2857         }
2858 
2859         /**
2860          * Gets the number of rows the item spans.
2861          *
2862          * @return The row span.
2863          */
getRowSpan()2864         public int getRowSpan() {
2865             return mRowSpan;
2866         }
2867 
2868         /**
2869          * Gets if the collection item is a heading. For example, section
2870          * heading, table header, etc.
2871          *
2872          * @return If the item is a heading.
2873          */
isHeading()2874         public boolean isHeading() {
2875             return mHeading;
2876         }
2877 
2878         /**
2879          * Recycles this instance.
2880          */
recycle()2881         void recycle() {
2882             clear();
2883             sPool.release(this);
2884         }
2885 
clear()2886         private void clear() {
2887             mColumnIndex = 0;
2888             mColumnSpan = 0;
2889             mRowIndex = 0;
2890             mRowSpan = 0;
2891             mHeading = false;
2892         }
2893     }
2894 
2895     /**
2896      * @see Parcelable.Creator
2897      */
2898     public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
2899             new Parcelable.Creator<AccessibilityNodeInfo>() {
2900         public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
2901             AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
2902             info.initFromParcel(parcel);
2903             return info;
2904         }
2905 
2906         public AccessibilityNodeInfo[] newArray(int size) {
2907             return new AccessibilityNodeInfo[size];
2908         }
2909     };
2910 }
2911