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