AccessibilityNodeInfo.java revision 8d5f3fa6455c1eedb4f31bbaf075f2c1f9a2887a
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        if ((action & ACTION_TYPE_MASK) != 0) {
999            throw new IllegalArgumentException("Action is not a combination of the standard " +
1000                    "actions: " + action);
1001        }
1002
1003        addLegacyStandardActions(action);
1004    }
1005
1006    /**
1007     * Removes an action that can be performed on the node. If the action was
1008     * not already added to the node, calling this method has no effect.
1009     * <p>
1010     *   <strong>Note:</strong> Cannot be called from an
1011     *   {@link android.accessibilityservice.AccessibilityService}.
1012     *   This class is made immutable before being delivered to an AccessibilityService.
1013     * </p>
1014     *
1015     * @param action The action to be removed.
1016     *
1017     * @throws IllegalStateException If called from an AccessibilityService.
1018     * @deprecated Use {@link #removeAction(AccessibilityAction)}
1019     */
1020    @Deprecated
1021    public void removeAction(int action) {
1022        enforceNotSealed();
1023
1024        removeAction(getActionSingleton(action));
1025    }
1026
1027    /**
1028     * Removes an action that can be performed on the node. If the action was
1029     * not already added to the node, calling this method has no effect.
1030     * <p>
1031     *   <strong>Note:</strong> Cannot be called from an
1032     *   {@link android.accessibilityservice.AccessibilityService}.
1033     *   This class is made immutable before being delivered to an AccessibilityService.
1034     * </p>
1035     *
1036     * @param action The action to be removed.
1037     * @return The action removed from the list of actions.
1038     *
1039     * @throws IllegalStateException If called from an AccessibilityService.
1040     */
1041    public boolean removeAction(AccessibilityAction action) {
1042        enforceNotSealed();
1043
1044        if (mActions == null || action == null) {
1045            return false;
1046        }
1047
1048        return mActions.remove(action);
1049    }
1050
1051    /**
1052     * Sets the movement granularities for traversing the text of this node.
1053     * <p>
1054     *   <strong>Note:</strong> Cannot be called from an
1055     *   {@link android.accessibilityservice.AccessibilityService}.
1056     *   This class is made immutable before being delivered to an AccessibilityService.
1057     * </p>
1058     *
1059     * @param granularities The bit mask with granularities.
1060     *
1061     * @throws IllegalStateException If called from an AccessibilityService.
1062     */
1063    public void setMovementGranularities(int granularities) {
1064        enforceNotSealed();
1065        mMovementGranularities = granularities;
1066    }
1067
1068    /**
1069     * Gets the movement granularities for traversing the text of this node.
1070     *
1071     * @return The bit mask with granularities.
1072     */
1073    public int getMovementGranularities() {
1074        return mMovementGranularities;
1075    }
1076
1077    /**
1078     * Performs an action on the node.
1079     * <p>
1080     *   <strong>Note:</strong> An action can be performed only if the request is made
1081     *   from an {@link android.accessibilityservice.AccessibilityService}.
1082     * </p>
1083     *
1084     * @param action The action to perform.
1085     * @return True if the action was performed.
1086     *
1087     * @throws IllegalStateException If called outside of an AccessibilityService.
1088     */
1089    public boolean performAction(int action) {
1090        enforceSealed();
1091        if (!canPerformRequestOverConnection(mSourceNodeId)) {
1092            return false;
1093        }
1094        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
1095        return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
1096                action, null);
1097    }
1098
1099    /**
1100     * Performs an action on the node.
1101     * <p>
1102     *   <strong>Note:</strong> An action can be performed only if the request is made
1103     *   from an {@link android.accessibilityservice.AccessibilityService}.
1104     * </p>
1105     *
1106     * @param action The action to perform.
1107     * @param arguments A bundle with additional arguments.
1108     * @return True if the action was performed.
1109     *
1110     * @throws IllegalStateException If called outside of an AccessibilityService.
1111     */
1112    public boolean performAction(int action, Bundle arguments) {
1113        enforceSealed();
1114        if (!canPerformRequestOverConnection(mSourceNodeId)) {
1115            return false;
1116        }
1117        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
1118        return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
1119                action, arguments);
1120    }
1121
1122    /**
1123     * Finds {@link AccessibilityNodeInfo}s by text. The match is case
1124     * insensitive containment. The search is relative to this info i.e.
1125     * this info is the root of the traversed tree.
1126     *
1127     * <p>
1128     *   <strong>Note:</strong> It is a client responsibility to recycle the
1129     *     received info by calling {@link AccessibilityNodeInfo#recycle()}
1130     *     to avoid creating of multiple instances.
1131     * </p>
1132     *
1133     * @param text The searched text.
1134     * @return A list of node info.
1135     */
1136    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
1137        enforceSealed();
1138        if (!canPerformRequestOverConnection(mSourceNodeId)) {
1139            return Collections.emptyList();
1140        }
1141        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
1142        return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,
1143                text);
1144    }
1145
1146    /**
1147     * Finds {@link AccessibilityNodeInfo}s by the fully qualified view id's resource
1148     * name where a fully qualified id is of the from "package:id/id_resource_name".
1149     * For example, if the target application's package is "foo.bar" and the id
1150     * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz".
1151     *
1152     * <p>
1153     *   <strong>Note:</strong> It is a client responsibility to recycle the
1154     *     received info by calling {@link AccessibilityNodeInfo#recycle()}
1155     *     to avoid creating of multiple instances.
1156     * </p>
1157     * <p>
1158     *   <strong>Note:</strong> The primary usage of this API is for UI test automation
1159     *   and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo}
1160     *   the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
1161     *   flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
1162     * </p>
1163     *
1164     * @param viewId The fully qualified resource name of the view id to find.
1165     * @return A list of node info.
1166     */
1167    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) {
1168        enforceSealed();
1169        if (!canPerformRequestOverConnection(mSourceNodeId)) {
1170            return Collections.emptyList();
1171        }
1172        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
1173        return client.findAccessibilityNodeInfosByViewId(mConnectionId, mWindowId, mSourceNodeId,
1174                viewId);
1175    }
1176
1177    /**
1178     * Gets the window to which this node belongs.
1179     *
1180     * @return The window.
1181     *
1182     * @see android.accessibilityservice.AccessibilityService#getWindows()
1183     */
1184    public AccessibilityWindowInfo getWindow() {
1185        enforceSealed();
1186        if (!canPerformRequestOverConnection(mSourceNodeId)) {
1187            return null;
1188        }
1189        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
1190        return client.getWindow(mConnectionId, mWindowId);
1191    }
1192
1193    /**
1194     * Gets the parent.
1195     * <p>
1196     *   <strong>Note:</strong> It is a client responsibility to recycle the
1197     *     received info by calling {@link AccessibilityNodeInfo#recycle()}
1198     *     to avoid creating of multiple instances.
1199     * </p>
1200     *
1201     * @return The parent.
1202     */
1203    public AccessibilityNodeInfo getParent() {
1204        enforceSealed();
1205        if (!canPerformRequestOverConnection(mParentNodeId)) {
1206            return null;
1207        }
1208        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
1209        return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
1210                mWindowId, mParentNodeId, false, FLAG_PREFETCH_PREDECESSORS
1211                        | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
1212    }
1213
1214    /**
1215     * @return The parent node id.
1216     *
1217     * @hide
1218     */
1219    public long getParentNodeId() {
1220        return mParentNodeId;
1221    }
1222
1223    /**
1224     * Sets the parent.
1225     * <p>
1226     *   <strong>Note:</strong> Cannot be called from an
1227     *   {@link android.accessibilityservice.AccessibilityService}.
1228     *   This class is made immutable before being delivered to an AccessibilityService.
1229     * </p>
1230     *
1231     * @param parent The parent.
1232     *
1233     * @throws IllegalStateException If called from an AccessibilityService.
1234     */
1235    public void setParent(View parent) {
1236        setParent(parent, UNDEFINED_ITEM_ID);
1237    }
1238
1239    /**
1240     * Sets the parent to be a virtual descendant of the given <code>root</code>.
1241     * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
1242     * is set as the parent.
1243     * <p>
1244     * A virtual descendant is an imaginary View that is reported as a part of the view
1245     * hierarchy for accessibility purposes. This enables custom views that draw complex
1246     * content to report them selves as a tree of virtual views, thus conveying their
1247     * logical structure.
1248     * </p>
1249     * <p>
1250     *   <strong>Note:</strong> Cannot be called from an
1251     *   {@link android.accessibilityservice.AccessibilityService}.
1252     *   This class is made immutable before being delivered to an AccessibilityService.
1253     * </p>
1254     *
1255     * @param root The root of the virtual subtree.
1256     * @param virtualDescendantId The id of the virtual descendant.
1257     */
1258    public void setParent(View root, int virtualDescendantId) {
1259        enforceNotSealed();
1260        final int rootAccessibilityViewId =
1261            (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
1262        mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
1263    }
1264
1265    /**
1266     * Gets the node bounds in parent coordinates.
1267     *
1268     * @param outBounds The output node bounds.
1269     */
1270    public void getBoundsInParent(Rect outBounds) {
1271        outBounds.set(mBoundsInParent.left, mBoundsInParent.top,
1272                mBoundsInParent.right, mBoundsInParent.bottom);
1273    }
1274
1275    /**
1276     * Sets the node bounds in parent coordinates.
1277     * <p>
1278     *   <strong>Note:</strong> Cannot be called from an
1279     *   {@link android.accessibilityservice.AccessibilityService}.
1280     *   This class is made immutable before being delivered to an AccessibilityService.
1281     * </p>
1282     *
1283     * @param bounds The node bounds.
1284     *
1285     * @throws IllegalStateException If called from an AccessibilityService.
1286     */
1287    public void setBoundsInParent(Rect bounds) {
1288        enforceNotSealed();
1289        mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
1290    }
1291
1292    /**
1293     * Gets the node bounds in screen coordinates.
1294     *
1295     * @param outBounds The output node bounds.
1296     */
1297    public void getBoundsInScreen(Rect outBounds) {
1298        outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top,
1299                mBoundsInScreen.right, mBoundsInScreen.bottom);
1300    }
1301
1302    /**
1303     * Sets the node bounds in screen coordinates.
1304     * <p>
1305     *   <strong>Note:</strong> Cannot be called from an
1306     *   {@link android.accessibilityservice.AccessibilityService}.
1307     *   This class is made immutable before being delivered to an AccessibilityService.
1308     * </p>
1309     *
1310     * @param bounds The node bounds.
1311     *
1312     * @throws IllegalStateException If called from an AccessibilityService.
1313     */
1314    public void setBoundsInScreen(Rect bounds) {
1315        enforceNotSealed();
1316        mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
1317    }
1318
1319    /**
1320     * Gets whether this node is checkable.
1321     *
1322     * @return True if the node is checkable.
1323     */
1324    public boolean isCheckable() {
1325        return getBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE);
1326    }
1327
1328    /**
1329     * Sets whether this node is checkable.
1330     * <p>
1331     *   <strong>Note:</strong> Cannot be called from an
1332     *   {@link android.accessibilityservice.AccessibilityService}.
1333     *   This class is made immutable before being delivered to an AccessibilityService.
1334     * </p>
1335     *
1336     * @param checkable True if the node is checkable.
1337     *
1338     * @throws IllegalStateException If called from an AccessibilityService.
1339     */
1340    public void setCheckable(boolean checkable) {
1341        setBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE, checkable);
1342    }
1343
1344    /**
1345     * Gets whether this node is checked.
1346     *
1347     * @return True if the node is checked.
1348     */
1349    public boolean isChecked() {
1350        return getBooleanProperty(BOOLEAN_PROPERTY_CHECKED);
1351    }
1352
1353    /**
1354     * Sets whether this node is checked.
1355     * <p>
1356     *   <strong>Note:</strong> Cannot be called from an
1357     *   {@link android.accessibilityservice.AccessibilityService}.
1358     *   This class is made immutable before being delivered to an AccessibilityService.
1359     * </p>
1360     *
1361     * @param checked True if the node is checked.
1362     *
1363     * @throws IllegalStateException If called from an AccessibilityService.
1364     */
1365    public void setChecked(boolean checked) {
1366        setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked);
1367    }
1368
1369    /**
1370     * Gets whether this node is focusable.
1371     *
1372     * @return True if the node is focusable.
1373     */
1374    public boolean isFocusable() {
1375        return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE);
1376    }
1377
1378    /**
1379     * Sets whether this node is focusable.
1380     * <p>
1381     *   <strong>Note:</strong> Cannot be called from an
1382     *   {@link android.accessibilityservice.AccessibilityService}.
1383     *   This class is made immutable before being delivered to an AccessibilityService.
1384     * </p>
1385     *
1386     * @param focusable True if the node is focusable.
1387     *
1388     * @throws IllegalStateException If called from an AccessibilityService.
1389     */
1390    public void setFocusable(boolean focusable) {
1391        setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable);
1392    }
1393
1394    /**
1395     * Gets whether this node is focused.
1396     *
1397     * @return True if the node is focused.
1398     */
1399    public boolean isFocused() {
1400        return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED);
1401    }
1402
1403    /**
1404     * Sets whether this node is focused.
1405     * <p>
1406     *   <strong>Note:</strong> Cannot be called from an
1407     *   {@link android.accessibilityservice.AccessibilityService}.
1408     *   This class is made immutable before being delivered to an AccessibilityService.
1409     * </p>
1410     *
1411     * @param focused True if the node is focused.
1412     *
1413     * @throws IllegalStateException If called from an AccessibilityService.
1414     */
1415    public void setFocused(boolean focused) {
1416        setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused);
1417    }
1418
1419    /**
1420     * Sets whether this node is visible to the user.
1421     *
1422     * @return Whether the node is visible to the user.
1423     */
1424    public boolean isVisibleToUser() {
1425        return getBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER);
1426    }
1427
1428    /**
1429     * Sets whether this node is visible to the user.
1430     * <p>
1431     *   <strong>Note:</strong> Cannot be called from an
1432     *   {@link android.accessibilityservice.AccessibilityService}.
1433     *   This class is made immutable before being delivered to an AccessibilityService.
1434     * </p>
1435     *
1436     * @param visibleToUser Whether the node is visible to the user.
1437     *
1438     * @throws IllegalStateException If called from an AccessibilityService.
1439     */
1440    public void setVisibleToUser(boolean visibleToUser) {
1441        setBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER, visibleToUser);
1442    }
1443
1444    /**
1445     * Gets whether this node is accessibility focused.
1446     *
1447     * @return True if the node is accessibility focused.
1448     */
1449    public boolean isAccessibilityFocused() {
1450        return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED);
1451    }
1452
1453    /**
1454     * Sets whether this node is accessibility focused.
1455     * <p>
1456     *   <strong>Note:</strong> Cannot be called from an
1457     *   {@link android.accessibilityservice.AccessibilityService}.
1458     *   This class is made immutable before being delivered to an AccessibilityService.
1459     * </p>
1460     *
1461     * @param focused True if the node is accessibility focused.
1462     *
1463     * @throws IllegalStateException If called from an AccessibilityService.
1464     */
1465    public void setAccessibilityFocused(boolean focused) {
1466        setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused);
1467    }
1468
1469    /**
1470     * Gets whether this node is selected.
1471     *
1472     * @return True if the node is selected.
1473     */
1474    public boolean isSelected() {
1475        return getBooleanProperty(BOOLEAN_PROPERTY_SELECTED);
1476    }
1477
1478    /**
1479     * Sets whether this node is selected.
1480     * <p>
1481     *   <strong>Note:</strong> Cannot be called from an
1482     *   {@link android.accessibilityservice.AccessibilityService}.
1483     *   This class is made immutable before being delivered to an AccessibilityService.
1484     * </p>
1485     *
1486     * @param selected True if the node is selected.
1487     *
1488     * @throws IllegalStateException If called from an AccessibilityService.
1489     */
1490    public void setSelected(boolean selected) {
1491        setBooleanProperty(BOOLEAN_PROPERTY_SELECTED, selected);
1492    }
1493
1494    /**
1495     * Gets whether this node is clickable.
1496     *
1497     * @return True if the node is clickable.
1498     */
1499    public boolean isClickable() {
1500        return getBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE);
1501    }
1502
1503    /**
1504     * Sets whether this node is clickable.
1505     * <p>
1506     *   <strong>Note:</strong> Cannot be called from an
1507     *   {@link android.accessibilityservice.AccessibilityService}.
1508     *   This class is made immutable before being delivered to an AccessibilityService.
1509     * </p>
1510     *
1511     * @param clickable True if the node is clickable.
1512     *
1513     * @throws IllegalStateException If called from an AccessibilityService.
1514     */
1515    public void setClickable(boolean clickable) {
1516        setBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE, clickable);
1517    }
1518
1519    /**
1520     * Gets whether this node is long clickable.
1521     *
1522     * @return True if the node is long clickable.
1523     */
1524    public boolean isLongClickable() {
1525        return getBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE);
1526    }
1527
1528    /**
1529     * Sets whether this node is long clickable.
1530     * <p>
1531     *   <strong>Note:</strong> Cannot be called from an
1532     *   {@link android.accessibilityservice.AccessibilityService}.
1533     *   This class is made immutable before being delivered to an AccessibilityService.
1534     * </p>
1535     *
1536     * @param longClickable True if the node is long clickable.
1537     *
1538     * @throws IllegalStateException If called from an AccessibilityService.
1539     */
1540    public void setLongClickable(boolean longClickable) {
1541        setBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE, longClickable);
1542    }
1543
1544    /**
1545     * Gets whether this node is enabled.
1546     *
1547     * @return True if the node is enabled.
1548     */
1549    public boolean isEnabled() {
1550        return getBooleanProperty(BOOLEAN_PROPERTY_ENABLED);
1551    }
1552
1553    /**
1554     * Sets whether this node is enabled.
1555     * <p>
1556     *   <strong>Note:</strong> Cannot be called from an
1557     *   {@link android.accessibilityservice.AccessibilityService}.
1558     *   This class is made immutable before being delivered to an AccessibilityService.
1559     * </p>
1560     *
1561     * @param enabled True if the node is enabled.
1562     *
1563     * @throws IllegalStateException If called from an AccessibilityService.
1564     */
1565    public void setEnabled(boolean enabled) {
1566        setBooleanProperty(BOOLEAN_PROPERTY_ENABLED, enabled);
1567    }
1568
1569    /**
1570     * Gets whether this node is a password.
1571     *
1572     * @return True if the node is a password.
1573     */
1574    public boolean isPassword() {
1575        return getBooleanProperty(BOOLEAN_PROPERTY_PASSWORD);
1576    }
1577
1578    /**
1579     * Sets whether this node is a password.
1580     * <p>
1581     *   <strong>Note:</strong> Cannot be called from an
1582     *   {@link android.accessibilityservice.AccessibilityService}.
1583     *   This class is made immutable before being delivered to an AccessibilityService.
1584     * </p>
1585     *
1586     * @param password True if the node is a password.
1587     *
1588     * @throws IllegalStateException If called from an AccessibilityService.
1589     */
1590    public void setPassword(boolean password) {
1591        setBooleanProperty(BOOLEAN_PROPERTY_PASSWORD, password);
1592    }
1593
1594    /**
1595     * Gets if the node is scrollable.
1596     *
1597     * @return True if the node is scrollable, false otherwise.
1598     */
1599    public boolean isScrollable() {
1600        return getBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE);
1601    }
1602
1603    /**
1604     * Sets if the node is scrollable.
1605     * <p>
1606     *   <strong>Note:</strong> Cannot be called from an
1607     *   {@link android.accessibilityservice.AccessibilityService}.
1608     *   This class is made immutable before being delivered to an AccessibilityService.
1609     * </p>
1610     *
1611     * @param scrollable True if the node is scrollable, false otherwise.
1612     *
1613     * @throws IllegalStateException If called from an AccessibilityService.
1614     */
1615    public void setScrollable(boolean scrollable) {
1616        setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable);
1617    }
1618
1619    /**
1620     * Gets if the node is editable.
1621     *
1622     * @return True if the node is editable, false otherwise.
1623     */
1624    public boolean isEditable() {
1625        return getBooleanProperty(BOOLEAN_PROPERTY_EDITABLE);
1626    }
1627
1628    /**
1629     * Sets whether this node is editable.
1630     * <p>
1631     *   <strong>Note:</strong> Cannot be called from an
1632     *   {@link android.accessibilityservice.AccessibilityService}.
1633     *   This class is made immutable before being delivered to an AccessibilityService.
1634     * </p>
1635     *
1636     * @param editable True if the node is editable.
1637     *
1638     * @throws IllegalStateException If called from an AccessibilityService.
1639     */
1640    public void setEditable(boolean editable) {
1641        setBooleanProperty(BOOLEAN_PROPERTY_EDITABLE, editable);
1642    }
1643
1644    /**
1645     * Gets the collection info if the node is a collection. A collection
1646     * child is always a collection item.
1647     *
1648     * @return The collection info.
1649     */
1650    public CollectionInfo getCollectionInfo() {
1651        return mCollectionInfo;
1652    }
1653
1654    /**
1655     * Sets the collection info if the node is a collection. A collection
1656     * child is always a collection item.
1657     * <p>
1658     *   <strong>Note:</strong> Cannot be called from an
1659     *   {@link android.accessibilityservice.AccessibilityService}.
1660     *   This class is made immutable before being delivered to an AccessibilityService.
1661     * </p>
1662     *
1663     * @param collectionInfo The collection info.
1664     */
1665    public void setCollectionInfo(CollectionInfo collectionInfo) {
1666        enforceNotSealed();
1667        mCollectionInfo = collectionInfo;
1668    }
1669
1670    /**
1671     * Gets the collection item info if the node is a collection item. A collection
1672     * item is always a child of a collection.
1673     *
1674     * @return The collection item info.
1675     */
1676    public CollectionItemInfo getCollectionItemInfo() {
1677        return mCollectionItemInfo;
1678    }
1679
1680    /**
1681     * Sets the collection item info if the node is a collection item. A collection
1682     * item is always a child of a collection.
1683     * <p>
1684     *   <strong>Note:</strong> Cannot be called from an
1685     *   {@link android.accessibilityservice.AccessibilityService}.
1686     *   This class is made immutable before being delivered to an AccessibilityService.
1687     * </p>
1688     */
1689    public void setCollectionItemInfo(CollectionItemInfo collectionItemInfo) {
1690        enforceNotSealed();
1691        mCollectionItemInfo = collectionItemInfo;
1692    }
1693
1694    /**
1695     * Gets the range info if this node is a range.
1696     *
1697     * @return The range.
1698     */
1699    public RangeInfo getRangeInfo() {
1700        return mRangeInfo;
1701    }
1702
1703    /**
1704     * Sets the range info if this node is a range.
1705     * <p>
1706     *   <strong>Note:</strong> Cannot be called from an
1707     *   {@link android.accessibilityservice.AccessibilityService}.
1708     *   This class is made immutable before being delivered to an AccessibilityService.
1709     * </p>
1710     *
1711     * @param rangeInfo The range info.
1712     */
1713    public void setRangeInfo(RangeInfo rangeInfo) {
1714        enforceNotSealed();
1715        mRangeInfo = rangeInfo;
1716    }
1717
1718    /**
1719     * Gets if the content of this node is invalid. For example,
1720     * a date is not well-formed.
1721     *
1722     * @return If the node content is invalid.
1723     */
1724    public boolean isContentInvalid() {
1725        return getBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID);
1726    }
1727
1728    /**
1729     * Sets if the content of this node is invalid. For example,
1730     * a date is not well-formed.
1731     * <p>
1732     *   <strong>Note:</strong> Cannot be called from an
1733     *   {@link android.accessibilityservice.AccessibilityService}.
1734     *   This class is made immutable before being delivered to an AccessibilityService.
1735     * </p>
1736     *
1737     * @param contentInvalid If the node content is invalid.
1738     */
1739    public void setContentInvalid(boolean contentInvalid) {
1740        setBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID, contentInvalid);
1741    }
1742
1743    /**
1744     * Gets the node's live region mode.
1745     * <p>
1746     * A live region is a node that contains information that is important for
1747     * the user and when it changes the user should be notified. For example,
1748     * in a login screen with a TextView that displays an "incorrect password"
1749     * notification, that view should be marked as a live region with mode
1750     * {@link View#ACCESSIBILITY_LIVE_REGION_POLITE}.
1751     * <p>
1752     * It is the responsibility of the accessibility service to monitor
1753     * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events indicating
1754     * changes to live region nodes and their children.
1755     *
1756     * @return The live region mode, or
1757     *         {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a
1758     *         live region.
1759     * @see android.view.View#getAccessibilityLiveRegion()
1760     */
1761    public int getLiveRegion() {
1762        return mLiveRegion;
1763    }
1764
1765    /**
1766     * Sets the node's live region mode.
1767     * <p>
1768     * <strong>Note:</strong> Cannot be called from an
1769     * {@link android.accessibilityservice.AccessibilityService}. This class is
1770     * made immutable before being delivered to an AccessibilityService.
1771     *
1772     * @param mode The live region mode, or
1773     *        {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a
1774     *        live region.
1775     * @see android.view.View#setAccessibilityLiveRegion(int)
1776     */
1777    public void setLiveRegion(int mode) {
1778        enforceNotSealed();
1779        mLiveRegion = mode;
1780    }
1781
1782    /**
1783     * Gets if the node is a multi line editable text.
1784     *
1785     * @return True if the node is multi line.
1786     */
1787    public boolean isMultiLine() {
1788        return getBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE);
1789    }
1790
1791    /**
1792     * Sets if the node is a multi line editable text.
1793     * <p>
1794     *   <strong>Note:</strong> Cannot be called from an
1795     *   {@link android.accessibilityservice.AccessibilityService}.
1796     *   This class is made immutable before being delivered to an AccessibilityService.
1797     * </p>
1798     *
1799     * @param multiLine True if the node is multi line.
1800     */
1801    public void setMultiLine(boolean multiLine) {
1802        setBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE, multiLine);
1803    }
1804
1805    /**
1806     * Gets if this node opens a popup or a dialog.
1807     *
1808     * @return If the the node opens a popup.
1809     */
1810    public boolean canOpenPopup() {
1811        return getBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP);
1812    }
1813
1814    /**
1815     * Sets if this node opens a popup or a dialog.
1816     * <p>
1817     *   <strong>Note:</strong> Cannot be called from an
1818     *   {@link android.accessibilityservice.AccessibilityService}.
1819     *   This class is made immutable before being delivered to an AccessibilityService.
1820     * </p>
1821     *
1822     * @param opensPopup If the the node opens a popup.
1823     */
1824    public void setCanOpenPopup(boolean opensPopup) {
1825        enforceNotSealed();
1826        setBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP, opensPopup);
1827    }
1828
1829    /**
1830     * Gets if the node can be dismissed.
1831     *
1832     * @return If the node can be dismissed.
1833     */
1834    public boolean isDismissable() {
1835        return getBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE);
1836    }
1837
1838    /**
1839     * Sets if the node can be dismissed.
1840     * <p>
1841     *   <strong>Note:</strong> Cannot be called from an
1842     *   {@link android.accessibilityservice.AccessibilityService}.
1843     *   This class is made immutable before being delivered to an AccessibilityService.
1844     * </p>
1845     *
1846     * @param dismissable If the node can be dismissed.
1847     */
1848    public void setDismissable(boolean dismissable) {
1849        setBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE, dismissable);
1850    }
1851
1852    /**
1853     * Gets the package this node comes from.
1854     *
1855     * @return The package name.
1856     */
1857    public CharSequence getPackageName() {
1858        return mPackageName;
1859    }
1860
1861    /**
1862     * Sets the package this node comes from.
1863     * <p>
1864     *   <strong>Note:</strong> Cannot be called from an
1865     *   {@link android.accessibilityservice.AccessibilityService}.
1866     *   This class is made immutable before being delivered to an AccessibilityService.
1867     * </p>
1868     *
1869     * @param packageName The package name.
1870     *
1871     * @throws IllegalStateException If called from an AccessibilityService.
1872     */
1873    public void setPackageName(CharSequence packageName) {
1874        enforceNotSealed();
1875        mPackageName = packageName;
1876    }
1877
1878    /**
1879     * Gets the class this node comes from.
1880     *
1881     * @return The class name.
1882     */
1883    public CharSequence getClassName() {
1884        return mClassName;
1885    }
1886
1887    /**
1888     * Sets the class this node comes from.
1889     * <p>
1890     *   <strong>Note:</strong> Cannot be called from an
1891     *   {@link android.accessibilityservice.AccessibilityService}.
1892     *   This class is made immutable before being delivered to an AccessibilityService.
1893     * </p>
1894     *
1895     * @param className The class name.
1896     *
1897     * @throws IllegalStateException If called from an AccessibilityService.
1898     */
1899    public void setClassName(CharSequence className) {
1900        enforceNotSealed();
1901        mClassName = className;
1902    }
1903
1904    /**
1905     * Gets the text of this node.
1906     *
1907     * @return The text.
1908     */
1909    public CharSequence getText() {
1910        return mText;
1911    }
1912
1913    /**
1914     * Sets the text of this node.
1915     * <p>
1916     *   <strong>Note:</strong> Cannot be called from an
1917     *   {@link android.accessibilityservice.AccessibilityService}.
1918     *   This class is made immutable before being delivered to an AccessibilityService.
1919     * </p>
1920     *
1921     * @param text The text.
1922     *
1923     * @throws IllegalStateException If called from an AccessibilityService.
1924     */
1925    public void setText(CharSequence text) {
1926        enforceNotSealed();
1927        mText = text;
1928    }
1929
1930    /**
1931     * Gets the content description of this node.
1932     *
1933     * @return The content description.
1934     */
1935    public CharSequence getContentDescription() {
1936        return mContentDescription;
1937    }
1938
1939    /**
1940     * Sets the content description of this node.
1941     * <p>
1942     *   <strong>Note:</strong> Cannot be called from an
1943     *   {@link android.accessibilityservice.AccessibilityService}.
1944     *   This class is made immutable before being delivered to an AccessibilityService.
1945     * </p>
1946     *
1947     * @param contentDescription The content description.
1948     *
1949     * @throws IllegalStateException If called from an AccessibilityService.
1950     */
1951    public void setContentDescription(CharSequence contentDescription) {
1952        enforceNotSealed();
1953        mContentDescription = contentDescription;
1954    }
1955
1956    /**
1957     * Sets the view for which the view represented by this info serves as a
1958     * label for accessibility purposes.
1959     *
1960     * @param labeled The view for which this info serves as a label.
1961     */
1962    public void setLabelFor(View labeled) {
1963        setLabelFor(labeled, UNDEFINED_ITEM_ID);
1964    }
1965
1966    /**
1967     * Sets the view for which the view represented by this info serves as a
1968     * label for accessibility purposes. If <code>virtualDescendantId</code>
1969     * is {@link View#NO_ID} the root is set as the labeled.
1970     * <p>
1971     * A virtual descendant is an imaginary View that is reported as a part of the view
1972     * hierarchy for accessibility purposes. This enables custom views that draw complex
1973     * content to report themselves as a tree of virtual views, thus conveying their
1974     * logical structure.
1975     * </p>
1976     * <p>
1977     *   <strong>Note:</strong> Cannot be called from an
1978     *   {@link android.accessibilityservice.AccessibilityService}.
1979     *   This class is made immutable before being delivered to an AccessibilityService.
1980     * </p>
1981     *
1982     * @param root The root whose virtual descendant serves as a label.
1983     * @param virtualDescendantId The id of the virtual descendant.
1984     */
1985    public void setLabelFor(View root, int virtualDescendantId) {
1986        enforceNotSealed();
1987        final int rootAccessibilityViewId = (root != null)
1988                ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
1989        mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
1990    }
1991
1992    /**
1993     * Gets the node info for which the view represented by this info serves as
1994     * a label for accessibility purposes.
1995     * <p>
1996     *   <strong>Note:</strong> It is a client responsibility to recycle the
1997     *     received info by calling {@link AccessibilityNodeInfo#recycle()}
1998     *     to avoid creating of multiple instances.
1999     * </p>
2000     *
2001     * @return The labeled info.
2002     */
2003    public AccessibilityNodeInfo getLabelFor() {
2004        enforceSealed();
2005        if (!canPerformRequestOverConnection(mLabelForId)) {
2006            return null;
2007        }
2008        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
2009        return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
2010                mWindowId, mLabelForId, false, FLAG_PREFETCH_PREDECESSORS
2011                        | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
2012    }
2013
2014    /**
2015     * Sets the view which serves as the label of the view represented by
2016     * this info for accessibility purposes.
2017     *
2018     * @param label The view that labels this node's source.
2019     */
2020    public void setLabeledBy(View label) {
2021        setLabeledBy(label, UNDEFINED_ITEM_ID);
2022    }
2023
2024    /**
2025     * Sets the view which serves as the label of the view represented by
2026     * this info for accessibility purposes. If <code>virtualDescendantId</code>
2027     * is {@link View#NO_ID} the root is set as the label.
2028     * <p>
2029     * A virtual descendant is an imaginary View that is reported as a part of the view
2030     * hierarchy for accessibility purposes. This enables custom views that draw complex
2031     * content to report themselves as a tree of virtual views, thus conveying their
2032     * logical structure.
2033     * </p>
2034     * <p>
2035     *   <strong>Note:</strong> Cannot be called from an
2036     *   {@link android.accessibilityservice.AccessibilityService}.
2037     *   This class is made immutable before being delivered to an AccessibilityService.
2038     * </p>
2039     *
2040     * @param root The root whose virtual descendant labels this node's source.
2041     * @param virtualDescendantId The id of the virtual descendant.
2042     */
2043    public void setLabeledBy(View root, int virtualDescendantId) {
2044        enforceNotSealed();
2045        final int rootAccessibilityViewId = (root != null)
2046                ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
2047        mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
2048    }
2049
2050    /**
2051     * Gets the node info which serves as the label of the view represented by
2052     * this info for accessibility purposes.
2053     * <p>
2054     *   <strong>Note:</strong> It is a client responsibility to recycle the
2055     *     received info by calling {@link AccessibilityNodeInfo#recycle()}
2056     *     to avoid creating of multiple instances.
2057     * </p>
2058     *
2059     * @return The label.
2060     */
2061    public AccessibilityNodeInfo getLabeledBy() {
2062        enforceSealed();
2063        if (!canPerformRequestOverConnection(mLabeledById)) {
2064            return null;
2065        }
2066        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
2067        return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
2068                mWindowId, mLabeledById, false, FLAG_PREFETCH_PREDECESSORS
2069                        | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
2070    }
2071
2072    /**
2073     * Sets the fully qualified resource name of the source view's id.
2074     *
2075     * <p>
2076     *   <strong>Note:</strong> Cannot be called from an
2077     *   {@link android.accessibilityservice.AccessibilityService}.
2078     *   This class is made immutable before being delivered to an AccessibilityService.
2079     * </p>
2080     *
2081     * @param viewIdResName The id resource name.
2082     */
2083    public void setViewIdResourceName(String viewIdResName) {
2084        enforceNotSealed();
2085        mViewIdResourceName = viewIdResName;
2086    }
2087
2088    /**
2089     * Gets the fully qualified resource name of the source view's id.
2090     *
2091     * <p>
2092     *   <strong>Note:</strong> The primary usage of this API is for UI test automation
2093     *   and in order to report the source view id of an {@link AccessibilityNodeInfo} the
2094     *   client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
2095     *   flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
2096     * </p>
2097
2098     * @return The id resource name.
2099     */
2100    public String getViewIdResourceName() {
2101        return mViewIdResourceName;
2102    }
2103
2104    /**
2105     * Gets the text selection start.
2106     *
2107     * @return The text selection start if there is selection or -1.
2108     */
2109    public int getTextSelectionStart() {
2110        return mTextSelectionStart;
2111    }
2112
2113    /**
2114     * Gets the text selection end.
2115     *
2116     * @return The text selection end if there is selection or -1.
2117     */
2118    public int getTextSelectionEnd() {
2119        return mTextSelectionEnd;
2120    }
2121
2122    /**
2123     * Sets the text selection start and end.
2124     * <p>
2125     *   <strong>Note:</strong> Cannot be called from an
2126     *   {@link android.accessibilityservice.AccessibilityService}.
2127     *   This class is made immutable before being delivered to an AccessibilityService.
2128     * </p>
2129     *
2130     * @param start The text selection start.
2131     * @param end The text selection end.
2132     *
2133     * @throws IllegalStateException If called from an AccessibilityService.
2134     */
2135    public void setTextSelection(int start, int end) {
2136        enforceNotSealed();
2137        mTextSelectionStart = start;
2138        mTextSelectionEnd = end;
2139    }
2140
2141    /**
2142     * Gets the input type of the source as defined by {@link InputType}.
2143     *
2144     * @return The input type.
2145     */
2146    public int getInputType() {
2147        return mInputType;
2148    }
2149
2150    /**
2151     * Sets the input type of the source as defined by {@link InputType}.
2152     * <p>
2153     *   <strong>Note:</strong> Cannot be called from an
2154     *   {@link android.accessibilityservice.AccessibilityService}.
2155     *   This class is made immutable before being delivered to an
2156     *   AccessibilityService.
2157     * </p>
2158     *
2159     * @param inputType The input type.
2160     *
2161     * @throws IllegalStateException If called from an AccessibilityService.
2162     */
2163    public void setInputType(int inputType) {
2164        enforceNotSealed();
2165        mInputType = inputType;
2166    }
2167
2168    /**
2169     * Gets an optional bundle with extra data. The bundle
2170     * is lazily created and never <code>null</code>.
2171     * <p>
2172     * <strong>Note:</strong> It is recommended to use the package
2173     * name of your application as a prefix for the keys to avoid
2174     * collisions which may confuse an accessibility service if the
2175     * same key has different meaning when emitted from different
2176     * applications.
2177     * </p>
2178     *
2179     * @return The bundle.
2180     */
2181    public Bundle getExtras() {
2182        if (mExtras == null) {
2183            mExtras = new Bundle();
2184        }
2185        return mExtras;
2186    }
2187
2188    /**
2189     * Gets the value of a boolean property.
2190     *
2191     * @param property The property.
2192     * @return The value.
2193     */
2194    private boolean getBooleanProperty(int property) {
2195        return (mBooleanProperties & property) != 0;
2196    }
2197
2198    /**
2199     * Sets a boolean property.
2200     *
2201     * @param property The property.
2202     * @param value The value.
2203     *
2204     * @throws IllegalStateException If called from an AccessibilityService.
2205     */
2206    private void setBooleanProperty(int property, boolean value) {
2207        enforceNotSealed();
2208        if (value) {
2209            mBooleanProperties |= property;
2210        } else {
2211            mBooleanProperties &= ~property;
2212        }
2213    }
2214
2215    /**
2216     * Sets the unique id of the IAccessibilityServiceConnection over which
2217     * this instance can send requests to the system.
2218     *
2219     * @param connectionId The connection id.
2220     *
2221     * @hide
2222     */
2223    public void setConnectionId(int connectionId) {
2224        enforceNotSealed();
2225        mConnectionId = connectionId;
2226    }
2227
2228    /**
2229     * {@inheritDoc}
2230     */
2231    @Override
2232    public int describeContents() {
2233        return 0;
2234    }
2235
2236    /**
2237     * Gets the id of the source node.
2238     *
2239     * @return The id.
2240     *
2241     * @hide
2242     */
2243    public long getSourceNodeId() {
2244        return mSourceNodeId;
2245    }
2246
2247    /**
2248     * Sets if this instance is sealed.
2249     *
2250     * @param sealed Whether is sealed.
2251     *
2252     * @hide
2253     */
2254    public void setSealed(boolean sealed) {
2255        mSealed = sealed;
2256    }
2257
2258    /**
2259     * Gets if this instance is sealed.
2260     *
2261     * @return Whether is sealed.
2262     *
2263     * @hide
2264     */
2265    public boolean isSealed() {
2266        return mSealed;
2267    }
2268
2269    /**
2270     * Enforces that this instance is sealed.
2271     *
2272     * @throws IllegalStateException If this instance is not sealed.
2273     *
2274     * @hide
2275     */
2276    protected void enforceSealed() {
2277        if (!isSealed()) {
2278            throw new IllegalStateException("Cannot perform this "
2279                    + "action on a not sealed instance.");
2280        }
2281    }
2282
2283    private void enforceValidFocusDirection(int direction) {
2284        switch (direction) {
2285            case View.FOCUS_DOWN:
2286            case View.FOCUS_UP:
2287            case View.FOCUS_LEFT:
2288            case View.FOCUS_RIGHT:
2289            case View.FOCUS_FORWARD:
2290            case View.FOCUS_BACKWARD:
2291                return;
2292            default:
2293                throw new IllegalArgumentException("Unknown direction: " + direction);
2294        }
2295    }
2296
2297    private void enforceValidFocusType(int focusType) {
2298        switch (focusType) {
2299            case FOCUS_INPUT:
2300            case FOCUS_ACCESSIBILITY:
2301                return;
2302            default:
2303                throw new IllegalArgumentException("Unknown focus type: " + focusType);
2304        }
2305    }
2306
2307    /**
2308     * Enforces that this instance is not sealed.
2309     *
2310     * @throws IllegalStateException If this instance is sealed.
2311     *
2312     * @hide
2313     */
2314    protected void enforceNotSealed() {
2315        if (isSealed()) {
2316            throw new IllegalStateException("Cannot perform this "
2317                    + "action on a sealed instance.");
2318        }
2319    }
2320
2321    /**
2322     * Returns a cached instance if such is available otherwise a new one
2323     * and sets the source.
2324     *
2325     * @param source The source view.
2326     * @return An instance.
2327     *
2328     * @see #setSource(View)
2329     */
2330    public static AccessibilityNodeInfo obtain(View source) {
2331        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
2332        info.setSource(source);
2333        return info;
2334    }
2335
2336    /**
2337     * Returns a cached instance if such is available otherwise a new one
2338     * and sets the source.
2339     *
2340     * @param root The root of the virtual subtree.
2341     * @param virtualDescendantId The id of the virtual descendant.
2342     * @return An instance.
2343     *
2344     * @see #setSource(View, int)
2345     */
2346    public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
2347        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
2348        info.setSource(root, virtualDescendantId);
2349        return info;
2350    }
2351
2352    /**
2353     * Returns a cached instance if such is available otherwise a new one.
2354     *
2355     * @return An instance.
2356     */
2357    public static AccessibilityNodeInfo obtain() {
2358        AccessibilityNodeInfo info = sPool.acquire();
2359        return (info != null) ? info : new AccessibilityNodeInfo();
2360    }
2361
2362    /**
2363     * Returns a cached instance if such is available or a new one is
2364     * create. The returned instance is initialized from the given
2365     * <code>info</code>.
2366     *
2367     * @param info The other info.
2368     * @return An instance.
2369     */
2370    public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
2371        AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain();
2372        infoClone.init(info);
2373        return infoClone;
2374    }
2375
2376    /**
2377     * Return an instance back to be reused.
2378     * <p>
2379     * <strong>Note:</strong> You must not touch the object after calling this function.
2380     *
2381     * @throws IllegalStateException If the info is already recycled.
2382     */
2383    public void recycle() {
2384        clear();
2385        sPool.release(this);
2386    }
2387
2388    /**
2389     * {@inheritDoc}
2390     * <p>
2391     *   <strong>Note:</strong> After the instance is written to a parcel it
2392     *      is recycled. You must not touch the object after calling this function.
2393     * </p>
2394     */
2395    @Override
2396    public void writeToParcel(Parcel parcel, int flags) {
2397        parcel.writeInt(isSealed() ? 1 : 0);
2398        parcel.writeLong(mSourceNodeId);
2399        parcel.writeInt(mWindowId);
2400        parcel.writeLong(mParentNodeId);
2401        parcel.writeLong(mLabelForId);
2402        parcel.writeLong(mLabeledById);
2403        parcel.writeInt(mConnectionId);
2404
2405        final LongArray childIds = mChildNodeIds;
2406        if (childIds == null) {
2407            parcel.writeInt(0);
2408        } else {
2409            final int childIdsSize = childIds.size();
2410            parcel.writeInt(childIdsSize);
2411            for (int i = 0; i < childIdsSize; i++) {
2412                parcel.writeLong(childIds.get(i));
2413            }
2414        }
2415
2416        parcel.writeInt(mBoundsInParent.top);
2417        parcel.writeInt(mBoundsInParent.bottom);
2418        parcel.writeInt(mBoundsInParent.left);
2419        parcel.writeInt(mBoundsInParent.right);
2420
2421        parcel.writeInt(mBoundsInScreen.top);
2422        parcel.writeInt(mBoundsInScreen.bottom);
2423        parcel.writeInt(mBoundsInScreen.left);
2424        parcel.writeInt(mBoundsInScreen.right);
2425
2426        if (mActions != null && !mActions.isEmpty()) {
2427            final int actionCount = mActions.size();
2428            parcel.writeInt(actionCount);
2429
2430            int defaultLegacyStandardActions = 0;
2431            for (int i = 0; i < actionCount; i++) {
2432                AccessibilityAction action = mActions.get(i);
2433                if (isDefaultLegacyStandardAction(action)) {
2434                    defaultLegacyStandardActions |= action.getId();
2435                }
2436            }
2437            parcel.writeInt(defaultLegacyStandardActions);
2438
2439            for (int i = 0; i < actionCount; i++) {
2440                AccessibilityAction action = mActions.get(i);
2441                if (!isDefaultLegacyStandardAction(action)) {
2442                    parcel.writeInt(action.getId());
2443                    parcel.writeCharSequence(action.getLabel());
2444                }
2445            }
2446        } else {
2447            parcel.writeInt(0);
2448        }
2449
2450        parcel.writeInt(mMovementGranularities);
2451
2452        parcel.writeInt(mBooleanProperties);
2453
2454        parcel.writeCharSequence(mPackageName);
2455        parcel.writeCharSequence(mClassName);
2456        parcel.writeCharSequence(mText);
2457        parcel.writeCharSequence(mContentDescription);
2458        parcel.writeString(mViewIdResourceName);
2459
2460        parcel.writeInt(mTextSelectionStart);
2461        parcel.writeInt(mTextSelectionEnd);
2462        parcel.writeInt(mInputType);
2463        parcel.writeInt(mLiveRegion);
2464
2465        if (mExtras != null) {
2466            parcel.writeInt(1);
2467            parcel.writeBundle(mExtras);
2468        } else {
2469            parcel.writeInt(0);
2470        }
2471
2472        if (mRangeInfo != null) {
2473            parcel.writeInt(1);
2474            parcel.writeInt(mRangeInfo.getType());
2475            parcel.writeFloat(mRangeInfo.getMin());
2476            parcel.writeFloat(mRangeInfo.getMax());
2477            parcel.writeFloat(mRangeInfo.getCurrent());
2478        } else {
2479            parcel.writeInt(0);
2480        }
2481
2482        if (mCollectionInfo != null) {
2483            parcel.writeInt(1);
2484            parcel.writeInt(mCollectionInfo.getRowCount());
2485            parcel.writeInt(mCollectionInfo.getColumnCount());
2486            parcel.writeInt(mCollectionInfo.isHierarchical() ? 1 : 0);
2487            parcel.writeInt(mCollectionInfo.getSelectionMode());
2488        } else {
2489            parcel.writeInt(0);
2490        }
2491
2492        if (mCollectionItemInfo != null) {
2493            parcel.writeInt(1);
2494            parcel.writeInt(mCollectionItemInfo.getColumnIndex());
2495            parcel.writeInt(mCollectionItemInfo.getColumnSpan());
2496            parcel.writeInt(mCollectionItemInfo.getRowIndex());
2497            parcel.writeInt(mCollectionItemInfo.getRowSpan());
2498            parcel.writeInt(mCollectionItemInfo.isHeading() ? 1 : 0);
2499            parcel.writeInt(mCollectionItemInfo.isSelected() ? 1 : 0);
2500        } else {
2501            parcel.writeInt(0);
2502        }
2503
2504        // Since instances of this class are fetched via synchronous i.e. blocking
2505        // calls in IPCs we always recycle as soon as the instance is marshaled.
2506        recycle();
2507    }
2508
2509    /**
2510     * Initializes this instance from another one.
2511     *
2512     * @param other The other instance.
2513     */
2514    private void init(AccessibilityNodeInfo other) {
2515        mSealed = other.mSealed;
2516        mSourceNodeId = other.mSourceNodeId;
2517        mParentNodeId = other.mParentNodeId;
2518        mLabelForId = other.mLabelForId;
2519        mLabeledById = other.mLabeledById;
2520        mWindowId = other.mWindowId;
2521        mConnectionId = other.mConnectionId;
2522        mBoundsInParent.set(other.mBoundsInParent);
2523        mBoundsInScreen.set(other.mBoundsInScreen);
2524        mPackageName = other.mPackageName;
2525        mClassName = other.mClassName;
2526        mText = other.mText;
2527        mContentDescription = other.mContentDescription;
2528        mViewIdResourceName = other.mViewIdResourceName;
2529
2530        final ArrayList<AccessibilityAction> otherActions = other.mActions;
2531        if (otherActions != null && otherActions.size() > 0) {
2532            if (mActions == null) {
2533                mActions = new ArrayList(otherActions);
2534            } else {
2535                mActions.clear();
2536                mActions.addAll(other.mActions);
2537            }
2538        }
2539
2540        mBooleanProperties = other.mBooleanProperties;
2541        mMovementGranularities = other.mMovementGranularities;
2542
2543        final LongArray otherChildNodeIds = other.mChildNodeIds;
2544        if (otherChildNodeIds != null && otherChildNodeIds.size() > 0) {
2545            if (mChildNodeIds == null) {
2546                mChildNodeIds = otherChildNodeIds.clone();
2547            } else {
2548                mChildNodeIds.clear();
2549                mChildNodeIds.addAll(otherChildNodeIds);
2550            }
2551        }
2552
2553        mTextSelectionStart = other.mTextSelectionStart;
2554        mTextSelectionEnd = other.mTextSelectionEnd;
2555        mInputType = other.mInputType;
2556        mLiveRegion = other.mLiveRegion;
2557        if (other.mExtras != null && !other.mExtras.isEmpty()) {
2558            getExtras().putAll(other.mExtras);
2559        }
2560        mRangeInfo = (other.mRangeInfo != null)
2561                ? RangeInfo.obtain(other.mRangeInfo) : null;
2562        mCollectionInfo = (other.mCollectionInfo != null)
2563                ? CollectionInfo.obtain(other.mCollectionInfo) : null;
2564        mCollectionItemInfo =  (other.mCollectionItemInfo != null)
2565                ? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null;
2566    }
2567
2568    /**
2569     * Creates a new instance from a {@link Parcel}.
2570     *
2571     * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
2572     */
2573    private void initFromParcel(Parcel parcel) {
2574        mSealed = (parcel.readInt()  == 1);
2575        mSourceNodeId = parcel.readLong();
2576        mWindowId = parcel.readInt();
2577        mParentNodeId = parcel.readLong();
2578        mLabelForId = parcel.readLong();
2579        mLabeledById = parcel.readLong();
2580        mConnectionId = parcel.readInt();
2581
2582        final int childrenSize = parcel.readInt();
2583        if (childrenSize <= 0) {
2584            mChildNodeIds = null;
2585        } else {
2586            mChildNodeIds = new LongArray(childrenSize);
2587            for (int i = 0; i < childrenSize; i++) {
2588                final long childId = parcel.readLong();
2589                mChildNodeIds.add(childId);
2590            }
2591        }
2592
2593        mBoundsInParent.top = parcel.readInt();
2594        mBoundsInParent.bottom = parcel.readInt();
2595        mBoundsInParent.left = parcel.readInt();
2596        mBoundsInParent.right = parcel.readInt();
2597
2598        mBoundsInScreen.top = parcel.readInt();
2599        mBoundsInScreen.bottom = parcel.readInt();
2600        mBoundsInScreen.left = parcel.readInt();
2601        mBoundsInScreen.right = parcel.readInt();
2602
2603        final int actionCount = parcel.readInt();
2604        if (actionCount > 0) {
2605            final int legacyStandardActions = parcel.readInt();
2606            addLegacyStandardActions(legacyStandardActions);
2607            final int nonLegacyActionCount = actionCount - Integer.bitCount(legacyStandardActions);
2608            for (int i = 0; i < nonLegacyActionCount; i++) {
2609                AccessibilityAction action = new AccessibilityAction(
2610                        parcel.readInt(), parcel.readCharSequence());
2611                addAction(action);
2612            }
2613        }
2614
2615        mMovementGranularities = parcel.readInt();
2616
2617        mBooleanProperties = parcel.readInt();
2618
2619        mPackageName = parcel.readCharSequence();
2620        mClassName = parcel.readCharSequence();
2621        mText = parcel.readCharSequence();
2622        mContentDescription = parcel.readCharSequence();
2623        mViewIdResourceName = parcel.readString();
2624
2625        mTextSelectionStart = parcel.readInt();
2626        mTextSelectionEnd = parcel.readInt();
2627
2628        mInputType = parcel.readInt();
2629        mLiveRegion = parcel.readInt();
2630
2631        if (parcel.readInt() == 1) {
2632            getExtras().putAll(parcel.readBundle());
2633        }
2634
2635        if (parcel.readInt() == 1) {
2636            mRangeInfo = RangeInfo.obtain(
2637                    parcel.readInt(),
2638                    parcel.readFloat(),
2639                    parcel.readFloat(),
2640                    parcel.readFloat());
2641        }
2642
2643        if (parcel.readInt() == 1) {
2644            mCollectionInfo = CollectionInfo.obtain(
2645                    parcel.readInt(),
2646                    parcel.readInt(),
2647                    parcel.readInt() == 1,
2648                    parcel.readInt());
2649        }
2650
2651        if (parcel.readInt() == 1) {
2652            mCollectionItemInfo = CollectionItemInfo.obtain(
2653                    parcel.readInt(),
2654                    parcel.readInt(),
2655                    parcel.readInt(),
2656                    parcel.readInt(),
2657                    parcel.readInt() == 1,
2658                    parcel.readInt() == 1);
2659        }
2660    }
2661
2662    /**
2663     * Clears the state of this instance.
2664     */
2665    private void clear() {
2666        mSealed = false;
2667        mSourceNodeId = ROOT_NODE_ID;
2668        mParentNodeId = ROOT_NODE_ID;
2669        mLabelForId = ROOT_NODE_ID;
2670        mLabeledById = ROOT_NODE_ID;
2671        mWindowId = UNDEFINED_ITEM_ID;
2672        mConnectionId = UNDEFINED_CONNECTION_ID;
2673        mMovementGranularities = 0;
2674        if (mChildNodeIds != null) {
2675            mChildNodeIds.clear();
2676        }
2677        mBoundsInParent.set(0, 0, 0, 0);
2678        mBoundsInScreen.set(0, 0, 0, 0);
2679        mBooleanProperties = 0;
2680        mPackageName = null;
2681        mClassName = null;
2682        mText = null;
2683        mContentDescription = null;
2684        mViewIdResourceName = null;
2685        if (mActions != null) {
2686            mActions.clear();
2687        }
2688        mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
2689        mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
2690        mInputType = InputType.TYPE_NULL;
2691        mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
2692        if (mExtras != null) {
2693            mExtras.clear();
2694        }
2695        if (mRangeInfo != null) {
2696            mRangeInfo.recycle();
2697            mRangeInfo = null;
2698        }
2699        if (mCollectionInfo != null) {
2700            mCollectionInfo.recycle();
2701            mCollectionInfo = null;
2702        }
2703        if (mCollectionItemInfo != null) {
2704            mCollectionItemInfo.recycle();
2705            mCollectionItemInfo = null;
2706        }
2707    }
2708
2709    private static boolean isDefaultLegacyStandardAction(AccessibilityAction action) {
2710        return (action.getId() <= LAST_LEGACY_STANDARD_ACTION
2711                && TextUtils.isEmpty(action.getLabel()));
2712    }
2713
2714    private static AccessibilityAction getActionSingleton(int actionId) {
2715        final int actions = AccessibilityAction.sStandardActions.size();
2716        for (int i = 0; i < actions; i++) {
2717            AccessibilityAction currentAction = AccessibilityAction.sStandardActions.valueAt(i);
2718            if (actionId == currentAction.getId()) {
2719                return currentAction;
2720            }
2721        }
2722
2723        return null;
2724    }
2725
2726    private void addLegacyStandardActions(int actionMask) {
2727        int remainingIds = actionMask;
2728        while (remainingIds > 0) {
2729            final int id = 1 << Integer.numberOfTrailingZeros(remainingIds);
2730            remainingIds &= ~id;
2731            AccessibilityAction action = getActionSingleton(id);
2732            addAction(action);
2733        }
2734    }
2735
2736    /**
2737     * Gets the human readable action symbolic name.
2738     *
2739     * @param action The action.
2740     * @return The symbolic name.
2741     */
2742    private static String getActionSymbolicName(int action) {
2743        switch (action) {
2744            case ACTION_FOCUS:
2745                return "ACTION_FOCUS";
2746            case ACTION_CLEAR_FOCUS:
2747                return "ACTION_CLEAR_FOCUS";
2748            case ACTION_SELECT:
2749                return "ACTION_SELECT";
2750            case ACTION_CLEAR_SELECTION:
2751                return "ACTION_CLEAR_SELECTION";
2752            case ACTION_CLICK:
2753                return "ACTION_CLICK";
2754            case ACTION_LONG_CLICK:
2755                return "ACTION_LONG_CLICK";
2756            case ACTION_ACCESSIBILITY_FOCUS:
2757                return "ACTION_ACCESSIBILITY_FOCUS";
2758            case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
2759                return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
2760            case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
2761                return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
2762            case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
2763                return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
2764            case ACTION_NEXT_HTML_ELEMENT:
2765                return "ACTION_NEXT_HTML_ELEMENT";
2766            case ACTION_PREVIOUS_HTML_ELEMENT:
2767                return "ACTION_PREVIOUS_HTML_ELEMENT";
2768            case ACTION_SCROLL_FORWARD:
2769                return "ACTION_SCROLL_FORWARD";
2770            case ACTION_SCROLL_BACKWARD:
2771                return "ACTION_SCROLL_BACKWARD";
2772            case ACTION_CUT:
2773                return "ACTION_CUT";
2774            case ACTION_COPY:
2775                return "ACTION_COPY";
2776            case ACTION_PASTE:
2777                return "ACTION_PASTE";
2778            case ACTION_SET_SELECTION:
2779                return "ACTION_SET_SELECTION";
2780            default:
2781                return"ACTION_UNKNOWN";
2782        }
2783    }
2784
2785    /**
2786     * Gets the human readable movement granularity symbolic name.
2787     *
2788     * @param granularity The granularity.
2789     * @return The symbolic name.
2790     */
2791    private static String getMovementGranularitySymbolicName(int granularity) {
2792        switch (granularity) {
2793            case MOVEMENT_GRANULARITY_CHARACTER:
2794                return "MOVEMENT_GRANULARITY_CHARACTER";
2795            case MOVEMENT_GRANULARITY_WORD:
2796                return "MOVEMENT_GRANULARITY_WORD";
2797            case MOVEMENT_GRANULARITY_LINE:
2798                return "MOVEMENT_GRANULARITY_LINE";
2799            case MOVEMENT_GRANULARITY_PARAGRAPH:
2800                return "MOVEMENT_GRANULARITY_PARAGRAPH";
2801            case MOVEMENT_GRANULARITY_PAGE:
2802                return "MOVEMENT_GRANULARITY_PAGE";
2803            default:
2804                throw new IllegalArgumentException("Unknown movement granularity: " + granularity);
2805        }
2806    }
2807
2808    private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
2809        return (mWindowId != UNDEFINED_ITEM_ID
2810                && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID
2811                && mConnectionId != UNDEFINED_CONNECTION_ID);
2812    }
2813
2814    @Override
2815    public boolean equals(Object object) {
2816        if (this == object) {
2817            return true;
2818        }
2819        if (object == null) {
2820            return false;
2821        }
2822        if (getClass() != object.getClass()) {
2823            return false;
2824        }
2825        AccessibilityNodeInfo other = (AccessibilityNodeInfo) object;
2826        if (mSourceNodeId != other.mSourceNodeId) {
2827            return false;
2828        }
2829        if (mWindowId != other.mWindowId) {
2830            return false;
2831        }
2832        return true;
2833    }
2834
2835    @Override
2836    public int hashCode() {
2837        final int prime = 31;
2838        int result = 1;
2839        result = prime * result + getAccessibilityViewId(mSourceNodeId);
2840        result = prime * result + getVirtualDescendantId(mSourceNodeId);
2841        result = prime * result + mWindowId;
2842        return result;
2843    }
2844
2845    @Override
2846    public String toString() {
2847        StringBuilder builder = new StringBuilder();
2848        builder.append(super.toString());
2849
2850        if (DEBUG) {
2851            builder.append("; sourceNodeId: " + mSourceNodeId);
2852            builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
2853            builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
2854            builder.append("; mParentNodeId: " + mParentNodeId);
2855
2856            int granularities = mMovementGranularities;
2857            builder.append("; MovementGranularities: [");
2858            while (granularities != 0) {
2859                final int granularity = 1 << Integer.numberOfTrailingZeros(granularities);
2860                granularities &= ~granularity;
2861                builder.append(getMovementGranularitySymbolicName(granularity));
2862                if (granularities != 0) {
2863                    builder.append(", ");
2864                }
2865            }
2866            builder.append("]");
2867
2868            builder.append("; childAccessibilityIds: [");
2869            final LongArray childIds = mChildNodeIds;
2870            if (childIds != null) {
2871                for (int i = 0, count = childIds.size(); i < count; i++) {
2872                    builder.append(childIds.get(i));
2873                    if (i < count - 1) {
2874                        builder.append(", ");
2875                    }
2876                }
2877            }
2878            builder.append("]");
2879        }
2880
2881        builder.append("; boundsInParent: " + mBoundsInParent);
2882        builder.append("; boundsInScreen: " + mBoundsInScreen);
2883
2884        builder.append("; packageName: ").append(mPackageName);
2885        builder.append("; className: ").append(mClassName);
2886        builder.append("; text: ").append(mText);
2887        builder.append("; contentDescription: ").append(mContentDescription);
2888        builder.append("; viewIdResName: ").append(mViewIdResourceName);
2889
2890        builder.append("; checkable: ").append(isCheckable());
2891        builder.append("; checked: ").append(isChecked());
2892        builder.append("; focusable: ").append(isFocusable());
2893        builder.append("; focused: ").append(isFocused());
2894        builder.append("; selected: ").append(isSelected());
2895        builder.append("; clickable: ").append(isClickable());
2896        builder.append("; longClickable: ").append(isLongClickable());
2897        builder.append("; enabled: ").append(isEnabled());
2898        builder.append("; password: ").append(isPassword());
2899        builder.append("; scrollable: ").append(isScrollable());
2900        builder.append("; actions: ").append(mActions);
2901
2902        return builder.toString();
2903    }
2904
2905    /**
2906     * A class defining an action that can be performed on an {@link AccessibilityNodeInfo}.
2907     * Each action has a unique id that is mandatory and optional data.
2908     * <p>
2909     * There are three categories of actions:
2910     * <ul>
2911     * <li><strong>Standard actions</strong> - These are actions that are reported and
2912     * handled by the standard UI widgets in the platform. For each standard action
2913     * there is a static constant defined in this class, e.g. {@link #ACTION_FOCUS}.
2914     * </li>
2915     * <li><strong>Custom actions action</strong> - These are actions that are reported
2916     * and handled by custom widgets. i.e. ones that are not part of the UI toolkit. For
2917     * example, an application may define a custom action for clearing the user history.
2918     * </li>
2919     * <li><strong>Overriden standard actions</strong> - These are actions that override
2920     * standard actions to customize them. For example, an app may add a label to the
2921     * standard click action to announce that this action clears browsing history.
2922     * </ul>
2923     * </p>
2924     */
2925    public static final class AccessibilityAction {
2926
2927        /**
2928         * Action that gives input focus to the node.
2929         */
2930        public static final AccessibilityAction ACTION_FOCUS =
2931                new AccessibilityAction(
2932                        AccessibilityNodeInfo.ACTION_FOCUS, null);
2933
2934        /**
2935         * Action that clears input focus of the node.
2936         */
2937        public static final AccessibilityAction ACTION_CLEAR_FOCUS =
2938                new AccessibilityAction(
2939                        AccessibilityNodeInfo.ACTION_CLEAR_FOCUS, null);
2940
2941        /**
2942         *  Action that selects the node.
2943         */
2944        public static final AccessibilityAction ACTION_SELECT =
2945                new AccessibilityAction(
2946                        AccessibilityNodeInfo.ACTION_SELECT, null);
2947
2948        /**
2949         * Action that deselects the node.
2950         */
2951        public static final AccessibilityAction ACTION_CLEAR_SELECTION =
2952                new AccessibilityAction(
2953                        AccessibilityNodeInfo.ACTION_CLEAR_SELECTION, null);
2954
2955        /**
2956         * Action that clicks on the node info.
2957         */
2958        public static final AccessibilityAction ACTION_CLICK =
2959                new AccessibilityAction(
2960                        AccessibilityNodeInfo.ACTION_CLICK, null);
2961
2962        /**
2963         * Action that long clicks on the node.
2964         */
2965        public static final AccessibilityAction ACTION_LONG_CLICK =
2966                new AccessibilityAction(
2967                        AccessibilityNodeInfo.ACTION_LONG_CLICK, null);
2968
2969        /**
2970         * Action that gives accessibility focus to the node.
2971         */
2972        public static final AccessibilityAction ACTION_ACCESSIBILITY_FOCUS =
2973                new AccessibilityAction(
2974                        AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
2975
2976        /**
2977         * Action that clears accessibility focus of the node.
2978         */
2979        public static final AccessibilityAction ACTION_CLEAR_ACCESSIBILITY_FOCUS =
2980                new AccessibilityAction(
2981                        AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
2982
2983        /**
2984         * Action that requests to go to the next entity in this node's text
2985         * at a given movement granularity. For example, move to the next character,
2986         * word, etc.
2987         * <p>
2988         * <strong>Arguments:</strong>
2989         * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
2990         *  AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
2991         * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
2992         *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
2993         * <strong>Example:</strong> Move to the previous character and do not extend selection.
2994         * <code><pre><p>
2995         *   Bundle arguments = new Bundle();
2996         *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
2997         *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
2998         *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
2999         *           false);
3000         *   info.performAction(AccessibilityAction.ACTION_NEXT_AT_MOVEMENT_GRANULARITY.getId(),
3001         *           arguments);
3002         * </code></pre></p>
3003         * </p>
3004         *
3005         * @see AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
3006         *  AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
3007         * @see AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
3008         *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
3009         *
3010         * @see AccessibilityNodeInfo#setMovementGranularities(int)
3011         *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
3012         * @see AccessibilityNodeInfo#getMovementGranularities()
3013         *  AccessibilityNodeInfo.getMovementGranularities()
3014         *
3015         * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_CHARACTER
3016         *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
3017         * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_WORD
3018         *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
3019         * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_LINE
3020         *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
3021         * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PARAGRAPH
3022         *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
3023         * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PAGE
3024         *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE
3025         */
3026        public static final AccessibilityAction ACTION_NEXT_AT_MOVEMENT_GRANULARITY =
3027                new AccessibilityAction(
3028                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, null);
3029
3030        /**
3031         * Action that requests to go to the previous entity in this node's text
3032         * at a given movement granularity. For example, move to the next character,
3033         * word, etc.
3034         * <p>
3035         * <strong>Arguments:</strong>
3036         * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
3037         *  AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
3038         * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
3039         *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
3040         * <strong>Example:</strong> Move to the next character and do not extend selection.
3041         * <code><pre><p>
3042         *   Bundle arguments = new Bundle();
3043         *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
3044         *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
3045         *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
3046         *           false);
3047         *   info.performAction(AccessibilityAction.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY.getId(),
3048         *           arguments);
3049         * </code></pre></p>
3050         * </p>
3051         *
3052         * @see AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
3053         *  AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
3054         * @see AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
3055         *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
3056         *
3057         * @see AccessibilityNodeInfo#setMovementGranularities(int)
3058         *   AccessibilityNodeInfo.setMovementGranularities(int)
3059         * @see AccessibilityNodeInfo#getMovementGranularities()
3060         *  AccessibilityNodeInfo.getMovementGranularities()
3061         *
3062         * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_CHARACTER
3063         *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
3064         * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_WORD
3065         *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
3066         * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_LINE
3067         *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
3068         * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PARAGRAPH
3069         *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
3070         * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PAGE
3071         *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE
3072         */
3073        public static final AccessibilityAction ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY =
3074                new AccessibilityAction(
3075                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, null);
3076
3077        /**
3078         * Action to move to the next HTML element of a given type. For example, move
3079         * to the BUTTON, INPUT, TABLE, etc.
3080         * <p>
3081         * <strong>Arguments:</strong>
3082         * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING
3083         *  AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
3084         * <strong>Example:</strong>
3085         * <code><pre><p>
3086         *   Bundle arguments = new Bundle();
3087         *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
3088         *   info.performAction(AccessibilityAction.ACTION_NEXT_HTML_ELEMENT.getId(), arguments);
3089         * </code></pre></p>
3090         * </p>
3091         */
3092        public static final AccessibilityAction ACTION_NEXT_HTML_ELEMENT =
3093                new AccessibilityAction(
3094                        AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, null);
3095
3096        /**
3097         * Action to move to the previous HTML element of a given type. For example, move
3098         * to the BUTTON, INPUT, TABLE, etc.
3099         * <p>
3100         * <strong>Arguments:</strong>
3101         * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING
3102         *  AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
3103         * <strong>Example:</strong>
3104         * <code><pre><p>
3105         *   Bundle arguments = new Bundle();
3106         *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
3107         *   info.performAction(AccessibilityAction.ACTION_PREVIOUS_HTML_ELEMENT.getId(), arguments);
3108         * </code></pre></p>
3109         * </p>
3110         */
3111        public static final AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT =
3112                new AccessibilityAction(
3113                        AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, null);
3114
3115        /**
3116         * Action to scroll the node content forward.
3117         */
3118        public static final AccessibilityAction ACTION_SCROLL_FORWARD =
3119                new AccessibilityAction(
3120                        AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, null);
3121
3122        /**
3123         * Action to scroll the node content backward.
3124         */
3125        public static final AccessibilityAction ACTION_SCROLL_BACKWARD =
3126                new AccessibilityAction(
3127                        AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD, null);
3128
3129        /**
3130         * Action to copy the current selection to the clipboard.
3131         */
3132        public static final AccessibilityAction ACTION_COPY =
3133                new AccessibilityAction(
3134                        AccessibilityNodeInfo.ACTION_COPY, null);
3135
3136        /**
3137         * Action to paste the current clipboard content.
3138         */
3139        public static final AccessibilityAction ACTION_PASTE =
3140                new AccessibilityAction(
3141                        AccessibilityNodeInfo.ACTION_PASTE, null);
3142
3143        /**
3144         * Action to cut the current selection and place it to the clipboard.
3145         */
3146        public static final AccessibilityAction ACTION_CUT =
3147                new AccessibilityAction(
3148                        AccessibilityNodeInfo.ACTION_CUT, null);
3149
3150        /**
3151         * Action to set the selection. Performing this action with no arguments
3152         * clears the selection.
3153         * <p>
3154         * <strong>Arguments:</strong>
3155         * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT
3156         *  AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT},
3157         * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT
3158         *  AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT}<br>
3159         * <strong>Example:</strong>
3160         * <code><pre><p>
3161         *   Bundle arguments = new Bundle();
3162         *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1);
3163         *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2);
3164         *   info.performAction(AccessibilityAction.ACTION_SET_SELECTION.getId(), arguments);
3165         * </code></pre></p>
3166         * </p>
3167         *
3168         * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT
3169         *  AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT
3170         * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT
3171         *  AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT
3172         */
3173        public static final AccessibilityAction ACTION_SET_SELECTION =
3174                new AccessibilityAction(
3175                        AccessibilityNodeInfo.ACTION_SET_SELECTION, null);
3176
3177        /**
3178         * Action to expand an expandable node.
3179         */
3180        public static final AccessibilityAction ACTION_EXPAND =
3181                new AccessibilityAction(
3182                        AccessibilityNodeInfo.ACTION_EXPAND, null);
3183
3184        /**
3185         * Action to collapse an expandable node.
3186         */
3187        public static final AccessibilityAction ACTION_COLLAPSE =
3188                new AccessibilityAction(
3189                        AccessibilityNodeInfo.ACTION_COLLAPSE, null);
3190
3191        /**
3192         * Action to dismiss a dismissable node.
3193         */
3194        public static final AccessibilityAction ACTION_DISMISS =
3195                new AccessibilityAction(
3196                        AccessibilityNodeInfo.ACTION_DISMISS, null);
3197
3198        /**
3199         * Action that sets the text of the node. Performing the action without argument,
3200         * using <code> null</code> or empty {@link CharSequence} will clear the text. This
3201         * action will also put the cursor at the end of text.
3202         * <p>
3203         * <strong>Arguments:</strong>
3204         * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE
3205         *  AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
3206         * <strong>Example:</strong>
3207         * <code><pre><p>
3208         *   Bundle arguments = new Bundle();
3209         *   arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
3210         *       "android");
3211         *   info.performAction(AccessibilityAction.ACTION_SET_TEXT.getId(), arguments);
3212         * </code></pre></p>
3213         */
3214        public static final AccessibilityAction ACTION_SET_TEXT =
3215                new AccessibilityAction(
3216                        AccessibilityNodeInfo.ACTION_SET_TEXT, null);
3217
3218        private static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<AccessibilityAction>();
3219        static {
3220            sStandardActions.add(ACTION_FOCUS);
3221            sStandardActions.add(ACTION_CLEAR_FOCUS);
3222            sStandardActions.add(ACTION_SELECT);
3223            sStandardActions.add(ACTION_CLEAR_SELECTION);
3224            sStandardActions.add(ACTION_CLICK);
3225            sStandardActions.add(ACTION_LONG_CLICK);
3226            sStandardActions.add(ACTION_ACCESSIBILITY_FOCUS);
3227            sStandardActions.add(ACTION_CLEAR_ACCESSIBILITY_FOCUS);
3228            sStandardActions.add(ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
3229            sStandardActions.add(ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
3230            sStandardActions.add(ACTION_NEXT_HTML_ELEMENT);
3231            sStandardActions.add(ACTION_PREVIOUS_HTML_ELEMENT);
3232            sStandardActions.add(ACTION_SCROLL_FORWARD);
3233            sStandardActions.add(ACTION_SCROLL_BACKWARD);
3234            sStandardActions.add(ACTION_COPY);
3235            sStandardActions.add(ACTION_PASTE);
3236            sStandardActions.add(ACTION_CUT);
3237            sStandardActions.add(ACTION_SET_SELECTION);
3238            sStandardActions.add(ACTION_EXPAND);
3239            sStandardActions.add(ACTION_COLLAPSE);
3240            sStandardActions.add(ACTION_DISMISS);
3241            sStandardActions.add(ACTION_SET_TEXT);
3242        }
3243
3244        private final int mActionId;
3245        private final CharSequence mLabel;
3246
3247        /**
3248         * Creates a new AccessibilityAction. For adding a standard action without a specific label,
3249         * use the static constants.
3250         *
3251         * You can also override the description for one the standard actions. Below is an example
3252         * how to override the standard click action by adding a custom label:
3253         * <pre>
3254         *   AccessibilityAction action = new AccessibilityAction(
3255         *           AccessibilityAction.ACTION_ACTION_CLICK, getLocalizedLabel());
3256         *   node.addAction(action);
3257         * </pre>
3258         *
3259         * @param actionId The id for this action. This should either be one of the
3260         *                 standard actions or a specific action for your app. In that case it is
3261         *                 required to use a resource identifier.
3262         * @param label The label for the new AccessibilityAction.
3263         */
3264        public AccessibilityAction(int actionId, @Nullable CharSequence label) {
3265            if ((actionId & ACTION_TYPE_MASK) == 0 && Integer.bitCount(actionId) > 1) {
3266                throw new IllegalArgumentException("Invalid standard action id");
3267            }
3268
3269            if ((actionId & STANDARD_NON_LEGACY_ACTION_MASK) != 0) {
3270                throw new IllegalArgumentException("action id not a resource id");
3271            }
3272
3273            mActionId = actionId;
3274            mLabel = label;
3275        }
3276
3277        /**
3278         * Gets the id for this action.
3279         *
3280         * @return The action id.
3281         */
3282        public int getId() {
3283            return mActionId;
3284        }
3285
3286        /**
3287         * Gets the label for this action. Its purpose is to describe the
3288         * action to user.
3289         *
3290         * @return The label.
3291         */
3292        public CharSequence getLabel() {
3293            return mLabel;
3294        }
3295
3296        @Override
3297        public int hashCode() {
3298            return mActionId;
3299        }
3300
3301        @Override
3302        public boolean equals(Object other) {
3303            if (other == null) {
3304                return false;
3305            }
3306
3307            if (other == this) {
3308                return true;
3309            }
3310
3311            if (getClass() != other.getClass()) {
3312                return false;
3313            }
3314
3315            return mActionId == ((AccessibilityAction)other).mActionId;
3316        }
3317
3318        @Override
3319        public String toString() {
3320            return "AccessibilityAction: " + getActionSymbolicName(mActionId) + " - " + mLabel;
3321        }
3322    }
3323
3324    /**
3325     * Class with information if a node is a range. Use
3326     * {@link RangeInfo#obtain(int, float, float, float)} to get an instance.
3327     */
3328    public static final class RangeInfo {
3329        private static final int MAX_POOL_SIZE = 10;
3330
3331        /** Range type: integer. */
3332        public static final int RANGE_TYPE_INT = 0;
3333        /** Range type: float. */
3334        public static final int RANGE_TYPE_FLOAT = 1;
3335        /** Range type: percent with values from zero to one.*/
3336        public static final int RANGE_TYPE_PERCENT = 2;
3337
3338        private static final SynchronizedPool<RangeInfo> sPool =
3339                new SynchronizedPool<AccessibilityNodeInfo.RangeInfo>(MAX_POOL_SIZE);
3340
3341        private int mType;
3342        private float mMin;
3343        private float mMax;
3344        private float mCurrent;
3345
3346        /**
3347         * Obtains a pooled instance that is a clone of another one.
3348         *
3349         * @param other The instance to clone.
3350         *
3351         * @hide
3352         */
3353        public static RangeInfo obtain(RangeInfo other) {
3354            return obtain(other.mType, other.mMin, other.mMax, other.mCurrent);
3355        }
3356
3357        /**
3358         * Obtains a pooled instance.
3359         *
3360         * @param type The type of the range.
3361         * @param min The min value.
3362         * @param max The max value.
3363         * @param current The current value.
3364         */
3365        public static RangeInfo obtain(int type, float min, float max, float current) {
3366            RangeInfo info = sPool.acquire();
3367            return (info != null) ? info : new RangeInfo(type, min, max, current);
3368        }
3369
3370        /**
3371         * Creates a new range.
3372         *
3373         * @param type The type of the range.
3374         * @param min The min value.
3375         * @param max The max value.
3376         * @param current The current value.
3377         */
3378        private RangeInfo(int type, float min, float max, float current) {
3379            mType = type;
3380            mMin = min;
3381            mMax = max;
3382            mCurrent = current;
3383        }
3384
3385        /**
3386         * Gets the range type.
3387         *
3388         * @return The range type.
3389         *
3390         * @see #RANGE_TYPE_INT
3391         * @see #RANGE_TYPE_FLOAT
3392         * @see #RANGE_TYPE_PERCENT
3393         */
3394        public int getType() {
3395            return mType;
3396        }
3397
3398        /**
3399         * Gets the min value.
3400         *
3401         * @return The min value.
3402         */
3403        public float getMin() {
3404            return mMin;
3405        }
3406
3407        /**
3408         * Gets the max value.
3409         *
3410         * @return The max value.
3411         */
3412        public float getMax() {
3413            return mMax;
3414        }
3415
3416        /**
3417         * Gets the current value.
3418         *
3419         * @return The current value.
3420         */
3421        public float getCurrent() {
3422            return mCurrent;
3423        }
3424
3425        /**
3426         * Recycles this instance.
3427         */
3428        void recycle() {
3429            clear();
3430            sPool.release(this);
3431        }
3432
3433        private void clear() {
3434            mType = 0;
3435            mMin = 0;
3436            mMax = 0;
3437            mCurrent = 0;
3438        }
3439    }
3440
3441    /**
3442     * Class with information if a node is a collection. Use
3443     * {@link CollectionInfo#obtain(int, int, boolean)} to get an instance.
3444     * <p>
3445     * A collection of items has rows and columns and may be hierarchical.
3446     * For example, a horizontal list is a collection with one column, as
3447     * many rows as the list items, and is not hierarchical; A table is a
3448     * collection with several rows, several columns, and is not hierarchical;
3449     * A vertical tree is a hierarchical collection with one column and
3450     * as many rows as the first level children.
3451     * </p>
3452     */
3453    public static final class CollectionInfo {
3454        /** Selection mode where items are not selectable. */
3455        public static final int SELECTION_MODE_NONE = 0;
3456
3457        /** Selection mode where a single item may be selected. */
3458        public static final int SELECTION_MODE_SINGLE = 1;
3459
3460        /** Selection mode where multiple items may be selected. */
3461        public static final int SELECTION_MODE_MULTIPLE = 2;
3462
3463        private static final int MAX_POOL_SIZE = 20;
3464
3465        private static final SynchronizedPool<CollectionInfo> sPool =
3466                new SynchronizedPool<CollectionInfo>(MAX_POOL_SIZE);
3467
3468        private int mRowCount;
3469        private int mColumnCount;
3470        private boolean mHierarchical;
3471        private int mSelectionMode;
3472
3473        /**
3474         * Obtains a pooled instance that is a clone of another one.
3475         *
3476         * @param other The instance to clone.
3477         * @hide
3478         */
3479        public static CollectionInfo obtain(CollectionInfo other) {
3480            return CollectionInfo.obtain(other.mRowCount, other.mColumnCount, other.mHierarchical,
3481                    other.mSelectionMode);
3482        }
3483
3484        /**
3485         * Obtains a pooled instance.
3486         *
3487         * @param rowCount The number of rows.
3488         * @param columnCount The number of columns.
3489         * @param hierarchical Whether the collection is hierarchical.
3490         */
3491        public static CollectionInfo obtain(int rowCount, int columnCount,
3492                boolean hierarchical) {
3493            return obtain(rowCount, columnCount, hierarchical, SELECTION_MODE_NONE);
3494        }
3495
3496        /**
3497         * Obtains a pooled instance.
3498         *
3499         * @param rowCount The number of rows.
3500         * @param columnCount The number of columns.
3501         * @param hierarchical Whether the collection is hierarchical.
3502         * @param selectionMode The collection's selection mode, one of:
3503         *            <ul>
3504         *            <li>{@link #SELECTION_MODE_NONE}
3505         *            <li>{@link #SELECTION_MODE_SINGLE}
3506         *            <li>{@link #SELECTION_MODE_MULTIPLE}
3507         *            </ul>
3508         */
3509        public static CollectionInfo obtain(int rowCount, int columnCount,
3510                boolean hierarchical, int selectionMode) {
3511           final CollectionInfo info = sPool.acquire();
3512            if (info == null) {
3513                return new CollectionInfo(rowCount, columnCount, hierarchical, selectionMode);
3514            }
3515
3516            info.mRowCount = rowCount;
3517            info.mColumnCount = columnCount;
3518            info.mHierarchical = hierarchical;
3519            info.mSelectionMode = selectionMode;
3520            return info;
3521        }
3522
3523        /**
3524         * Creates a new instance.
3525         *
3526         * @param rowCount The number of rows.
3527         * @param columnCount The number of columns.
3528         * @param hierarchical Whether the collection is hierarchical.
3529         * @param selectionMode The collection's selection mode.
3530         */
3531        private CollectionInfo(int rowCount, int columnCount, boolean hierarchical,
3532                int selectionMode) {
3533            mRowCount = rowCount;
3534            mColumnCount = columnCount;
3535            mHierarchical = hierarchical;
3536            mSelectionMode = selectionMode;
3537        }
3538
3539        /**
3540         * Gets the number of rows.
3541         *
3542         * @return The row count.
3543         */
3544        public int getRowCount() {
3545            return mRowCount;
3546        }
3547
3548        /**
3549         * Gets the number of columns.
3550         *
3551         * @return The column count.
3552         */
3553        public int getColumnCount() {
3554            return mColumnCount;
3555        }
3556
3557        /**
3558         * Gets if the collection is a hierarchically ordered.
3559         *
3560         * @return Whether the collection is hierarchical.
3561         */
3562        public boolean isHierarchical() {
3563            return mHierarchical;
3564        }
3565
3566        /**
3567         * Gets the collection's selection mode.
3568         *
3569         * @return The collection's selection mode, one of:
3570         *         <ul>
3571         *         <li>{@link #SELECTION_MODE_NONE}
3572         *         <li>{@link #SELECTION_MODE_SINGLE}
3573         *         <li>{@link #SELECTION_MODE_MULTIPLE}
3574         *         </ul>
3575         */
3576        public int getSelectionMode() {
3577            return mSelectionMode;
3578        }
3579
3580        /**
3581         * Recycles this instance.
3582         */
3583        void recycle() {
3584            clear();
3585            sPool.release(this);
3586        }
3587
3588        private void clear() {
3589            mRowCount = 0;
3590            mColumnCount = 0;
3591            mHierarchical = false;
3592            mSelectionMode = SELECTION_MODE_NONE;
3593        }
3594    }
3595
3596    /**
3597     * Class with information if a node is a collection item. Use
3598     * {@link CollectionItemInfo#obtain(int, int, int, int, boolean)}
3599     * to get an instance.
3600     * <p>
3601     * A collection item is contained in a collection, it starts at
3602     * a given row and column in the collection, and spans one or
3603     * more rows and columns. For example, a header of two related
3604     * table columns starts at the first row and the first column,
3605     * spans one row and two columns.
3606     * </p>
3607     */
3608    public static final class CollectionItemInfo {
3609        private static final int MAX_POOL_SIZE = 20;
3610
3611        private static final SynchronizedPool<CollectionItemInfo> sPool =
3612                new SynchronizedPool<CollectionItemInfo>(MAX_POOL_SIZE);
3613
3614        /**
3615         * Obtains a pooled instance that is a clone of another one.
3616         *
3617         * @param other The instance to clone.
3618         * @hide
3619         */
3620        public static CollectionItemInfo obtain(CollectionItemInfo other) {
3621            return CollectionItemInfo.obtain(other.mRowIndex, other.mRowSpan, other.mColumnIndex,
3622                    other.mColumnSpan, other.mHeading, other.mSelected);
3623        }
3624
3625        /**
3626         * Obtains a pooled instance.
3627         *
3628         * @param rowIndex The row index at which the item is located.
3629         * @param rowSpan The number of rows the item spans.
3630         * @param columnIndex The column index at which the item is located.
3631         * @param columnSpan The number of columns the item spans.
3632         * @param heading Whether the item is a heading.
3633         */
3634        public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
3635                int columnIndex, int columnSpan, boolean heading) {
3636            return obtain(rowIndex, rowSpan, columnIndex, columnSpan, heading, false);
3637        }
3638
3639        /**
3640         * Obtains a pooled instance.
3641         *
3642         * @param rowIndex The row index at which the item is located.
3643         * @param rowSpan The number of rows the item spans.
3644         * @param columnIndex The column index at which the item is located.
3645         * @param columnSpan The number of columns the item spans.
3646         * @param heading Whether the item is a heading.
3647         * @param selected Whether the item is selected.
3648         */
3649        public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
3650                int columnIndex, int columnSpan, boolean heading, boolean selected) {
3651            final CollectionItemInfo info = sPool.acquire();
3652            if (info == null) {
3653                return new CollectionItemInfo(
3654                        rowIndex, rowSpan, columnIndex, columnSpan, heading, selected);
3655            }
3656
3657            info.mRowIndex = rowIndex;
3658            info.mRowSpan = rowSpan;
3659            info.mColumnIndex = columnIndex;
3660            info.mColumnSpan = columnSpan;
3661            info.mHeading = heading;
3662            info.mSelected = selected;
3663            return info;
3664        }
3665
3666        private boolean mHeading;
3667        private int mColumnIndex;
3668        private int mRowIndex;
3669        private int mColumnSpan;
3670        private int mRowSpan;
3671        private boolean mSelected;
3672
3673        /**
3674         * Creates a new instance.
3675         *
3676         * @param rowIndex The row index at which the item is located.
3677         * @param rowSpan The number of rows the item spans.
3678         * @param columnIndex The column index at which the item is located.
3679         * @param columnSpan The number of columns the item spans.
3680         * @param heading Whether the item is a heading.
3681         */
3682        private CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan,
3683                boolean heading, boolean selected) {
3684            mRowIndex = rowIndex;
3685            mRowSpan = rowSpan;
3686            mColumnIndex = columnIndex;
3687            mColumnSpan = columnSpan;
3688            mHeading = heading;
3689            mSelected = selected;
3690        }
3691
3692        /**
3693         * Gets the column index at which the item is located.
3694         *
3695         * @return The column index.
3696         */
3697        public int getColumnIndex() {
3698            return mColumnIndex;
3699        }
3700
3701        /**
3702         * Gets the row index at which the item is located.
3703         *
3704         * @return The row index.
3705         */
3706        public int getRowIndex() {
3707            return mRowIndex;
3708        }
3709
3710        /**
3711         * Gets the number of columns the item spans.
3712         *
3713         * @return The column span.
3714         */
3715        public int getColumnSpan() {
3716            return mColumnSpan;
3717        }
3718
3719        /**
3720         * Gets the number of rows the item spans.
3721         *
3722         * @return The row span.
3723         */
3724        public int getRowSpan() {
3725            return mRowSpan;
3726        }
3727
3728        /**
3729         * Gets if the collection item is a heading. For example, section
3730         * heading, table header, etc.
3731         *
3732         * @return If the item is a heading.
3733         */
3734        public boolean isHeading() {
3735            return mHeading;
3736        }
3737
3738        /**
3739         * Gets if the collection item is selected.
3740         *
3741         * @return If the item is selected.
3742         */
3743        public boolean isSelected() {
3744            return mSelected;
3745        }
3746
3747        /**
3748         * Recycles this instance.
3749         */
3750        void recycle() {
3751            clear();
3752            sPool.release(this);
3753        }
3754
3755        private void clear() {
3756            mColumnIndex = 0;
3757            mColumnSpan = 0;
3758            mRowIndex = 0;
3759            mRowSpan = 0;
3760            mHeading = false;
3761            mSelected = false;
3762        }
3763    }
3764
3765    /**
3766     * @see android.os.Parcelable.Creator
3767     */
3768    public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
3769            new Parcelable.Creator<AccessibilityNodeInfo>() {
3770        @Override
3771        public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
3772            AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
3773            info.initFromParcel(parcel);
3774            return info;
3775        }
3776
3777        @Override
3778        public AccessibilityNodeInfo[] newArray(int size) {
3779            return new AccessibilityNodeInfo[size];
3780        }
3781    };
3782}
3783