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