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