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