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