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