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