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