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