AccessibilityNodeInfoCompat.java revision 9dede51868bbbe16aadcd65e04860bea8ea50e05
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 androidx.core.view.accessibility;
18
19import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20
21import android.graphics.Rect;
22import android.os.Build;
23import android.os.Bundle;
24import android.text.InputType;
25import android.view.View;
26import android.view.accessibility.AccessibilityNodeInfo;
27
28import androidx.annotation.NonNull;
29import androidx.annotation.Nullable;
30import androidx.annotation.RestrictTo;
31import androidx.core.accessibilityservice.AccessibilityServiceInfoCompat;
32import androidx.core.os.BuildCompat;
33import androidx.core.view.ViewCompat;
34
35import java.util.ArrayList;
36import java.util.Collections;
37import java.util.List;
38
39/**
40 * Helper for accessing {@link android.view.accessibility.AccessibilityNodeInfo} in a backwards
41 * compatible fashion.
42 */
43@SuppressWarnings("NewApi")
44public class AccessibilityNodeInfoCompat {
45
46    public static class AccessibilityActionCompat {
47
48        /**
49         * Action that gives input focus to the node.
50         */
51        public static final AccessibilityActionCompat ACTION_FOCUS =
52                new AccessibilityActionCompat(
53                        AccessibilityNodeInfoCompat.ACTION_FOCUS, null);
54
55        /**
56         * Action that clears input focus of the node.
57         */
58        public static final AccessibilityActionCompat ACTION_CLEAR_FOCUS =
59                new AccessibilityActionCompat(
60                        AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS, null);
61
62        /**
63         *  Action that selects the node.
64         */
65        public static final AccessibilityActionCompat ACTION_SELECT =
66                new AccessibilityActionCompat(
67                        AccessibilityNodeInfoCompat.ACTION_SELECT, null);
68
69        /**
70         * Action that deselects the node.
71         */
72        public static final AccessibilityActionCompat ACTION_CLEAR_SELECTION =
73                new AccessibilityActionCompat(
74                        AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION, null);
75
76        /**
77         * Action that clicks on the node info.
78         */
79        public static final AccessibilityActionCompat ACTION_CLICK =
80                new AccessibilityActionCompat(
81                        AccessibilityNodeInfoCompat.ACTION_CLICK, null);
82
83        /**
84         * Action that long clicks on the node.
85         */
86        public static final AccessibilityActionCompat ACTION_LONG_CLICK =
87                new AccessibilityActionCompat(
88                        AccessibilityNodeInfoCompat.ACTION_LONG_CLICK, null);
89
90        /**
91         * Action that gives accessibility focus to the node.
92         */
93        public static final AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS =
94                new AccessibilityActionCompat(
95                        AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null);
96
97        /**
98         * Action that clears accessibility focus of the node.
99         */
100        public static final AccessibilityActionCompat ACTION_CLEAR_ACCESSIBILITY_FOCUS =
101                new AccessibilityActionCompat(
102                        AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
103
104        /**
105         * Action that requests to go to the next entity in this node's text
106         * at a given movement granularity. For example, move to the next character,
107         * word, etc.
108         * <p>
109         * <strong>Arguments:</strong>
110         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
111         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
112         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
113         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
114         * <strong>Example:</strong> Move to the previous character and do not extend selection.
115         * <code><pre><p>
116         *   Bundle arguments = new Bundle();
117         *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
118         *           AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER);
119         *   arguments.putBoolean(
120         *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
121         *   info.performAction(
122         *           AccessibilityActionCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY.getId(),
123         *           arguments);
124         * </code></pre></p>
125         * </p>
126         *
127         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
128         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
129         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
130         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
131         *
132         * @see AccessibilityNodeInfoCompat#setMovementGranularities(int)
133         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
134         * @see AccessibilityNodeInfoCompat#getMovementGranularities()
135         *  AccessibilityNodeInfoCompat.getMovementGranularities()
136         *
137         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_CHARACTER
138         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER
139         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_WORD
140         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_WORD
141         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_LINE
142         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_LINE
143         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PARAGRAPH
144         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PARAGRAPH
145         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PAGE
146         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE
147         */
148        public static final AccessibilityActionCompat ACTION_NEXT_AT_MOVEMENT_GRANULARITY =
149                new AccessibilityActionCompat(
150                        AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, null);
151
152        /**
153         * Action that requests to go to the previous entity in this node's text
154         * at a given movement granularity. For example, move to the next character,
155         * word, etc.
156         * <p>
157         * <strong>Arguments:</strong>
158         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
159         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
160         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
161         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
162         * <strong>Example:</strong> Move to the next character and do not extend selection.
163         * <code><pre><p>
164         *   Bundle arguments = new Bundle();
165         *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
166         *           AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER);
167         *   arguments.putBoolean(
168         *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
169         *   info.performAction(
170         *           AccessibilityActionCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY.getId(),
171         *           arguments);
172         * </code></pre></p>
173         * </p>
174         *
175         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
176         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
177         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
178         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
179         *
180         * @see AccessibilityNodeInfoCompat#setMovementGranularities(int)
181         *   AccessibilityNodeInfoCompat.setMovementGranularities(int)
182         * @see AccessibilityNodeInfoCompat#getMovementGranularities()
183         *  AccessibilityNodeInfoCompat.getMovementGranularities()
184         *
185         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_CHARACTER
186         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER
187         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_WORD
188         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_WORD
189         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_LINE
190         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_LINE
191         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PARAGRAPH
192         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PARAGRAPH
193         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PAGE
194         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE
195         */
196        public static final AccessibilityActionCompat ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY =
197                new AccessibilityActionCompat(
198                        AccessibilityNodeInfoCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, null);
199
200        /**
201         * Action to move to the next HTML element of a given type. For example, move
202         * to the BUTTON, INPUT, TABLE, etc.
203         * <p>
204         * <strong>Arguments:</strong>
205         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_HTML_ELEMENT_STRING
206         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
207         * <strong>Example:</strong>
208         * <code><pre><p>
209         *   Bundle arguments = new Bundle();
210         *   arguments.putString(
211         *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
212         *   info.performAction(
213         *           AccessibilityActionCompat.ACTION_NEXT_HTML_ELEMENT.getId(), arguments);
214         * </code></pre></p>
215         * </p>
216         */
217        public static final AccessibilityActionCompat ACTION_NEXT_HTML_ELEMENT =
218                new AccessibilityActionCompat(
219                        AccessibilityNodeInfoCompat.ACTION_NEXT_HTML_ELEMENT, null);
220
221        /**
222         * Action to move to the previous HTML element of a given type. For example, move
223         * to the BUTTON, INPUT, TABLE, etc.
224         * <p>
225         * <strong>Arguments:</strong>
226         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_HTML_ELEMENT_STRING
227         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
228         * <strong>Example:</strong>
229         * <code><pre><p>
230         *   Bundle arguments = new Bundle();
231         *   arguments.putString(
232         *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
233         *   info.performAction(
234         *           AccessibilityActionCompat.ACTION_PREVIOUS_HTML_ELEMENT.getId(), arguments);
235         * </code></pre></p>
236         * </p>
237         */
238        public static final AccessibilityActionCompat ACTION_PREVIOUS_HTML_ELEMENT =
239                new AccessibilityActionCompat(
240                        AccessibilityNodeInfoCompat.ACTION_PREVIOUS_HTML_ELEMENT, null);
241
242        /**
243         * Action to scroll the node content forward.
244         */
245        public static final AccessibilityActionCompat ACTION_SCROLL_FORWARD =
246                new AccessibilityActionCompat(
247                        AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, null);
248
249        /**
250         * Action to scroll the node content backward.
251         */
252        public static final AccessibilityActionCompat ACTION_SCROLL_BACKWARD =
253                new AccessibilityActionCompat(
254                        AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, null);
255
256        /**
257         * Action to copy the current selection to the clipboard.
258         */
259        public static final AccessibilityActionCompat ACTION_COPY =
260                new AccessibilityActionCompat(
261                        AccessibilityNodeInfoCompat.ACTION_COPY, null);
262
263        /**
264         * Action to paste the current clipboard content.
265         */
266        public static final AccessibilityActionCompat ACTION_PASTE =
267                new AccessibilityActionCompat(
268                        AccessibilityNodeInfoCompat.ACTION_PASTE, null);
269
270        /**
271         * Action to cut the current selection and place it to the clipboard.
272         */
273        public static final AccessibilityActionCompat ACTION_CUT =
274                new AccessibilityActionCompat(
275                        AccessibilityNodeInfoCompat.ACTION_CUT, null);
276
277        /**
278         * Action to set the selection. Performing this action with no arguments
279         * clears the selection.
280         * <p>
281         * <strong>Arguments:</strong>
282         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_START_INT
283         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT},
284         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_END_INT
285         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT}<br>
286         * <strong>Example:</strong>
287         * <code><pre><p>
288         *   Bundle arguments = new Bundle();
289         *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT, 1);
290         *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT, 2);
291         *   info.performAction(AccessibilityActionCompat.ACTION_SET_SELECTION.getId(), arguments);
292         * </code></pre></p>
293         * </p>
294         *
295         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_START_INT
296         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT
297         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_END_INT
298         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT
299         */
300        public static final AccessibilityActionCompat ACTION_SET_SELECTION =
301                new AccessibilityActionCompat(
302                        AccessibilityNodeInfoCompat.ACTION_SET_SELECTION, null);
303
304        /**
305         * Action to expand an expandable node.
306         */
307        public static final AccessibilityActionCompat ACTION_EXPAND =
308                new AccessibilityActionCompat(
309                        AccessibilityNodeInfoCompat.ACTION_EXPAND, null);
310
311        /**
312         * Action to collapse an expandable node.
313         */
314        public static final AccessibilityActionCompat ACTION_COLLAPSE =
315                new AccessibilityActionCompat(
316                        AccessibilityNodeInfoCompat.ACTION_COLLAPSE, null);
317
318        /**
319         * Action to dismiss a dismissable node.
320         */
321        public static final AccessibilityActionCompat ACTION_DISMISS =
322                new AccessibilityActionCompat(
323                        AccessibilityNodeInfoCompat.ACTION_DISMISS, null);
324
325        /**
326         * Action that sets the text of the node. Performing the action without argument,
327         * using <code> null</code> or empty {@link CharSequence} will clear the text. This
328         * action will also put the cursor at the end of text.
329         * <p>
330         * <strong>Arguments:</strong>
331         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE
332         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
333         * <strong>Example:</strong>
334         * <code><pre><p>
335         *   Bundle arguments = new Bundle();
336         *   arguments.putCharSequence(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
337         *       "android");
338         *   info.performAction(AccessibilityActionCompat.ACTION_SET_TEXT.getId(), arguments);
339         * </code></pre></p>
340         */
341        public static final AccessibilityActionCompat ACTION_SET_TEXT =
342                new AccessibilityActionCompat(
343                        AccessibilityNodeInfoCompat.ACTION_SET_TEXT, null);
344
345        /**
346         * Action that requests the node make its bounding rectangle visible
347         * on the screen, scrolling if necessary just enough.
348         *
349         * @see View#requestRectangleOnScreen(Rect)
350         */
351        public static final AccessibilityActionCompat ACTION_SHOW_ON_SCREEN =
352                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
353                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_ON_SCREEN : null);
354
355        /**
356         * Action that scrolls the node to make the specified collection
357         * position visible on screen.
358         * <p>
359         * <strong>Arguments:</strong>
360         * <ul>
361         *     <li>{@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_ROW_INT}</li>
362         *     <li>{@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_COLUMN_INT}</li>
363         * <ul>
364         *
365         * @see AccessibilityNodeInfoCompat#getCollectionInfo()
366         */
367        public static final AccessibilityActionCompat ACTION_SCROLL_TO_POSITION =
368                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
369                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_TO_POSITION
370                        : null);
371
372        /**
373         * Action to scroll the node content up.
374         */
375        public static final AccessibilityActionCompat ACTION_SCROLL_UP =
376                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
377                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP : null);
378
379        /**
380         * Action to scroll the node content left.
381         */
382        public static final AccessibilityActionCompat ACTION_SCROLL_LEFT =
383                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
384                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_LEFT : null);
385
386        /**
387         * Action to scroll the node content down.
388         */
389        public static final AccessibilityActionCompat ACTION_SCROLL_DOWN =
390                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
391                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN : null);
392
393        /**
394         * Action to scroll the node content right.
395         */
396        public static final AccessibilityActionCompat ACTION_SCROLL_RIGHT =
397                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
398                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_RIGHT : null);
399
400        /**
401         * Action that context clicks the node.
402         */
403        public static final AccessibilityActionCompat ACTION_CONTEXT_CLICK =
404                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
405                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_CONTEXT_CLICK : null);
406
407        /**
408         * Action that sets progress between {@link  RangeInfoCompat#getMin() RangeInfo.getMin()} and
409         * {@link  RangeInfoCompat#getMax() RangeInfo.getMax()}. It should use the same value type as
410         * {@link RangeInfoCompat#getType() RangeInfo.getType()}
411         * <p>
412         * <strong>Arguments:</strong>
413         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_PROGRESS_VALUE}
414         *
415         * @see RangeInfoCompat
416         */
417        public static final AccessibilityActionCompat ACTION_SET_PROGRESS =
418                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 24
419                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS : null);
420
421        /**
422         * Action to move a window to a new location.
423         * <p>
424         * <strong>Arguments:</strong>
425         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVE_WINDOW_X}
426         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVE_WINDOW_Y}
427         */
428        public static final AccessibilityActionCompat ACTION_MOVE_WINDOW =
429                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 26
430                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_MOVE_WINDOW : null);
431
432        /**
433         * Action to show a tooltip.
434         */
435        public static final AccessibilityActionCompat ACTION_SHOW_TOOLTIP =
436                new AccessibilityActionCompat(BuildCompat.isAtLeastP()
437                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_TOOLTIP : null);
438
439        /**
440         * Action to hide a tooltip. A node should expose this action only for views that are
441         * currently showing a tooltip.
442         */
443        public static final AccessibilityActionCompat ACTION_HIDE_TOOLTIP =
444                new AccessibilityActionCompat(BuildCompat.isAtLeastP()
445                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_HIDE_TOOLTIP : null);
446
447        final Object mAction;
448
449        /**
450         * Creates a new instance.
451         *
452         * @param actionId The action id.
453         * @param label The action label.
454         */
455        public AccessibilityActionCompat(int actionId, CharSequence label) {
456            this(Build.VERSION.SDK_INT >= 21
457                    ? new AccessibilityNodeInfo.AccessibilityAction(actionId, label) : null);
458        }
459
460        AccessibilityActionCompat(Object action) {
461            mAction = action;
462        }
463
464        /**
465         * Gets the id for this action.
466         *
467         * @return The action id.
468         */
469        public int getId() {
470            if (Build.VERSION.SDK_INT >= 21) {
471                return ((AccessibilityNodeInfo.AccessibilityAction) mAction).getId();
472            } else {
473                return 0;
474            }
475        }
476
477        /**
478         * Gets the label for this action. Its purpose is to describe the
479         * action to user.
480         *
481         * @return The label.
482         */
483        public CharSequence getLabel() {
484            if (Build.VERSION.SDK_INT >= 21) {
485                return ((AccessibilityNodeInfo.AccessibilityAction) mAction).getLabel();
486            } else {
487                return null;
488            }
489        }
490    }
491
492    /**
493     * Class with information if a node is a collection.
494     * <p>
495     * A collection of items has rows and columns and may be hierarchical.
496     * For example, a horizontal list is a collection with one column, as
497     * many rows as the list items, and is not hierarchical; A table is a
498     * collection with several rows, several columns, and is not hierarchical;
499     * A vertical tree is a hierarchical collection with one column and
500     * as many rows as the first level children.
501     * </p>
502     */
503    public static class CollectionInfoCompat {
504        /** Selection mode where items are not selectable. */
505        public static final int SELECTION_MODE_NONE = 0;
506
507        /** Selection mode where a single item may be selected. */
508        public static final int SELECTION_MODE_SINGLE = 1;
509
510        /** Selection mode where multiple items may be selected. */
511        public static final int SELECTION_MODE_MULTIPLE = 2;
512
513        final Object mInfo;
514
515        /**
516         * Returns a cached instance if such is available otherwise a new one.
517         *
518         * @param rowCount The number of rows.
519         * @param columnCount The number of columns.
520         * @param hierarchical Whether the collection is hierarchical.
521         * @param selectionMode The collection's selection mode, one of:
522         *            <ul>
523         *            <li>{@link #SELECTION_MODE_NONE}
524         *            <li>{@link #SELECTION_MODE_SINGLE}
525         *            <li>{@link #SELECTION_MODE_MULTIPLE}
526         *            </ul>
527         *
528         * @return An instance.
529         */
530        public static CollectionInfoCompat obtain(int rowCount, int columnCount,
531                boolean hierarchical, int selectionMode) {
532            if (Build.VERSION.SDK_INT >= 21) {
533                return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain(
534                        rowCount, columnCount, hierarchical, selectionMode));
535            } else if (Build.VERSION.SDK_INT >= 19) {
536                return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain(
537                        rowCount, columnCount, hierarchical));
538            } else {
539                return new CollectionInfoCompat(null);
540            }
541        }
542
543        /**
544         * Returns a cached instance if such is available otherwise a new one.
545         *
546         * @param rowCount The number of rows.
547         * @param columnCount The number of columns.
548         * @param hierarchical Whether the collection is hierarchical.
549         *
550         * @return An instance.
551         */
552        public static CollectionInfoCompat obtain(int rowCount, int columnCount,
553                boolean hierarchical) {
554            if (Build.VERSION.SDK_INT >= 19) {
555                return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain(
556                        rowCount, columnCount, hierarchical));
557            } else {
558                return new CollectionInfoCompat(null);
559            }
560        }
561
562        CollectionInfoCompat(Object info) {
563            mInfo = info;
564        }
565
566        /**
567         * Gets the number of columns.
568         *
569         * @return The column count.
570         */
571        public int getColumnCount() {
572            if (Build.VERSION.SDK_INT >= 19) {
573                return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getColumnCount();
574            } else {
575                return 0;
576            }
577        }
578
579        /**
580         * Gets the number of rows.
581         *
582         * @return The row count.
583         */
584        public int getRowCount() {
585            if (Build.VERSION.SDK_INT >= 19) {
586                return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getRowCount();
587            } else {
588                return 0;
589            }
590        }
591
592        /**
593         * Gets if the collection is a hierarchically ordered.
594         *
595         * @return Whether the collection is hierarchical.
596         */
597        public boolean isHierarchical() {
598            if (Build.VERSION.SDK_INT >= 19) {
599                return ((AccessibilityNodeInfo.CollectionInfo) mInfo).isHierarchical();
600            } else {
601                return false;
602            }
603        }
604
605        /**
606         * Gets the collection's selection mode.
607         *
608         * @return The collection's selection mode, one of:
609         *         <ul>
610         *         <li>{@link #SELECTION_MODE_NONE}
611         *         <li>{@link #SELECTION_MODE_SINGLE}
612         *         <li>{@link #SELECTION_MODE_MULTIPLE}
613         *         </ul>
614         */
615        public int getSelectionMode() {
616            if (Build.VERSION.SDK_INT >= 21) {
617                return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getSelectionMode();
618            } else {
619                return 0;
620            }
621        }
622    }
623
624    /**
625     * Class with information if a node is a collection item.
626     * <p>
627     * A collection item is contained in a collection, it starts at
628     * a given row and column in the collection, and spans one or
629     * more rows and columns. For example, a header of two related
630     * table columns starts at the first row and the first column,
631     * spans one row and two columns.
632     * </p>
633     */
634    public static class CollectionItemInfoCompat {
635
636        final Object mInfo;
637
638        /**
639         * Returns a cached instance if such is available otherwise a new one.
640         *
641         * @param rowIndex The row index at which the item is located.
642         * @param rowSpan The number of rows the item spans.
643         * @param columnIndex The column index at which the item is located.
644         * @param columnSpan The number of columns the item spans.
645         * @param heading Whether the item is a heading.
646         * @param selected Whether the item is selected.
647         * @return An instance.
648         */
649        public static CollectionItemInfoCompat obtain(int rowIndex, int rowSpan,
650                int columnIndex, int columnSpan, boolean heading, boolean selected) {
651            if (Build.VERSION.SDK_INT >= 21) {
652                return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain(
653                        rowIndex, rowSpan, columnIndex, columnSpan, heading, selected));
654            } else if (Build.VERSION.SDK_INT >= 19) {
655                return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain(
656                        rowIndex, rowSpan, columnIndex, columnSpan, heading));
657            } else {
658                return new CollectionItemInfoCompat(null);
659            }
660        }
661
662        /**
663         * Returns a cached instance if such is available otherwise a new one.
664         *
665         * @param rowIndex The row index at which the item is located.
666         * @param rowSpan The number of rows the item spans.
667         * @param columnIndex The column index at which the item is located.
668         * @param columnSpan The number of columns the item spans.
669         * @param heading Whether the item is a heading.
670         * @return An instance.
671         */
672        public static CollectionItemInfoCompat obtain(int rowIndex, int rowSpan,
673                int columnIndex, int columnSpan, boolean heading) {
674            if (Build.VERSION.SDK_INT >= 19) {
675                return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain(
676                        rowIndex, rowSpan, columnIndex, columnSpan, heading));
677            } else {
678                return new CollectionItemInfoCompat(null);
679            }
680        }
681
682        CollectionItemInfoCompat(Object info) {
683            mInfo = info;
684        }
685
686        /**
687         * Gets the column index at which the item is located.
688         *
689         * @return The column index.
690         */
691        public int getColumnIndex() {
692            if (Build.VERSION.SDK_INT >= 19) {
693                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getColumnIndex();
694            } else {
695                return 0;
696            }
697        }
698
699        /**
700         * Gets the number of columns the item spans.
701         *
702         * @return The column span.
703         */
704        public int getColumnSpan() {
705            if (Build.VERSION.SDK_INT >= 19) {
706                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getColumnSpan();
707            } else {
708                return 0;
709            }
710        }
711
712        /**
713         * Gets the row index at which the item is located.
714         *
715         * @return The row index.
716         */
717        public int getRowIndex() {
718            if (Build.VERSION.SDK_INT >= 19) {
719                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getRowIndex();
720            } else {
721                return 0;
722            }
723        }
724
725        /**
726         * Gets the number of rows the item spans.
727         *
728         * @return The row span.
729         */
730        public int getRowSpan() {
731            if (Build.VERSION.SDK_INT >= 19) {
732                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getRowSpan();
733            } else {
734                return 0;
735            }
736        }
737
738        /**
739         * Gets if the collection item is a heading. For example, section
740         * heading, table header, etc.
741         *
742         * @return If the item is a heading.
743         */
744        public boolean isHeading() {
745            if (Build.VERSION.SDK_INT >= 19) {
746                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).isHeading();
747            } else {
748                return false;
749            }
750        }
751
752        /**
753         * Gets if the collection item is selected.
754         *
755         * @return If the item is selected.
756         */
757        public boolean isSelected() {
758            if (Build.VERSION.SDK_INT >= 21) {
759                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).isSelected();
760            } else {
761                return false;
762            }
763        }
764    }
765
766    /**
767     * Class with information if a node is a range.
768     */
769    public static class RangeInfoCompat {
770        /** Range type: integer. */
771        public static final int RANGE_TYPE_INT = 0;
772        /** Range type: float. */
773        public static final int RANGE_TYPE_FLOAT = 1;
774        /** Range type: percent with values from zero to one.*/
775        public static final int RANGE_TYPE_PERCENT = 2;
776
777        /**
778         * Obtains a cached instance if such is available otherwise a new one.
779         *
780         * @param type The type of the range.
781         * @param min The min value.
782         * @param max The max value.
783         * @param current The current value.
784         * @return The instance
785         */
786        public static RangeInfoCompat obtain(int type, float min, float max, float current) {
787            if (Build.VERSION.SDK_INT >= 19) {
788                return new RangeInfoCompat(
789                        AccessibilityNodeInfo.RangeInfo.obtain(type, min, max, current));
790            } else {
791                return new RangeInfoCompat(null);
792            }
793        }
794
795        final Object mInfo;
796
797        RangeInfoCompat(Object info) {
798            mInfo = info;
799        }
800
801        /**
802         * Gets the current value.
803         *
804         * @return The current value.
805         */
806        public float getCurrent() {
807            if (Build.VERSION.SDK_INT >= 19) {
808                return ((AccessibilityNodeInfo.RangeInfo) mInfo).getCurrent();
809            } else {
810                return 0;
811            }
812        }
813
814        /**
815         * Gets the max value.
816         *
817         * @return The max value.
818         */
819        public float getMax() {
820            if (Build.VERSION.SDK_INT >= 19) {
821                return ((AccessibilityNodeInfo.RangeInfo) mInfo).getMax();
822            } else {
823                return 0;
824            }
825        }
826
827        /**
828         * Gets the min value.
829         *
830         * @return The min value.
831         */
832        public float getMin() {
833            if (Build.VERSION.SDK_INT >= 19) {
834                return ((AccessibilityNodeInfo.RangeInfo) mInfo).getMin();
835            } else {
836                return 0;
837            }
838        }
839
840        /**
841         * Gets the range type.
842         *
843         * @return The range type.
844         *
845         * @see #RANGE_TYPE_INT
846         * @see #RANGE_TYPE_FLOAT
847         * @see #RANGE_TYPE_PERCENT
848         */
849        public int getType() {
850            if (Build.VERSION.SDK_INT >= 19) {
851                return ((AccessibilityNodeInfo.RangeInfo) mInfo).getType();
852            } else {
853                return RANGE_TYPE_INT;
854            }
855        }
856    }
857
858    private static final String ROLE_DESCRIPTION_KEY =
859            "AccessibilityNodeInfo.roleDescription";
860
861    private static final String PANE_TITLE_KEY =
862            "androidx.view.accessibility.AccessibilityNodeInfoCompat.PANE_TITLE_KEY";
863
864    private static final String TOOLTIP_TEXT_KEY =
865            "androidx.view.accessibility.AccessibilityNodeInfoCompat.TOOLTIP_TEXT_KEY";
866
867    private static final String HINT_TEXT_KEY =
868            "androidx.view.accessibility.AccessibilityNodeInfoCompat.HINT_TEXT_KEY";
869
870    private static final String BOOLEAN_PROPERTY_KEY =
871            "androidx.view.accessibility.AccessibilityNodeInfoCompat.BOOLEAN_PROPERTY_KEY";
872
873    // These don't line up with the internal framework constants, since they are independent
874    // and we might as well get all 32 bits of utility here.
875    private static final int BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE = 0x00000001;
876    private static final int BOOLEAN_PROPERTY_IS_HEADING = 0x00000002;
877    private static final int BOOLEAN_PROPERTY_IS_SHOWING_HINT = 0x00000004;
878
879    private final AccessibilityNodeInfo mInfo;
880
881    /**
882     *  androidx.customview.widget.ExploreByTouchHelper.HOST_ID = -1;
883     *
884     *  @hide
885     */
886    @RestrictTo(LIBRARY_GROUP)
887    public int mParentVirtualDescendantId = -1;
888
889    // Actions introduced in IceCreamSandwich
890
891    /**
892     * Action that focuses the node.
893     */
894    public static final int ACTION_FOCUS = 0x00000001;
895
896    /**
897     * Action that unfocuses the node.
898     */
899    public static final int ACTION_CLEAR_FOCUS = 0x00000002;
900
901    /**
902     * Action that selects the node.
903     */
904    public static final int ACTION_SELECT = 0x00000004;
905
906    /**
907     * Action that unselects the node.
908     */
909    public static final int ACTION_CLEAR_SELECTION = 0x00000008;
910
911    /**
912     * Action that clicks on the node info.
913     */
914    public static final int ACTION_CLICK = 0x00000010;
915
916    /**
917     * Action that long clicks on the node.
918     */
919    public static final int ACTION_LONG_CLICK = 0x00000020;
920
921    // Actions introduced in JellyBean
922
923    /**
924     * Action that gives accessibility focus to the node.
925     */
926    public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040;
927
928    /**
929     * Action that clears accessibility focus of the node.
930     */
931    public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080;
932
933    /**
934     * Action that requests to go to the next entity in this node's text
935     * at a given movement granularity. For example, move to the next character,
936     * word, etc.
937     * <p>
938     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
939     * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
940     * <strong>Example:</strong> Move to the previous character and do not extend selection.
941     * <code><pre><p>
942     *   Bundle arguments = new Bundle();
943     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
944     *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
945     *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
946     *           false);
947     *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
948     * </code></pre></p>
949     * </p>
950     *
951     * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
952     * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
953     *
954     * @see #setMovementGranularities(int)
955     * @see #getMovementGranularities()
956     *
957     * @see #MOVEMENT_GRANULARITY_CHARACTER
958     * @see #MOVEMENT_GRANULARITY_WORD
959     * @see #MOVEMENT_GRANULARITY_LINE
960     * @see #MOVEMENT_GRANULARITY_PARAGRAPH
961     * @see #MOVEMENT_GRANULARITY_PAGE
962     */
963    public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100;
964
965    /**
966     * Action that requests to go to the previous entity in this node's text
967     * at a given movement granularity. For example, move to the next character,
968     * word, etc.
969     * <p>
970     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
971     * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
972     * <strong>Example:</strong> Move to the next character and do not extend selection.
973     * <code><pre><p>
974     *   Bundle arguments = new Bundle();
975     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
976     *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
977     *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
978     *           false);
979     *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
980     *           arguments);
981     * </code></pre></p>
982     * </p>
983     *
984     * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
985     * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
986     *
987     * @see #setMovementGranularities(int)
988     * @see #getMovementGranularities()
989     *
990     * @see #MOVEMENT_GRANULARITY_CHARACTER
991     * @see #MOVEMENT_GRANULARITY_WORD
992     * @see #MOVEMENT_GRANULARITY_LINE
993     * @see #MOVEMENT_GRANULARITY_PARAGRAPH
994     * @see #MOVEMENT_GRANULARITY_PAGE
995     */
996    public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200;
997
998    /**
999     * Action to move to the next HTML element of a given type. For example, move
1000     * to the BUTTON, INPUT, TABLE, etc.
1001     * <p>
1002     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
1003     * <strong>Example:</strong>
1004     * <code><pre><p>
1005     *   Bundle arguments = new Bundle();
1006     *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
1007     *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
1008     * </code></pre></p>
1009     * </p>
1010     */
1011    public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;
1012
1013    /**
1014     * Action to move to the previous HTML element of a given type. For example, move
1015     * to the BUTTON, INPUT, TABLE, etc.
1016     * <p>
1017     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
1018     * <strong>Example:</strong>
1019     * <code><pre><p>
1020     *   Bundle arguments = new Bundle();
1021     *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
1022     *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
1023     * </code></pre></p>
1024     * </p>
1025     */
1026    public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
1027
1028    /**
1029     * Action to scroll the node content forward.
1030     */
1031    public static final int ACTION_SCROLL_FORWARD = 0x00001000;
1032
1033    /**
1034     * Action to scroll the node content backward.
1035     */
1036    public static final int ACTION_SCROLL_BACKWARD = 0x00002000;
1037
1038    // Actions introduced in JellyBeanMr2
1039
1040    /**
1041     * Action to copy the current selection to the clipboard.
1042     */
1043    public static final int ACTION_COPY = 0x00004000;
1044
1045    /**
1046     * Action to paste the current clipboard content.
1047     */
1048    public static final int ACTION_PASTE = 0x00008000;
1049
1050    /**
1051     * Action to cut the current selection and place it to the clipboard.
1052     */
1053    public static final int ACTION_CUT = 0x00010000;
1054
1055    /**
1056     * Action to set the selection. Performing this action with no arguments
1057     * clears the selection.
1058     * <p>
1059     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SELECTION_START_INT},
1060     * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br>
1061     * <strong>Example:</strong>
1062     * <code><pre><p>
1063     *   Bundle arguments = new Bundle();
1064     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1);
1065     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2);
1066     *   info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments);
1067     * </code></pre></p>
1068     * </p>
1069     *
1070     * @see #ACTION_ARGUMENT_SELECTION_START_INT
1071     * @see #ACTION_ARGUMENT_SELECTION_END_INT
1072     */
1073    public static final int ACTION_SET_SELECTION = 0x00020000;
1074
1075    /**
1076     * Action to expand an expandable node.
1077     */
1078    public static final int ACTION_EXPAND = 0x00040000;
1079
1080    /**
1081     * Action to collapse an expandable node.
1082     */
1083    public static final int ACTION_COLLAPSE = 0x00080000;
1084
1085    /**
1086     * Action to dismiss a dismissable node.
1087     */
1088    public static final int ACTION_DISMISS = 0x00100000;
1089
1090    /**
1091     * Action that sets the text of the node. Performing the action without argument, using <code>
1092     * null</code> or empty {@link CharSequence} will clear the text. This action will also put the
1093     * cursor at the end of text.
1094     * <p>
1095     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
1096     * <strong>Example:</strong>
1097     * <code><pre><p>
1098     *   Bundle arguments = new Bundle();
1099     *   arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
1100     *       "android");
1101     *   info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
1102     * </code></pre></p>
1103     */
1104    public static final int ACTION_SET_TEXT = 0x00200000;
1105
1106    // Action arguments
1107
1108    /**
1109     * Argument for which movement granularity to be used when traversing the node text.
1110     * <p>
1111     * <strong>Type:</strong> int<br>
1112     * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
1113     * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
1114     * </p>
1115     */
1116    public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
1117        "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
1118
1119    /**
1120     * Argument for which HTML element to get moving to the next/previous HTML element.
1121     * <p>
1122     * <strong>Type:</strong> String<br>
1123     * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
1124     *         {@link #ACTION_PREVIOUS_HTML_ELEMENT}
1125     * </p>
1126     */
1127    public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
1128        "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
1129
1130    /**
1131     * Argument for whether when moving at granularity to extend the selection
1132     * or to move it otherwise.
1133     * <p>
1134     * <strong>Type:</strong> boolean<br>
1135     * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
1136     * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
1137     * </p>
1138     *
1139     * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
1140     * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
1141     */
1142    public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN =
1143            "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
1144
1145    /**
1146     * Argument for specifying the selection start.
1147     * <p>
1148     * <strong>Type:</strong> int<br>
1149     * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
1150     * </p>
1151     *
1152     * @see #ACTION_SET_SELECTION
1153     */
1154    public static final String ACTION_ARGUMENT_SELECTION_START_INT =
1155            "ACTION_ARGUMENT_SELECTION_START_INT";
1156
1157    /**
1158     * Argument for specifying the selection end.
1159     * <p>
1160     * <strong>Type:</strong> int<br>
1161     * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
1162     * </p>
1163     *
1164     * @see #ACTION_SET_SELECTION
1165     */
1166    public static final String ACTION_ARGUMENT_SELECTION_END_INT =
1167            "ACTION_ARGUMENT_SELECTION_END_INT";
1168
1169    /**
1170     * Argument for specifying the text content to set
1171     * <p>
1172     * <strong>Type:</strong> CharSequence<br>
1173     * <strong>Actions:</strong> {@link #ACTION_SET_TEXT}
1174     * </p>
1175     *
1176     * @see #ACTION_SET_TEXT
1177     */
1178    public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE =
1179            "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
1180
1181    /**
1182     * Argument for specifying the collection row to make visible on screen.
1183     * <p>
1184     * <strong>Type:</strong> int<br>
1185     * <strong>Actions:</strong>
1186     * <ul>
1187     *     <li>{@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION}</li>
1188     * </ul>
1189     *
1190     * @see AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION
1191     */
1192    public static final String ACTION_ARGUMENT_ROW_INT =
1193            "android.view.accessibility.action.ARGUMENT_ROW_INT";
1194
1195    /**
1196     * Argument for specifying the collection column to make visible on screen.
1197     * <p>
1198     * <strong>Type:</strong> int<br>
1199     * <strong>Actions:</strong>
1200     * <ul>
1201     *     <li>{@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION}</li>
1202     * </ul>
1203     *
1204     * @see AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION
1205     */
1206    public static final String ACTION_ARGUMENT_COLUMN_INT =
1207            "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
1208
1209    /**
1210     * Argument for specifying the progress value to set.
1211     * <p>
1212     * <strong>Type:</strong> float<br>
1213     * <strong>Actions:</strong>
1214     * <ul>
1215     *     <li>{@link AccessibilityActionCompat#ACTION_SET_PROGRESS}</li>
1216     * </ul>
1217     *
1218     * @see AccessibilityActionCompat#ACTION_SET_PROGRESS
1219     */
1220    public static final String ACTION_ARGUMENT_PROGRESS_VALUE =
1221            "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
1222
1223    /**
1224     * Argument for specifying the x coordinate to which to move a window.
1225     * <p>
1226     * <strong>Type:</strong> int<br>
1227     * <strong>Actions:</strong>
1228     * <ul>
1229     *     <li>{@link AccessibilityActionCompat#ACTION_MOVE_WINDOW}</li>
1230     * </ul>
1231     *
1232     * @see AccessibilityActionCompat#ACTION_MOVE_WINDOW
1233     */
1234    public static final String ACTION_ARGUMENT_MOVE_WINDOW_X =
1235            "ACTION_ARGUMENT_MOVE_WINDOW_X";
1236
1237    /**
1238     * Argument for specifying the y coordinate to which to move a window.
1239     * <p>
1240     * <strong>Type:</strong> int<br>
1241     * <strong>Actions:</strong>
1242     * <ul>
1243     *     <li>{@link AccessibilityActionCompat#ACTION_MOVE_WINDOW}</li>
1244     * </ul>
1245     *
1246     * @see AccessibilityActionCompat#ACTION_MOVE_WINDOW
1247     */
1248    public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y =
1249            "ACTION_ARGUMENT_MOVE_WINDOW_Y";
1250
1251    // Focus types
1252
1253    /**
1254     * The input focus.
1255     */
1256    public static final int FOCUS_INPUT = 1;
1257
1258    /**
1259     * The accessibility focus.
1260     */
1261    public static final int FOCUS_ACCESSIBILITY = 2;
1262
1263    // Movement granularities
1264
1265    /**
1266     * Movement granularity bit for traversing the text of a node by character.
1267     */
1268    public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001;
1269
1270    /**
1271     * Movement granularity bit for traversing the text of a node by word.
1272     */
1273    public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002;
1274
1275    /**
1276     * Movement granularity bit for traversing the text of a node by line.
1277     */
1278    public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004;
1279
1280    /**
1281     * Movement granularity bit for traversing the text of a node by paragraph.
1282     */
1283    public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008;
1284
1285    /**
1286     * Movement granularity bit for traversing the text of a node by page.
1287     */
1288    public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010;
1289
1290    /**
1291     * Creates a wrapper for info implementation.
1292     *
1293     * @param object The info to wrap.
1294     * @return A wrapper for if the object is not null, null otherwise.
1295     */
1296    static AccessibilityNodeInfoCompat wrapNonNullInstance(Object object) {
1297        if (object != null) {
1298            return new AccessibilityNodeInfoCompat(object);
1299        }
1300        return null;
1301    }
1302
1303    /**
1304     * Creates a new instance wrapping an
1305     * {@link android.view.accessibility.AccessibilityNodeInfo}.
1306     *
1307     * @param info The info.
1308     *
1309     * @deprecated Use {@link #wrap(AccessibilityNodeInfo)} instead.
1310     */
1311    @Deprecated
1312    public AccessibilityNodeInfoCompat(Object info) {
1313        mInfo = (AccessibilityNodeInfo) info;
1314    }
1315
1316    private AccessibilityNodeInfoCompat(AccessibilityNodeInfo info) {
1317        mInfo = info;
1318    }
1319
1320    /**
1321     * Creates a new instance wrapping an
1322     * {@link android.view.accessibility.AccessibilityNodeInfo}.
1323     *
1324     * @param info The info.
1325     */
1326    public static AccessibilityNodeInfoCompat wrap(@NonNull AccessibilityNodeInfo info) {
1327        return new AccessibilityNodeInfoCompat(info);
1328    }
1329
1330    /**
1331     * @return The unwrapped {@link android.view.accessibility.AccessibilityNodeInfo}.
1332     */
1333    public AccessibilityNodeInfo unwrap() {
1334        return mInfo;
1335    }
1336
1337    /**
1338     * @return The wrapped {@link android.view.accessibility.AccessibilityNodeInfo}.
1339     *
1340     * @deprecated Use {@link #unwrap()} instead.
1341     */
1342    @Deprecated
1343    public Object getInfo() {
1344        return mInfo;
1345    }
1346
1347    /**
1348     * Returns a cached instance if such is available otherwise a new one and
1349     * sets the source.
1350     *
1351     * @return An instance.
1352     * @see #setSource(View)
1353     */
1354    public static AccessibilityNodeInfoCompat obtain(View source) {
1355        return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain(source));
1356    }
1357
1358    /**
1359     * Returns a cached instance if such is available otherwise a new one
1360     * and sets the source.
1361     *
1362     * @param root The root of the virtual subtree.
1363     * @param virtualDescendantId The id of the virtual descendant.
1364     * @return An instance.
1365     *
1366     * @see #setSource(View, int)
1367     */
1368    public static AccessibilityNodeInfoCompat obtain(View root, int virtualDescendantId) {
1369        if (Build.VERSION.SDK_INT >= 16) {
1370            return AccessibilityNodeInfoCompat.wrapNonNullInstance(
1371                    AccessibilityNodeInfo.obtain(root, virtualDescendantId));
1372        } else {
1373            return null;
1374        }
1375    }
1376
1377    /**
1378     * Returns a cached instance if such is available otherwise a new one.
1379     *
1380     * @return An instance.
1381     */
1382    public static AccessibilityNodeInfoCompat obtain() {
1383        return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain());
1384    }
1385
1386    /**
1387     * Returns a cached instance if such is available or a new one is create.
1388     * The returned instance is initialized from the given <code>info</code>.
1389     *
1390     * @param info The other info.
1391     * @return An instance.
1392     */
1393    public static AccessibilityNodeInfoCompat obtain(AccessibilityNodeInfoCompat info) {
1394        return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain(info.mInfo));
1395    }
1396
1397    /**
1398     * Sets the source.
1399     *
1400     * @param source The info source.
1401     */
1402    public void setSource(View source) {
1403        mInfo.setSource(source);
1404    }
1405
1406    /**
1407     * Sets the source to be a virtual descendant of the given <code>root</code>.
1408     * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
1409     * is set as the source.
1410     * <p>
1411     * A virtual descendant is an imaginary View that is reported as a part of the view
1412     * hierarchy for accessibility purposes. This enables custom views that draw complex
1413     * content to report themselves as a tree of virtual views, thus conveying their
1414     * logical structure.
1415     * </p>
1416     * <p>
1417     *   <strong>Note:</strong> Cannot be called from an
1418     *   {@link android.accessibilityservice.AccessibilityService}.
1419     *   This class is made immutable before being delivered to an AccessibilityService.
1420     * </p>
1421     *
1422     * @param root The root of the virtual subtree.
1423     * @param virtualDescendantId The id of the virtual descendant.
1424     */
1425    public void setSource(View root, int virtualDescendantId) {
1426        if (Build.VERSION.SDK_INT >= 16) {
1427            mInfo.setSource(root, virtualDescendantId);
1428        }
1429    }
1430
1431    /**
1432     * Find the view that has the specified focus type. The search starts from
1433     * the view represented by this node info.
1434     *
1435     * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
1436     *         {@link #FOCUS_ACCESSIBILITY}.
1437     * @return The node info of the focused view or null.
1438     *
1439     * @see #FOCUS_INPUT
1440     * @see #FOCUS_ACCESSIBILITY
1441     */
1442    public AccessibilityNodeInfoCompat findFocus(int focus) {
1443        if (Build.VERSION.SDK_INT >= 16) {
1444            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.findFocus(focus));
1445        } else {
1446            return null;
1447        }
1448    }
1449
1450    /**
1451     * Searches for the nearest view in the specified direction that can take
1452     * input focus.
1453     *
1454     * @param direction The direction. Can be one of:
1455     *     {@link View#FOCUS_DOWN},
1456     *     {@link View#FOCUS_UP},
1457     *     {@link View#FOCUS_LEFT},
1458     *     {@link View#FOCUS_RIGHT},
1459     *     {@link View#FOCUS_FORWARD},
1460     *     {@link View#FOCUS_BACKWARD}.
1461     *
1462     * @return The node info for the view that can take accessibility focus.
1463     */
1464    public AccessibilityNodeInfoCompat focusSearch(int direction) {
1465        if (Build.VERSION.SDK_INT >= 16) {
1466            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.focusSearch(direction));
1467        } else {
1468            return null;
1469        }
1470    }
1471
1472    /**
1473     * Gets the id of the window from which the info comes from.
1474     *
1475     * @return The window id.
1476     */
1477    public int getWindowId() {
1478        return mInfo.getWindowId();
1479    }
1480
1481    /**
1482     * Gets the number of children.
1483     *
1484     * @return The child count.
1485     */
1486    public int getChildCount() {
1487        return mInfo.getChildCount();
1488    }
1489
1490    /**
1491     * Get the child at given index.
1492     * <p>
1493     * <strong>Note:</strong> It is a client responsibility to recycle the
1494     * received info by calling {@link AccessibilityNodeInfoCompat#recycle()} to
1495     * avoid creating of multiple instances.
1496     * </p>
1497     *
1498     * @param index The child index.
1499     * @return The child node.
1500     * @throws IllegalStateException If called outside of an
1501     *             AccessibilityService.
1502     */
1503    public AccessibilityNodeInfoCompat getChild(int index) {
1504        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getChild(index));
1505    }
1506
1507    /**
1508     * Adds a child.
1509     * <p>
1510     * <strong>Note:</strong> Cannot be called from an
1511     * {@link android.accessibilityservice.AccessibilityService}. This class is
1512     * made immutable before being delivered to an AccessibilityService.
1513     * </p>
1514     *
1515     * @param child The child.
1516     * @throws IllegalStateException If called from an AccessibilityService.
1517     */
1518    public void addChild(View child) {
1519        mInfo.addChild(child);
1520    }
1521
1522    /**
1523     * Adds a virtual child which is a descendant of the given <code>root</code>.
1524     * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
1525     * is added as a child.
1526     * <p>
1527     * A virtual descendant is an imaginary View that is reported as a part of the view
1528     * hierarchy for accessibility purposes. This enables custom views that draw complex
1529     * content to report them selves as a tree of virtual views, thus conveying their
1530     * logical structure.
1531     * </p>
1532     *
1533     * @param root The root of the virtual subtree.
1534     * @param virtualDescendantId The id of the virtual child.
1535     */
1536    public void addChild(View root, int virtualDescendantId) {
1537        if (Build.VERSION.SDK_INT >= 16) {
1538            mInfo.addChild(root, virtualDescendantId);
1539        }
1540    }
1541
1542    /**
1543     * Removes a child. If the child was not previously added to the node,
1544     * calling this method has no effect.
1545     * <p>
1546     * <strong>Note:</strong> Cannot be called from an
1547     * {@link android.accessibilityservice.AccessibilityService}.
1548     * This class is made immutable before being delivered to an AccessibilityService.
1549     * </p>
1550     *
1551     * @param child The child.
1552     * @return true if the child was present
1553     *
1554     * @throws IllegalStateException If called from an AccessibilityService.
1555     */
1556    public boolean removeChild(View child) {
1557        if (Build.VERSION.SDK_INT >= 21) {
1558            return mInfo.removeChild(child);
1559        } else {
1560            return false;
1561        }
1562    }
1563
1564    /**
1565     * Removes a virtual child which is a descendant of the given
1566     * <code>root</code>. If the child was not previously added to the node,
1567     * calling this method has no effect.
1568     *
1569     * @param root The root of the virtual subtree.
1570     * @param virtualDescendantId The id of the virtual child.
1571     * @return true if the child was present
1572     * @see #addChild(View, int)
1573     */
1574    public boolean removeChild(View root, int virtualDescendantId) {
1575        if (Build.VERSION.SDK_INT >= 21) {
1576            return mInfo.removeChild(root, virtualDescendantId);
1577        } else {
1578            return false;
1579        }
1580    }
1581
1582    /**
1583     * Gets the actions that can be performed on the node.
1584     *
1585     * @return The bit mask of with actions.
1586     * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_FOCUS
1587     * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
1588     * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_SELECT
1589     * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
1590     */
1591    public int getActions() {
1592        return mInfo.getActions();
1593    }
1594
1595    /**
1596     * Adds an action that can be performed on the node.
1597     * <p>
1598     * <strong>Note:</strong> Cannot be called from an
1599     * {@link android.accessibilityservice.AccessibilityService}. This class is
1600     * made immutable before being delivered to an AccessibilityService.
1601     * </p>
1602     *
1603     * @param action The action.
1604     * @throws IllegalStateException If called from an AccessibilityService.
1605     */
1606    public void addAction(int action) {
1607        mInfo.addAction(action);
1608    }
1609
1610    /**
1611     * Adds an action that can be performed on the node.
1612     * <p>
1613     * <strong>Note:</strong> Cannot be called from an
1614     * {@link android.accessibilityservice.AccessibilityService}. This class is
1615     * made immutable before being delivered to an AccessibilityService.
1616     * </p>
1617     *
1618     * @param action The action.
1619     * @throws IllegalStateException If called from an AccessibilityService.
1620     */
1621    public void addAction(AccessibilityActionCompat action) {
1622        if (Build.VERSION.SDK_INT >= 21) {
1623            mInfo.addAction((AccessibilityNodeInfo.AccessibilityAction) action.mAction);
1624        }
1625    }
1626
1627    /**
1628     * Removes an action that can be performed on the node. If the action was
1629     * not already added to the node, calling this method has no effect.
1630     * <p>
1631     *   <strong>Note:</strong> Cannot be called from an
1632     *   {@link android.accessibilityservice.AccessibilityService}.
1633     *   This class is made immutable before being delivered to an AccessibilityService.
1634     * </p>
1635     *
1636     * @param action The action to be removed.
1637     * @return The action removed from the list of actions.
1638     *
1639     * @throws IllegalStateException If called from an AccessibilityService.
1640     */
1641    public boolean removeAction(AccessibilityActionCompat action) {
1642        if (Build.VERSION.SDK_INT >= 21) {
1643            return mInfo.removeAction((AccessibilityNodeInfo.AccessibilityAction) action.mAction);
1644        } else {
1645            return false;
1646        }
1647    }
1648
1649    /**
1650     * Performs an action on the node.
1651     * <p>
1652     * <strong>Note:</strong> An action can be performed only if the request is
1653     * made from an {@link android.accessibilityservice.AccessibilityService}.
1654     * </p>
1655     *
1656     * @param action The action to perform.
1657     * @return True if the action was performed.
1658     * @throws IllegalStateException If called outside of an
1659     *             AccessibilityService.
1660     */
1661    public boolean performAction(int action) {
1662        return mInfo.performAction(action);
1663    }
1664
1665    /**
1666     * Performs an action on the node.
1667     * <p>
1668     *   <strong>Note:</strong> An action can be performed only if the request is made
1669     *   from an {@link android.accessibilityservice.AccessibilityService}.
1670     * </p>
1671     *
1672     * @param action The action to perform.
1673     * @param arguments A bundle with additional arguments.
1674     * @return True if the action was performed.
1675     *
1676     * @throws IllegalStateException If called outside of an AccessibilityService.
1677     */
1678    public boolean performAction(int action, Bundle arguments) {
1679        if (Build.VERSION.SDK_INT >= 16) {
1680            return mInfo.performAction(action, arguments);
1681        } else {
1682            return false;
1683        }
1684    }
1685
1686    /**
1687     * Sets the movement granularities for traversing the text of this node.
1688     * <p>
1689     *   <strong>Note:</strong> Cannot be called from an
1690     *   {@link android.accessibilityservice.AccessibilityService}.
1691     *   This class is made immutable before being delivered to an AccessibilityService.
1692     * </p>
1693     *
1694     * @param granularities The bit mask with granularities.
1695     *
1696     * @throws IllegalStateException If called from an AccessibilityService.
1697     */
1698    public void setMovementGranularities(int granularities) {
1699        if (Build.VERSION.SDK_INT >= 16) {
1700            mInfo.setMovementGranularities(granularities);
1701        }
1702    }
1703
1704    /**
1705     * Gets the movement granularities for traversing the text of this node.
1706     *
1707     * @return The bit mask with granularities.
1708     */
1709    public int getMovementGranularities() {
1710        if (Build.VERSION.SDK_INT >= 16) {
1711            return mInfo.getMovementGranularities();
1712        } else {
1713            return 0;
1714        }
1715    }
1716
1717    /**
1718     * Finds {@link android.view.accessibility.AccessibilityNodeInfo}s by text. The match
1719     * is case insensitive containment. The search is relative to this info i.e. this
1720     * info is the root of the traversed tree.
1721     * <p>
1722     * <strong>Note:</strong> It is a client responsibility to recycle the
1723     * received info by calling {@link android.view.accessibility.AccessibilityNodeInfo#recycle()}
1724     * to avoid creating of multiple instances.
1725     * </p>
1726     *
1727     * @param text The searched text.
1728     * @return A list of node info.
1729     */
1730    public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(String text) {
1731        List<AccessibilityNodeInfoCompat> result = new ArrayList<AccessibilityNodeInfoCompat>();
1732        List<AccessibilityNodeInfo> infos = mInfo.findAccessibilityNodeInfosByText(text);
1733        final int infoCount = infos.size();
1734        for (int i = 0; i < infoCount; i++) {
1735            AccessibilityNodeInfo info = infos.get(i);
1736            result.add(AccessibilityNodeInfoCompat.wrap(info));
1737        }
1738        return result;
1739    }
1740
1741    /**
1742     * Gets the parent.
1743     * <p>
1744     * <strong>Note:</strong> It is a client responsibility to recycle the
1745     * received info by calling {@link android.view.accessibility.AccessibilityNodeInfo#recycle()}
1746     * to avoid creating of multiple instances.
1747     * </p>
1748     *
1749     * @return The parent.
1750     */
1751    public AccessibilityNodeInfoCompat getParent() {
1752        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getParent());
1753    }
1754
1755    /**
1756     * Sets the parent.
1757     * <p>
1758     * <strong>Note:</strong> Cannot be called from an
1759     * {@link android.accessibilityservice.AccessibilityService}. This class is
1760     * made immutable before being delivered to an AccessibilityService.
1761     * </p>
1762     *
1763     * @param parent The parent.
1764     * @throws IllegalStateException If called from an AccessibilityService.
1765     */
1766    public void setParent(View parent) {
1767        mInfo.setParent(parent);
1768    }
1769
1770    /**
1771     * Sets the parent to be a virtual descendant of the given <code>root</code>.
1772     * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
1773     * is set as the parent.
1774     * <p>
1775     * A virtual descendant is an imaginary View that is reported as a part of the view
1776     * hierarchy for accessibility purposes. This enables custom views that draw complex
1777     * content to report them selves as a tree of virtual views, thus conveying their
1778     * logical structure.
1779     * </p>
1780     * <p>
1781     *   <strong>Note:</strong> Cannot be called from an
1782     *   {@link android.accessibilityservice.AccessibilityService}.
1783     *   This class is made immutable before being delivered to an AccessibilityService.
1784     * </p>
1785     *
1786     * @param root The root of the virtual subtree.
1787     * @param virtualDescendantId The id of the virtual descendant.
1788     */
1789    public void setParent(View root, int virtualDescendantId) {
1790        mParentVirtualDescendantId = virtualDescendantId;
1791        if (Build.VERSION.SDK_INT >= 16) {
1792            mInfo.setParent(root, virtualDescendantId);
1793        }
1794    }
1795
1796    /**
1797     * Gets the node bounds in parent coordinates.
1798     *
1799     * @param outBounds The output node bounds.
1800     */
1801    public void getBoundsInParent(Rect outBounds) {
1802        mInfo.getBoundsInParent(outBounds);
1803    }
1804
1805    /**
1806     * Sets the node bounds in parent coordinates.
1807     * <p>
1808     * <strong>Note:</strong> Cannot be called from an
1809     * {@link android.accessibilityservice.AccessibilityService}. This class is
1810     * made immutable before being delivered to an AccessibilityService.
1811     * </p>
1812     *
1813     * @param bounds The node bounds.
1814     * @throws IllegalStateException If called from an AccessibilityService.
1815     */
1816    public void setBoundsInParent(Rect bounds) {
1817        mInfo.setBoundsInParent(bounds);
1818    }
1819
1820    /**
1821     * Gets the node bounds in screen coordinates.
1822     *
1823     * @param outBounds The output node bounds.
1824     */
1825    public void getBoundsInScreen(Rect outBounds) {
1826        mInfo.getBoundsInScreen(outBounds);
1827    }
1828
1829    /**
1830     * Sets the node bounds in screen coordinates.
1831     * <p>
1832     * <strong>Note:</strong> Cannot be called from an
1833     * {@link android.accessibilityservice.AccessibilityService}. This class is
1834     * made immutable before being delivered to an AccessibilityService.
1835     * </p>
1836     *
1837     * @param bounds The node bounds.
1838     * @throws IllegalStateException If called from an AccessibilityService.
1839     */
1840    public void setBoundsInScreen(Rect bounds) {
1841        mInfo.setBoundsInScreen(bounds);
1842    }
1843
1844    /**
1845     * Gets whether this node is checkable.
1846     *
1847     * @return True if the node is checkable.
1848     */
1849    public boolean isCheckable() {
1850        return mInfo.isCheckable();
1851    }
1852
1853    /**
1854     * Sets whether this node is checkable.
1855     * <p>
1856     * <strong>Note:</strong> Cannot be called from an
1857     * {@link android.accessibilityservice.AccessibilityService}. This class is
1858     * made immutable before being delivered to an AccessibilityService.
1859     * </p>
1860     *
1861     * @param checkable True if the node is checkable.
1862     * @throws IllegalStateException If called from an AccessibilityService.
1863     */
1864    public void setCheckable(boolean checkable) {
1865        mInfo.setCheckable(checkable);
1866    }
1867
1868    /**
1869     * Gets whether this node is checked.
1870     *
1871     * @return True if the node is checked.
1872     */
1873    public boolean isChecked() {
1874        return mInfo.isChecked();
1875    }
1876
1877    /**
1878     * Sets whether this node is checked.
1879     * <p>
1880     * <strong>Note:</strong> Cannot be called from an
1881     * {@link android.accessibilityservice.AccessibilityService}. This class is
1882     * made immutable before being delivered to an AccessibilityService.
1883     * </p>
1884     *
1885     * @param checked True if the node is checked.
1886     * @throws IllegalStateException If called from an AccessibilityService.
1887     */
1888    public void setChecked(boolean checked) {
1889        mInfo.setChecked(checked);
1890    }
1891
1892    /**
1893     * Gets whether this node is focusable.
1894     *
1895     * @return True if the node is focusable.
1896     */
1897    public boolean isFocusable() {
1898        return mInfo.isFocusable();
1899    }
1900
1901    /**
1902     * Sets whether this node is focusable.
1903     * <p>
1904     * <strong>Note:</strong> Cannot be called from an
1905     * {@link android.accessibilityservice.AccessibilityService}. This class is
1906     * made immutable before being delivered to an AccessibilityService.
1907     * </p>
1908     *
1909     * @param focusable True if the node is focusable.
1910     * @throws IllegalStateException If called from an AccessibilityService.
1911     */
1912    public void setFocusable(boolean focusable) {
1913        mInfo.setFocusable(focusable);
1914    }
1915
1916    /**
1917     * Gets whether this node is focused.
1918     *
1919     * @return True if the node is focused.
1920     */
1921    public boolean isFocused() {
1922        return mInfo.isFocused();
1923    }
1924
1925    /**
1926     * Sets whether this node is focused.
1927     * <p>
1928     * <strong>Note:</strong> Cannot be called from an
1929     * {@link android.accessibilityservice.AccessibilityService}. This class is
1930     * made immutable before being delivered to an AccessibilityService.
1931     * </p>
1932     *
1933     * @param focused True if the node is focused.
1934     * @throws IllegalStateException If called from an AccessibilityService.
1935     */
1936    public void setFocused(boolean focused) {
1937        mInfo.setFocused(focused);
1938    }
1939
1940    /**
1941     * Gets whether this node is visible to the user.
1942     *
1943     * @return Whether the node is visible to the user.
1944     */
1945    public boolean isVisibleToUser() {
1946        if (Build.VERSION.SDK_INT >= 16) {
1947            return mInfo.isVisibleToUser();
1948        } else {
1949            return false;
1950        }
1951    }
1952
1953    /**
1954     * Sets whether this node is visible to the user.
1955     * <p>
1956     *   <strong>Note:</strong> Cannot be called from an
1957     *   {@link android.accessibilityservice.AccessibilityService}.
1958     *   This class is made immutable before being delivered to an AccessibilityService.
1959     * </p>
1960     *
1961     * @param visibleToUser Whether the node is visible to the user.
1962     *
1963     * @throws IllegalStateException If called from an AccessibilityService.
1964     */
1965    public void setVisibleToUser(boolean visibleToUser) {
1966        if (Build.VERSION.SDK_INT >= 16) {
1967            mInfo.setVisibleToUser(visibleToUser);
1968        }
1969    }
1970
1971    /**
1972     * Gets whether this node is accessibility focused.
1973     *
1974     * @return True if the node is accessibility focused.
1975     */
1976    public boolean isAccessibilityFocused() {
1977        if (Build.VERSION.SDK_INT >= 16) {
1978            return mInfo.isAccessibilityFocused();
1979        } else {
1980            return false;
1981        }
1982    }
1983
1984    /**
1985     * Sets whether this node is accessibility focused.
1986     * <p>
1987     *   <strong>Note:</strong> Cannot be called from an
1988     *   {@link android.accessibilityservice.AccessibilityService}.
1989     *   This class is made immutable before being delivered to an AccessibilityService.
1990     * </p>
1991     *
1992     * @param focused True if the node is accessibility focused.
1993     *
1994     * @throws IllegalStateException If called from an AccessibilityService.
1995     */
1996    public void setAccessibilityFocused(boolean focused) {
1997        if (Build.VERSION.SDK_INT >= 16) {
1998            mInfo.setAccessibilityFocused(focused);
1999        }
2000    }
2001
2002    /**
2003     * Gets whether this node is selected.
2004     *
2005     * @return True if the node is selected.
2006     */
2007    public boolean isSelected() {
2008        return mInfo.isSelected();
2009    }
2010
2011    /**
2012     * Sets whether this node is selected.
2013     * <p>
2014     * <strong>Note:</strong> Cannot be called from an
2015     * {@link android.accessibilityservice.AccessibilityService}. This class is
2016     * made immutable before being delivered to an AccessibilityService.
2017     * </p>
2018     *
2019     * @param selected True if the node is selected.
2020     * @throws IllegalStateException If called from an AccessibilityService.
2021     */
2022    public void setSelected(boolean selected) {
2023        mInfo.setSelected(selected);
2024    }
2025
2026    /**
2027     * Gets whether this node is clickable.
2028     *
2029     * @return True if the node is clickable.
2030     */
2031    public boolean isClickable() {
2032        return mInfo.isClickable();
2033    }
2034
2035    /**
2036     * Sets whether this node is clickable.
2037     * <p>
2038     * <strong>Note:</strong> Cannot be called from an
2039     * {@link android.accessibilityservice.AccessibilityService}. This class is
2040     * made immutable before being delivered to an AccessibilityService.
2041     * </p>
2042     *
2043     * @param clickable True if the node is clickable.
2044     * @throws IllegalStateException If called from an AccessibilityService.
2045     */
2046    public void setClickable(boolean clickable) {
2047        mInfo.setClickable(clickable);
2048    }
2049
2050    /**
2051     * Gets whether this node is long clickable.
2052     *
2053     * @return True if the node is long clickable.
2054     */
2055    public boolean isLongClickable() {
2056        return mInfo.isLongClickable();
2057    }
2058
2059    /**
2060     * Sets whether this node is long clickable.
2061     * <p>
2062     * <strong>Note:</strong> Cannot be called from an
2063     * {@link android.accessibilityservice.AccessibilityService}. This class is
2064     * made immutable before being delivered to an AccessibilityService.
2065     * </p>
2066     *
2067     * @param longClickable True if the node is long clickable.
2068     * @throws IllegalStateException If called from an AccessibilityService.
2069     */
2070    public void setLongClickable(boolean longClickable) {
2071        mInfo.setLongClickable(longClickable);
2072    }
2073
2074    /**
2075     * Gets whether this node is enabled.
2076     *
2077     * @return True if the node is enabled.
2078     */
2079    public boolean isEnabled() {
2080        return mInfo.isEnabled();
2081    }
2082
2083    /**
2084     * Sets whether this node is enabled.
2085     * <p>
2086     * <strong>Note:</strong> Cannot be called from an
2087     * {@link android.accessibilityservice.AccessibilityService}. This class is
2088     * made immutable before being delivered to an AccessibilityService.
2089     * </p>
2090     *
2091     * @param enabled True if the node is enabled.
2092     * @throws IllegalStateException If called from an AccessibilityService.
2093     */
2094    public void setEnabled(boolean enabled) {
2095        mInfo.setEnabled(enabled);
2096    }
2097
2098    /**
2099     * Gets whether this node is a password.
2100     *
2101     * @return True if the node is a password.
2102     */
2103    public boolean isPassword() {
2104        return mInfo.isPassword();
2105    }
2106
2107    /**
2108     * Sets whether this node is a password.
2109     * <p>
2110     * <strong>Note:</strong> Cannot be called from an
2111     * {@link android.accessibilityservice.AccessibilityService}. This class is
2112     * made immutable before being delivered to an AccessibilityService.
2113     * </p>
2114     *
2115     * @param password True if the node is a password.
2116     * @throws IllegalStateException If called from an AccessibilityService.
2117     */
2118    public void setPassword(boolean password) {
2119        mInfo.setPassword(password);
2120    }
2121
2122    /**
2123     * Gets if the node is scrollable.
2124     *
2125     * @return True if the node is scrollable, false otherwise.
2126     */
2127    public boolean isScrollable() {
2128        return mInfo.isScrollable();
2129    }
2130
2131    /**
2132     * Sets if the node is scrollable.
2133     * <p>
2134     * <strong>Note:</strong> Cannot be called from an
2135     * {@link android.accessibilityservice.AccessibilityService}. This class is
2136     * made immutable before being delivered to an AccessibilityService.
2137     * </p>
2138     *
2139     * @param scrollable True if the node is scrollable, false otherwise.
2140     * @throws IllegalStateException If called from an AccessibilityService.
2141     */
2142    public void setScrollable(boolean scrollable) {
2143        mInfo.setScrollable(scrollable);
2144    }
2145
2146    /**
2147     * Returns whether the node originates from a view considered important for accessibility.
2148     *
2149     * @return {@code true} if the node originates from a view considered important for
2150     *         accessibility, {@code false} otherwise
2151     *
2152     * @see View#isImportantForAccessibility()
2153     */
2154    public boolean isImportantForAccessibility() {
2155        if (Build.VERSION.SDK_INT >= 24) {
2156            return mInfo.isImportantForAccessibility();
2157        } else {
2158            return true;
2159        }
2160    }
2161
2162    /**
2163     * Sets whether the node is considered important for accessibility.
2164     * <p>
2165     *   <strong>Note:</strong> Cannot be called from an
2166     *   {@link android.accessibilityservice.AccessibilityService}.
2167     *   This class is made immutable before being delivered to an AccessibilityService.
2168     * </p>
2169     *
2170     * @param important {@code true} if the node is considered important for accessibility,
2171     *                  {@code false} otherwise
2172     */
2173    public void setImportantForAccessibility(boolean important) {
2174        if (Build.VERSION.SDK_INT >= 24) {
2175            mInfo.setImportantForAccessibility(important);
2176        }
2177    }
2178
2179    /**
2180     * Gets the package this node comes from.
2181     *
2182     * @return The package name.
2183     */
2184    public CharSequence getPackageName() {
2185        return mInfo.getPackageName();
2186    }
2187
2188    /**
2189     * Sets the package this node comes from.
2190     * <p>
2191     * <strong>Note:</strong> Cannot be called from an
2192     * {@link android.accessibilityservice.AccessibilityService}. This class is
2193     * made immutable before being delivered to an AccessibilityService.
2194     * </p>
2195     *
2196     * @param packageName The package name.
2197     * @throws IllegalStateException If called from an AccessibilityService.
2198     */
2199    public void setPackageName(CharSequence packageName) {
2200        mInfo.setPackageName(packageName);
2201    }
2202
2203    /**
2204     * Gets the class this node comes from.
2205     *
2206     * @return The class name.
2207     */
2208    public CharSequence getClassName() {
2209        return mInfo.getClassName();
2210    }
2211
2212    /**
2213     * Sets the class this node comes from.
2214     * <p>
2215     * <strong>Note:</strong> Cannot be called from an
2216     * {@link android.accessibilityservice.AccessibilityService}. This class is
2217     * made immutable before being delivered to an AccessibilityService.
2218     * </p>
2219     *
2220     * @param className The class name.
2221     * @throws IllegalStateException If called from an AccessibilityService.
2222     */
2223    public void setClassName(CharSequence className) {
2224        mInfo.setClassName(className);
2225    }
2226
2227    /**
2228     * Gets the text of this node.
2229     *
2230     * @return The text.
2231     */
2232    public CharSequence getText() {
2233        return mInfo.getText();
2234    }
2235
2236    /**
2237     * Sets the text of this node.
2238     * <p>
2239     * <strong>Note:</strong> Cannot be called from an
2240     * {@link android.accessibilityservice.AccessibilityService}. This class is
2241     * made immutable before being delivered to an AccessibilityService.
2242     * </p>
2243     *
2244     * @param text The text.
2245     * @throws IllegalStateException If called from an AccessibilityService.
2246     */
2247    public void setText(CharSequence text) {
2248        mInfo.setText(text);
2249    }
2250
2251    /**
2252     * Gets the content description of this node.
2253     *
2254     * @return The content description.
2255     */
2256    public CharSequence getContentDescription() {
2257        return mInfo.getContentDescription();
2258    }
2259
2260    /**
2261     * Sets the content description of this node.
2262     * <p>
2263     * <strong>Note:</strong> Cannot be called from an
2264     * {@link android.accessibilityservice.AccessibilityService}. This class is
2265     * made immutable before being delivered to an AccessibilityService.
2266     * </p>
2267     *
2268     * @param contentDescription The content description.
2269     * @throws IllegalStateException If called from an AccessibilityService.
2270     */
2271    public void setContentDescription(CharSequence contentDescription) {
2272        mInfo.setContentDescription(contentDescription);
2273    }
2274
2275    /**
2276     * Return an instance back to be reused.
2277     * <p>
2278     * <strong>Note:</strong> You must not touch the object after calling this function.
2279     *
2280     * @throws IllegalStateException If the info is already recycled.
2281     */
2282    public void recycle() {
2283        mInfo.recycle();
2284    }
2285
2286    /**
2287     * Sets the fully qualified resource name of the source view's id.
2288     *
2289     * <p>
2290     *   <strong>Note:</strong> Cannot be called from an
2291     *   {@link android.accessibilityservice.AccessibilityService}.
2292     *   This class is made immutable before being delivered to an AccessibilityService.
2293     * </p>
2294     *
2295     * @param viewId The id resource name.
2296     */
2297    public void setViewIdResourceName(String viewId) {
2298        if (Build.VERSION.SDK_INT >= 18) {
2299            mInfo.setViewIdResourceName(viewId);
2300        }
2301    }
2302
2303    /**
2304     * Gets the fully qualified resource name of the source view's id.
2305     *
2306     * <p>
2307     *   <strong>Note:</strong> The primary usage of this API is for UI test automation
2308     *   and in order to report the source view id of an {@link AccessibilityNodeInfoCompat}
2309     *   the client has to set the {@link AccessibilityServiceInfoCompat#FLAG_REPORT_VIEW_IDS}
2310     *   flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
2311     * </p>
2312     *
2313     * @return The id resource name.
2314     */
2315    public String getViewIdResourceName() {
2316        if (Build.VERSION.SDK_INT >= 18) {
2317            return mInfo.getViewIdResourceName();
2318        } else {
2319            return null;
2320        }
2321    }
2322
2323    /**
2324     * Gets the node's live region mode.
2325     * <p>
2326     * A live region is a node that contains information that is important for
2327     * the user and when it changes the user should be notified. For example,
2328     * in a login screen with a TextView that displays an "incorrect password"
2329     * notification, that view should be marked as a live region with mode
2330     * {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_POLITE}.
2331     * <p>
2332     * It is the responsibility of the accessibility service to monitor
2333     * {@link AccessibilityEventCompat#TYPE_WINDOW_CONTENT_CHANGED} events
2334     * indicating changes to live region nodes and their children.
2335     *
2336     * @return The live region mode, or
2337     *         {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_NONE} if the view is
2338     *         not a live region.
2339     * @see ViewCompat#getAccessibilityLiveRegion(View)
2340     */
2341    public int getLiveRegion() {
2342        if (Build.VERSION.SDK_INT >= 19) {
2343            return mInfo.getLiveRegion();
2344        } else {
2345            return ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE;
2346        }
2347    }
2348
2349    /**
2350     * Sets the node's live region mode.
2351     * <p>
2352     * <strong>Note:</strong> Cannot be called from an
2353     * {@link android.accessibilityservice.AccessibilityService}. This class is
2354     * made immutable before being delivered to an AccessibilityService.
2355     *
2356     * @param mode The live region mode, or
2357     *        {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_NONE} if the view is
2358     *        not a live region.
2359     * @see ViewCompat#setAccessibilityLiveRegion(View, int)
2360     */
2361    public void setLiveRegion(int mode) {
2362        if (Build.VERSION.SDK_INT >= 19) {
2363            mInfo.setLiveRegion(mode);
2364        }
2365    }
2366
2367    /**
2368     * Get the drawing order of the view corresponding it this node.
2369     * <p>
2370     * Drawing order is determined only within the node's parent, so this index is only relative
2371     * to its siblings.
2372     * <p>
2373     * In some cases, the drawing order is essentially simultaneous, so it is possible for two
2374     * siblings to return the same value. It is also possible that values will be skipped.
2375     *
2376     * @return The drawing position of the view corresponding to this node relative to its siblings.
2377     */
2378    public int getDrawingOrder() {
2379        if (Build.VERSION.SDK_INT >= 24) {
2380            return mInfo.getDrawingOrder();
2381        } else {
2382            return 0;
2383        }
2384    }
2385
2386    /**
2387     * Set the drawing order of the view corresponding it this node.
2388     *
2389     * <p>
2390     *   <strong>Note:</strong> Cannot be called from an
2391     *   {@link android.accessibilityservice.AccessibilityService}.
2392     *   This class is made immutable before being delivered to an AccessibilityService.
2393     * </p>
2394     * @param drawingOrderInParent
2395     * @throws IllegalStateException If called from an AccessibilityService.
2396     */
2397    public void setDrawingOrder(int drawingOrderInParent) {
2398        if (Build.VERSION.SDK_INT >= 24) {
2399            mInfo.setDrawingOrder(drawingOrderInParent);
2400        }
2401    }
2402
2403    /**
2404     * Gets the collection info if the node is a collection. A collection
2405     * child is always a collection item.
2406     *
2407     * @return The collection info.
2408     */
2409    public CollectionInfoCompat getCollectionInfo() {
2410        if (Build.VERSION.SDK_INT >= 19) {
2411            AccessibilityNodeInfo.CollectionInfo info = mInfo.getCollectionInfo();
2412            if (info != null) {
2413                return new CollectionInfoCompat(info);
2414            }
2415        }
2416        return null;
2417    }
2418
2419    public void setCollectionInfo(Object collectionInfo) {
2420        if (Build.VERSION.SDK_INT >= 19) {
2421            mInfo.setCollectionInfo((collectionInfo == null) ? null
2422                    : (AccessibilityNodeInfo.CollectionInfo) ((CollectionInfoCompat)
2423                            collectionInfo).mInfo);
2424        }
2425
2426    }
2427
2428    public void setCollectionItemInfo(Object collectionItemInfo) {
2429        if (Build.VERSION.SDK_INT >= 19) {
2430            mInfo.setCollectionItemInfo((collectionItemInfo == null) ? null
2431                    : (AccessibilityNodeInfo.CollectionItemInfo) ((CollectionItemInfoCompat)
2432                            collectionItemInfo).mInfo);
2433        }
2434    }
2435
2436    /**
2437     * Gets the collection item info if the node is a collection item. A collection
2438     * item is always a child of a collection.
2439     *
2440     * @return The collection item info.
2441     */
2442    public CollectionItemInfoCompat getCollectionItemInfo() {
2443        if (Build.VERSION.SDK_INT >= 19) {
2444            AccessibilityNodeInfo.CollectionItemInfo info = mInfo.getCollectionItemInfo();
2445            if (info != null) {
2446                return new CollectionItemInfoCompat(info);
2447            }
2448        }
2449        return null;
2450    }
2451
2452    /**
2453     * Gets the range info if this node is a range.
2454     *
2455     * @return The range.
2456     */
2457    public RangeInfoCompat getRangeInfo() {
2458        if (Build.VERSION.SDK_INT >= 19) {
2459            AccessibilityNodeInfo.RangeInfo info = mInfo.getRangeInfo();
2460            if (info != null) {
2461                return new RangeInfoCompat(info);
2462            }
2463        }
2464        return null;
2465    }
2466
2467    /**
2468     * Sets the range info if this node is a range.
2469     * <p>
2470     *   <strong>Note:</strong> Cannot be called from an
2471     *   {@link android.accessibilityservice.AccessibilityService}.
2472     *   This class is made immutable before being delivered to an AccessibilityService.
2473     * </p>
2474     *
2475     * @param rangeInfo The range info.
2476     */
2477    public void setRangeInfo(RangeInfoCompat rangeInfo) {
2478        if (Build.VERSION.SDK_INT >= 19) {
2479            mInfo.setRangeInfo((AccessibilityNodeInfo.RangeInfo) rangeInfo.mInfo);
2480        }
2481    }
2482
2483    /**
2484     * Gets the actions that can be performed on the node.
2485     *
2486     * @return A list of AccessibilityActions.
2487     */
2488    @SuppressWarnings("unchecked")
2489    public List<AccessibilityActionCompat> getActionList() {
2490        List<Object> actions = null;
2491        if (Build.VERSION.SDK_INT >= 21) {
2492            actions = (List<Object>) (List<?>) mInfo.getActionList();
2493        }
2494        if (actions != null) {
2495            List<AccessibilityActionCompat> result = new ArrayList<AccessibilityActionCompat>();
2496            final int actionCount = actions.size();
2497            for (int i = 0; i < actionCount; i++) {
2498                Object action = actions.get(i);
2499                result.add(new AccessibilityActionCompat(action));
2500            }
2501            return result;
2502        } else {
2503            return Collections.<AccessibilityActionCompat>emptyList();
2504        }
2505    }
2506
2507    /**
2508     * Sets if the content of this node is invalid. For example,
2509     * a date is not well-formed.
2510     * <p>
2511     *   <strong>Note:</strong> Cannot be called from an
2512     *   {@link android.accessibilityservice.AccessibilityService}.
2513     *   This class is made immutable before being delivered to an AccessibilityService.
2514     * </p>
2515     *
2516     * @param contentInvalid If the node content is invalid.
2517     */
2518    public void setContentInvalid(boolean contentInvalid) {
2519        if (Build.VERSION.SDK_INT >= 19) {
2520            mInfo.setContentInvalid(contentInvalid);
2521        }
2522    }
2523
2524    /**
2525     * Gets if the content of this node is invalid. For example,
2526     * a date is not well-formed.
2527     *
2528     * @return If the node content is invalid.
2529     */
2530    public boolean isContentInvalid() {
2531        if (Build.VERSION.SDK_INT >= 19) {
2532            return mInfo.isContentInvalid();
2533        } else {
2534            return false;
2535        }
2536    }
2537
2538    /**
2539     * Gets whether this node is context clickable.
2540     *
2541     * @return True if the node is context clickable.
2542     */
2543    public boolean isContextClickable() {
2544        if (Build.VERSION.SDK_INT >= 23) {
2545            return mInfo.isContextClickable();
2546        } else {
2547            return false;
2548        }
2549    }
2550
2551    /**
2552     * Sets whether this node is context clickable.
2553     * <p>
2554     * <strong>Note:</strong> Cannot be called from an
2555     * {@link android.accessibilityservice.AccessibilityService}. This class is made immutable
2556     * before being delivered to an AccessibilityService.
2557     * </p>
2558     *
2559     * @param contextClickable True if the node is context clickable.
2560     * @throws IllegalStateException If called from an AccessibilityService.
2561     */
2562    public void setContextClickable(boolean contextClickable) {
2563        if (Build.VERSION.SDK_INT >= 23) {
2564            mInfo.setContextClickable(contextClickable);
2565        }
2566    }
2567
2568    /**
2569     * Gets the hint text of this node. Only applies to nodes where text can be entered.
2570     *
2571     * @return The hint text.
2572     */
2573    public @Nullable CharSequence getHintText() {
2574        if (Build.VERSION.SDK_INT >= 26) {
2575            return mInfo.getHintText();
2576        } else if (Build.VERSION.SDK_INT >= 19) {
2577            return mInfo.getExtras().getCharSequence(HINT_TEXT_KEY);
2578        }
2579        return null;
2580    }
2581
2582    /**
2583     * Sets the hint text of this node. Only applies to nodes where text can be entered.
2584     * <p>This method has no effect below API 19</p>
2585     * <p>
2586     *   <strong>Note:</strong> Cannot be called from an
2587     *   {@link android.accessibilityservice.AccessibilityService}.
2588     *   This class is made immutable before being delivered to an AccessibilityService.
2589     * </p>
2590     *
2591     * @param hintText The hint text for this mode.
2592     *
2593     * @throws IllegalStateException If called from an AccessibilityService.
2594     */
2595    public void setHintText(@Nullable CharSequence hintText) {
2596        if (Build.VERSION.SDK_INT >= 26) {
2597            mInfo.setHintText(hintText);
2598        } else if (Build.VERSION.SDK_INT >= 19) {
2599            mInfo.getExtras().putCharSequence(HINT_TEXT_KEY, hintText);
2600        }
2601    }
2602
2603
2604    /**
2605     * Sets the error text of this node.
2606     * <p>
2607     *   <strong>Note:</strong> Cannot be called from an
2608     *   {@link android.accessibilityservice.AccessibilityService}.
2609     *   This class is made immutable before being delivered to an AccessibilityService.
2610     * </p>
2611     *
2612     * @param error The error text.
2613     *
2614     * @throws IllegalStateException If called from an AccessibilityService.
2615     */
2616    public void setError(CharSequence error) {
2617        if (Build.VERSION.SDK_INT >= 21) {
2618            mInfo.setError(error);
2619        }
2620    }
2621
2622    /**
2623     * Gets the error text of this node.
2624     *
2625     * @return The error text.
2626     */
2627    public CharSequence getError() {
2628        if (Build.VERSION.SDK_INT >= 21) {
2629            return mInfo.getError();
2630        } else {
2631            return null;
2632        }
2633    }
2634
2635    /**
2636     * Sets the view for which the view represented by this info serves as a
2637     * label for accessibility purposes.
2638     *
2639     * @param labeled The view for which this info serves as a label.
2640     */
2641    public void setLabelFor(View labeled) {
2642        if (Build.VERSION.SDK_INT >= 17) {
2643            mInfo.setLabelFor(labeled);
2644        }
2645    }
2646
2647    /**
2648     * Sets the view for which the view represented by this info serves as a
2649     * label for accessibility purposes. If <code>virtualDescendantId</code>
2650     * is {@link View#NO_ID} the root is set as the labeled.
2651     * <p>
2652     * A virtual descendant is an imaginary View that is reported as a part of the view
2653     * hierarchy for accessibility purposes. This enables custom views that draw complex
2654     * content to report themselves as a tree of virtual views, thus conveying their
2655     * logical structure.
2656     * </p>
2657     *
2658     * @param root The root whose virtual descendant serves as a label.
2659     * @param virtualDescendantId The id of the virtual descendant.
2660     */
2661    public void setLabelFor(View root, int virtualDescendantId) {
2662        if (Build.VERSION.SDK_INT >= 17) {
2663            mInfo.setLabelFor(root, virtualDescendantId);
2664        }
2665    }
2666
2667    /**
2668     * Gets the node info for which the view represented by this info serves as
2669     * a label for accessibility purposes.
2670     * <p>
2671     *   <strong>Note:</strong> It is a client responsibility to recycle the
2672     *     received info by calling {@link AccessibilityNodeInfoCompat#recycle()}
2673     *     to avoid creating of multiple instances.
2674     * </p>
2675     *
2676     * @return The labeled info.
2677     */
2678    public AccessibilityNodeInfoCompat getLabelFor() {
2679        if (Build.VERSION.SDK_INT >= 17) {
2680            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getLabelFor());
2681        } else {
2682            return null;
2683        }
2684    }
2685
2686    /**
2687     * Sets the view which serves as the label of the view represented by
2688     * this info for accessibility purposes.
2689     *
2690     * @param label The view that labels this node's source.
2691     */
2692    public void setLabeledBy(View label) {
2693        if (Build.VERSION.SDK_INT >= 17) {
2694            mInfo.setLabeledBy(label);
2695        }
2696    }
2697
2698    /**
2699     * Sets the view which serves as the label of the view represented by
2700     * this info for accessibility purposes. If <code>virtualDescendantId</code>
2701     * is {@link View#NO_ID} the root is set as the label.
2702     * <p>
2703     * A virtual descendant is an imaginary View that is reported as a part of the view
2704     * hierarchy for accessibility purposes. This enables custom views that draw complex
2705     * content to report themselves as a tree of virtual views, thus conveying their
2706     * logical structure.
2707     * </p>
2708     * <p>
2709     *   <strong>Note:</strong> Cannot be called from an
2710     *   {@link android.accessibilityservice.AccessibilityService}.
2711     *   This class is made immutable before being delivered to an AccessibilityService.
2712     * </p>
2713     *
2714     * @param root The root whose virtual descendant labels this node's source.
2715     * @param virtualDescendantId The id of the virtual descendant.
2716     */
2717    public void setLabeledBy(View root, int virtualDescendantId) {
2718        if (Build.VERSION.SDK_INT >= 17) {
2719            mInfo.setLabeledBy(root, virtualDescendantId);
2720        }
2721    }
2722
2723    /**
2724     * Gets the node info which serves as the label of the view represented by
2725     * this info for accessibility purposes.
2726     * <p>
2727     *   <strong>Note:</strong> It is a client responsibility to recycle the
2728     *     received info by calling {@link AccessibilityNodeInfoCompat#recycle()}
2729     *     to avoid creating of multiple instances.
2730     * </p>
2731     *
2732     * @return The label.
2733     */
2734    public AccessibilityNodeInfoCompat getLabeledBy() {
2735        if (Build.VERSION.SDK_INT >= 17) {
2736            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getLabeledBy());
2737        } else {
2738            return null;
2739        }
2740    }
2741
2742    /**
2743     * Gets if this node opens a popup or a dialog.
2744     *
2745     * @return If the the node opens a popup.
2746     */
2747    public boolean canOpenPopup() {
2748        if (Build.VERSION.SDK_INT >= 19) {
2749            return mInfo.canOpenPopup();
2750        } else {
2751            return false;
2752        }
2753    }
2754
2755    /**
2756     * Sets if this node opens a popup or a dialog.
2757     * <p>
2758     *   <strong>Note:</strong> Cannot be called from an
2759     *   {@link android.accessibilityservice.AccessibilityService}.
2760     *   This class is made immutable before being delivered to an AccessibilityService.
2761     * </p>
2762     *
2763     * @param opensPopup If the the node opens a popup.
2764     */
2765    public void setCanOpenPopup(boolean opensPopup) {
2766        if (Build.VERSION.SDK_INT >= 19) {
2767            mInfo.setCanOpenPopup(opensPopup);
2768        }
2769    }
2770
2771    /**
2772     * Finds {@link AccessibilityNodeInfoCompat}s by the fully qualified view id's resource
2773     * name where a fully qualified id is of the from "package:id/id_resource_name".
2774     * For example, if the target application's package is "foo.bar" and the id
2775     * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz".
2776     *
2777     * <p>
2778     *   <strong>Note:</strong> It is a client responsibility to recycle the
2779     *     received info by calling {@link AccessibilityNodeInfoCompat#recycle()}
2780     *     to avoid creating of multiple instances.
2781     * </p>
2782     * <p>
2783     *   <strong>Note:</strong> The primary usage of this API is for UI test automation
2784     *   and in order to report the fully qualified view id if an
2785     *   {@link AccessibilityNodeInfoCompat} the client has to set the
2786     *   {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
2787     *   flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
2788     * </p>
2789     *
2790     * @param viewId The fully qualified resource name of the view id to find.
2791     * @return A list of node info.
2792     */
2793    public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(String viewId) {
2794        if (Build.VERSION.SDK_INT >= 18) {
2795            List<AccessibilityNodeInfo> nodes = mInfo.findAccessibilityNodeInfosByViewId(viewId);
2796            List<AccessibilityNodeInfoCompat> result = new ArrayList<>();
2797            for (AccessibilityNodeInfo node : nodes) {
2798                result.add(AccessibilityNodeInfoCompat.wrap(node));
2799            }
2800            return result;
2801        } else {
2802            return Collections.emptyList();
2803        }
2804    }
2805
2806    /**
2807     * Gets an optional bundle with extra data. The bundle
2808     * is lazily created and never <code>null</code>.
2809     * <p>
2810     * <strong>Note:</strong> It is recommended to use the package
2811     * name of your application as a prefix for the keys to avoid
2812     * collisions which may confuse an accessibility service if the
2813     * same key has different meaning when emitted from different
2814     * applications.
2815     * </p>
2816     *
2817     * @return The bundle.
2818     */
2819    public Bundle getExtras() {
2820        if (Build.VERSION.SDK_INT >= 19) {
2821            return mInfo.getExtras();
2822        } else {
2823            return new Bundle();
2824        }
2825    }
2826
2827    /**
2828     * Gets the input type of the source as defined by {@link InputType}.
2829     *
2830     * @return The input type.
2831     */
2832    public int getInputType() {
2833        if (Build.VERSION.SDK_INT >= 19) {
2834            return mInfo.getInputType();
2835        } else {
2836            return InputType.TYPE_NULL;
2837        }
2838    }
2839
2840    /**
2841     * Sets the input type of the source as defined by {@link InputType}.
2842     * <p>
2843     *   <strong>Note:</strong> Cannot be called from an
2844     *   {@link android.accessibilityservice.AccessibilityService}.
2845     *   This class is made immutable before being delivered to an
2846     *   AccessibilityService.
2847     * </p>
2848     *
2849     * @param inputType The input type.
2850     *
2851     * @throws IllegalStateException If called from an AccessibilityService.
2852     */
2853    public void setInputType(int inputType) {
2854        if (Build.VERSION.SDK_INT >= 19) {
2855            mInfo.setInputType(inputType);
2856        }
2857    }
2858
2859    /**
2860     * Sets the maximum text length, or -1 for no limit.
2861     * <p>
2862     * Typically used to indicate that an editable text field has a limit on
2863     * the number of characters entered.
2864     * <p>
2865     * <strong>Note:</strong> Cannot be called from an
2866     * {@link android.accessibilityservice.AccessibilityService}.
2867     * This class is made immutable before being delivered to an AccessibilityService.
2868     *
2869     * @param max The maximum text length.
2870     * @see #getMaxTextLength()
2871     *
2872     * @throws IllegalStateException If called from an AccessibilityService.
2873     */
2874    public void setMaxTextLength(int max) {
2875        if (Build.VERSION.SDK_INT >= 21) {
2876            mInfo.setMaxTextLength(max);
2877        }
2878    }
2879
2880    /**
2881     * Returns the maximum text length for this node.
2882     *
2883     * @return The maximum text length, or -1 for no limit.
2884     * @see #setMaxTextLength(int)
2885     */
2886    public int getMaxTextLength() {
2887        if (Build.VERSION.SDK_INT >= 21) {
2888            return mInfo.getMaxTextLength();
2889        } else {
2890            return -1;
2891        }
2892    }
2893
2894    /**
2895     * Sets the text selection start and end.
2896     * <p>
2897     *   <strong>Note:</strong> Cannot be called from an
2898     *   {@link android.accessibilityservice.AccessibilityService}.
2899     *   This class is made immutable before being delivered to an AccessibilityService.
2900     * </p>
2901     *
2902     * @param start The text selection start.
2903     * @param end The text selection end.
2904     *
2905     * @throws IllegalStateException If called from an AccessibilityService.
2906     */
2907    public void setTextSelection(int start, int end) {
2908        if (Build.VERSION.SDK_INT >= 18) {
2909            mInfo.setTextSelection(start, end);
2910        }
2911    }
2912
2913    /**
2914     * Gets the text selection start.
2915     *
2916     * @return The text selection start if there is selection or -1.
2917     */
2918    public int getTextSelectionStart() {
2919        if (Build.VERSION.SDK_INT >= 18) {
2920            return mInfo.getTextSelectionStart();
2921        } else {
2922            return -1;
2923        }
2924    }
2925
2926    /**
2927     * Gets the text selection end.
2928     *
2929     * @return The text selection end if there is selection or -1.
2930     */
2931    public int getTextSelectionEnd() {
2932        if (Build.VERSION.SDK_INT >= 18) {
2933            return mInfo.getTextSelectionEnd();
2934        } else {
2935            return -1;
2936        }
2937    }
2938
2939    /**
2940     * Gets the node before which this one is visited during traversal. A screen-reader
2941     * must visit the content of this node before the content of the one it precedes.
2942     *
2943     * @return The succeeding node if such or <code>null</code>.
2944     *
2945     * @see #setTraversalBefore(android.view.View)
2946     * @see #setTraversalBefore(android.view.View, int)
2947     */
2948    public AccessibilityNodeInfoCompat getTraversalBefore() {
2949        if (Build.VERSION.SDK_INT >= 22) {
2950            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getTraversalBefore());
2951        } else {
2952            return null;
2953        }
2954    }
2955
2956    /**
2957     * Sets the view before whose node this one should be visited during traversal. A
2958     * screen-reader must visit the content of this node before the content of the one
2959     * it precedes.
2960     * <p>
2961     *   <strong>Note:</strong> Cannot be called from an
2962     *   {@link android.accessibilityservice.AccessibilityService}.
2963     *   This class is made immutable before being delivered to an AccessibilityService.
2964     * </p>
2965     *
2966     * @param view The view providing the preceding node.
2967     *
2968     * @see #getTraversalBefore()
2969     */
2970    public void setTraversalBefore(View view) {
2971        if (Build.VERSION.SDK_INT >= 22) {
2972            mInfo.setTraversalBefore(view);
2973        }
2974    }
2975
2976    /**
2977     * Sets the node before which this one is visited during traversal. A screen-reader
2978     * must visit the content of this node before the content of the one it precedes.
2979     * The successor is a virtual descendant of the given <code>root</code>. If
2980     * <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root is set
2981     * as the successor.
2982     * <p>
2983     * A virtual descendant is an imaginary View that is reported as a part of the view
2984     * hierarchy for accessibility purposes. This enables custom views that draw complex
2985     * content to report them selves as a tree of virtual views, thus conveying their
2986     * logical structure.
2987     * </p>
2988     * <p>
2989     *   <strong>Note:</strong> Cannot be called from an
2990     *   {@link android.accessibilityservice.AccessibilityService}.
2991     *   This class is made immutable before being delivered to an AccessibilityService.
2992     * </p>
2993     *
2994     * @param root The root of the virtual subtree.
2995     * @param virtualDescendantId The id of the virtual descendant.
2996     */
2997    public void setTraversalBefore(View root, int virtualDescendantId) {
2998        if (Build.VERSION.SDK_INT >= 22) {
2999            mInfo.setTraversalBefore(root, virtualDescendantId);
3000        }
3001    }
3002
3003    /**
3004     * Gets the node after which this one is visited in accessibility traversal.
3005     * A screen-reader must visit the content of the other node before the content
3006     * of this one.
3007     *
3008     * @return The succeeding node if such or <code>null</code>.
3009     *
3010     * @see #setTraversalAfter(android.view.View)
3011     * @see #setTraversalAfter(android.view.View, int)
3012     */
3013    public AccessibilityNodeInfoCompat getTraversalAfter() {
3014        if (Build.VERSION.SDK_INT >= 22) {
3015            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getTraversalAfter());
3016        } else {
3017            return null;
3018        }
3019    }
3020
3021    /**
3022     * Sets the view whose node is visited after this one in accessibility traversal.
3023     * A screen-reader must visit the content of the other node before the content
3024     * of this one.
3025     * <p>
3026     *   <strong>Note:</strong> Cannot be called from an
3027     *   {@link android.accessibilityservice.AccessibilityService}.
3028     *   This class is made immutable before being delivered to an AccessibilityService.
3029     * </p>
3030     *
3031     * @param view The previous view.
3032     *
3033     * @see #getTraversalAfter()
3034     */
3035    public void setTraversalAfter(View view) {
3036        if (Build.VERSION.SDK_INT >= 22) {
3037            mInfo.setTraversalAfter(view);
3038        }
3039    }
3040
3041    /**
3042     * Sets the node after which this one is visited in accessibility traversal.
3043     * A screen-reader must visit the content of the other node before the content
3044     * of this one. If <code>virtualDescendantId</code> equals to {@link View#NO_ID}
3045     * the root is set as the predecessor.
3046     * <p>
3047     * A virtual descendant is an imaginary View that is reported as a part of the view
3048     * hierarchy for accessibility purposes. This enables custom views that draw complex
3049     * content to report them selves as a tree of virtual views, thus conveying their
3050     * logical structure.
3051     * </p>
3052     * <p>
3053     *   <strong>Note:</strong> Cannot be called from an
3054     *   {@link android.accessibilityservice.AccessibilityService}.
3055     *   This class is made immutable before being delivered to an AccessibilityService.
3056     * </p>
3057     *
3058     * @param root The root of the virtual subtree.
3059     * @param virtualDescendantId The id of the virtual descendant.
3060     */
3061    public void setTraversalAfter(View root, int virtualDescendantId) {
3062        if (Build.VERSION.SDK_INT >= 22) {
3063            mInfo.setTraversalAfter(root, virtualDescendantId);
3064        }
3065    }
3066
3067    /**
3068     * Gets the window to which this node belongs.
3069     *
3070     * @return The window.
3071     *
3072     * @see android.accessibilityservice.AccessibilityService#getWindows()
3073     */
3074    public AccessibilityWindowInfoCompat getWindow() {
3075        if (Build.VERSION.SDK_INT >= 21) {
3076            return AccessibilityWindowInfoCompat.wrapNonNullInstance(mInfo.getWindow());
3077        } else {
3078            return null;
3079        }
3080    }
3081
3082    /**
3083     * Gets if the node can be dismissed.
3084     *
3085     * @return If the node can be dismissed.
3086     */
3087    public boolean isDismissable() {
3088        if (Build.VERSION.SDK_INT >= 19) {
3089            return mInfo.isDismissable();
3090        } else {
3091            return false;
3092        }
3093    }
3094
3095    /**
3096     * Sets if the node can be dismissed.
3097     * <p>
3098     *   <strong>Note:</strong> Cannot be called from an
3099     *   {@link android.accessibilityservice.AccessibilityService}.
3100     *   This class is made immutable before being delivered to an AccessibilityService.
3101     * </p>
3102     *
3103     * @param dismissable If the node can be dismissed.
3104     */
3105    public void setDismissable(boolean dismissable) {
3106        if (Build.VERSION.SDK_INT >= 19) {
3107            mInfo.setDismissable(dismissable);
3108        }
3109    }
3110
3111    /**
3112     * Gets if the node is editable.
3113     *
3114     * @return True if the node is editable, false otherwise.
3115     */
3116    public boolean isEditable() {
3117        if (Build.VERSION.SDK_INT >= 18) {
3118            return mInfo.isEditable();
3119        } else {
3120            return false;
3121        }
3122    }
3123
3124    /**
3125     * Sets whether this node is editable.
3126     * <p>
3127     *   <strong>Note:</strong> Cannot be called from an
3128     *   {@link android.accessibilityservice.AccessibilityService}.
3129     *   This class is made immutable before being delivered to an AccessibilityService.
3130     * </p>
3131     *
3132     * @param editable True if the node is editable.
3133     *
3134     * @throws IllegalStateException If called from an AccessibilityService.
3135     */
3136    public void setEditable(boolean editable) {
3137        if (Build.VERSION.SDK_INT >= 18) {
3138            mInfo.setEditable(editable);
3139        }
3140    }
3141
3142    /**
3143     * Gets if the node is a multi line editable text.
3144     *
3145     * @return True if the node is multi line.
3146     */
3147    public boolean isMultiLine() {
3148        if (Build.VERSION.SDK_INT >= 19) {
3149            return mInfo.isMultiLine();
3150        } else {
3151            return false;
3152        }
3153    }
3154
3155    /**
3156     * Sets if the node is a multi line editable text.
3157     * <p>
3158     *   <strong>Note:</strong> Cannot be called from an
3159     *   {@link android.accessibilityservice.AccessibilityService}.
3160     *   This class is made immutable before being delivered to an AccessibilityService.
3161     * </p>
3162     *
3163     * @param multiLine True if the node is multi line.
3164     */
3165    public void setMultiLine(boolean multiLine) {
3166        if (Build.VERSION.SDK_INT >= 19) {
3167            mInfo.setMultiLine(multiLine);
3168        }
3169    }
3170
3171    /**
3172     * Gets the tooltip text of this node.
3173     *
3174     * @return The tooltip text.
3175     */
3176    @Nullable
3177    public CharSequence getTooltipText() {
3178        if (BuildCompat.isAtLeastP()) {
3179            return mInfo.getTooltipText();
3180        } else if (Build.VERSION.SDK_INT >= 19) {
3181            return mInfo.getExtras().getCharSequence(TOOLTIP_TEXT_KEY);
3182        }
3183        return null;
3184    }
3185
3186    /**
3187     * Sets the tooltip text of this node.
3188     * <p>This method has no effect below API 19</p>
3189     * <p>
3190     *   <strong>Note:</strong> Cannot be called from an
3191     *   {@link android.accessibilityservice.AccessibilityService}.
3192     *   This class is made immutable before being delivered to an AccessibilityService.
3193     * </p>
3194     *
3195     * @param tooltipText The tooltip text.
3196     *
3197     * @throws IllegalStateException If called from an AccessibilityService.
3198     */
3199    public void setTooltipText(@Nullable CharSequence tooltipText) {
3200        if (BuildCompat.isAtLeastP()) {
3201            mInfo.setTooltipText(tooltipText);
3202        } else if (Build.VERSION.SDK_INT >= 19) {
3203            mInfo.getExtras().putCharSequence(TOOLTIP_TEXT_KEY, tooltipText);
3204        }
3205    }
3206
3207    /**
3208     * If this node represents a visually distinct region of the screen that may update separately
3209     * from the rest of the window, it is considered a pane. Set the pane title to indicate that
3210     * the node is a pane, and to provide a title for it.
3211     * <p>This method has no effect below API 19</p>
3212     * <p>
3213     *   <strong>Note:</strong> Cannot be called from an
3214     *   {@link android.accessibilityservice.AccessibilityService}.
3215     *   This class is made immutable before being delivered to an AccessibilityService.
3216     * </p>
3217     * @param paneTitle The title of the window represented by this node.
3218     */
3219    public void setPaneTitle(@Nullable CharSequence paneTitle) {
3220        if (BuildCompat.isAtLeastP()) {
3221            mInfo.setPaneTitle(paneTitle);
3222        } else if (Build.VERSION.SDK_INT >= 19) {
3223            mInfo.getExtras().putCharSequence(PANE_TITLE_KEY, paneTitle);
3224        }
3225    }
3226
3227    /**
3228     * Get the title of the pane represented by this node.
3229     *
3230     * @return The title of the pane represented by this node, or {@code null} if this node does
3231     *         not represent a pane.
3232     */
3233    public @Nullable CharSequence getPaneTitle() {
3234        if (BuildCompat.isAtLeastP()) {
3235            return mInfo.getPaneTitle();
3236        } else if (Build.VERSION.SDK_INT >= 19) {
3237            return mInfo.getExtras().getCharSequence(PANE_TITLE_KEY);
3238        }
3239        return null;
3240    }
3241
3242    /**
3243     * Returns whether the node is explicitly marked as a focusable unit by a screen reader. Note
3244     * that {@code false} indicates that it is not explicitly marked, not that the node is not
3245     * a focusable unit. Screen readers should generally use other signals, such as
3246     * {@link #isFocusable()}, or the presence of text in a node, to determine what should receive
3247     * focus.
3248     *
3249     * @return {@code true} if the node is specifically marked as a focusable unit for screen
3250     *         readers, {@code false} otherwise.
3251     */
3252    public boolean isScreenReaderFocusable() {
3253        if (BuildCompat.isAtLeastP()) {
3254            return mInfo.isScreenReaderFocusable();
3255        }
3256        return getBooleanProperty(BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE);
3257    }
3258
3259    /**
3260     * Sets whether the node should be considered a focusable unit by a screen reader.
3261     * <p>This method has no effect below API 19</p>
3262     * <p>
3263     *   <strong>Note:</strong> Cannot be called from an
3264     *   {@link android.accessibilityservice.AccessibilityService}.
3265     *   This class is made immutable before being delivered to an AccessibilityService.
3266     * </p>
3267     *
3268     * @param screenReaderFocusable {@code true} if the node is a focusable unit for screen readers,
3269     *                              {@code false} otherwise.
3270     */
3271    public void setScreenReaderFocusable(boolean screenReaderFocusable) {
3272        if (BuildCompat.isAtLeastP()) {
3273            mInfo.setScreenReaderFocusable(screenReaderFocusable);
3274        } else {
3275            setBooleanProperty(BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE, screenReaderFocusable);
3276        }
3277    }
3278
3279    /**
3280     * Returns whether the node's text represents a hint for the user to enter text. It should only
3281     * be {@code true} if the node has editable text.
3282     *
3283     * @return {@code true} if the text in the node represents a hint to the user, {@code false}
3284     * otherwise.
3285     */
3286    public boolean isShowingHintText() {
3287        if (Build.VERSION.SDK_INT >= 26) {
3288            return mInfo.isShowingHintText();
3289        }
3290        return getBooleanProperty(BOOLEAN_PROPERTY_IS_SHOWING_HINT);
3291    }
3292
3293    /**
3294     * Sets whether the node's text represents a hint for the user to enter text. It should only
3295     * be {@code true} if the node has editable text.
3296     * <p>This method has no effect below API 19</p>
3297     * <p>
3298     *   <strong>Note:</strong> Cannot be called from an
3299     *   {@link android.accessibilityservice.AccessibilityService}.
3300     *   This class is made immutable before being delivered to an AccessibilityService.
3301     * </p>
3302     *
3303     * @param showingHintText {@code true} if the text in the node represents a hint to the user,
3304     * {@code false} otherwise.
3305     */
3306    public void setShowingHintText(boolean showingHintText) {
3307        if (Build.VERSION.SDK_INT >= 26) {
3308            mInfo.setShowingHintText(showingHintText);
3309        } else {
3310            setBooleanProperty(BOOLEAN_PROPERTY_IS_SHOWING_HINT, showingHintText);
3311        }
3312    }
3313
3314    /**
3315     * Returns whether node represents a heading.
3316     *
3317     * @return {@code true} if the node is a heading, {@code false} otherwise.
3318     */
3319    public boolean isHeading() {
3320        return getBooleanProperty(BOOLEAN_PROPERTY_IS_HEADING);
3321    }
3322
3323    /**
3324     * Sets whether the node represents a heading.
3325     * <p>This method has no effect below API 19</p>
3326     * <p>
3327     *   <strong>Note:</strong> Cannot be called from an
3328     *   {@link android.accessibilityservice.AccessibilityService}.
3329     *   This class is made immutable before being delivered to an AccessibilityService.
3330     * </p>
3331     *
3332     * @param isHeading {@code true} if the node is a heading, {@code false} otherwise.
3333     */
3334    public void setHeading(boolean isHeading) {
3335        setBooleanProperty(BOOLEAN_PROPERTY_IS_HEADING, isHeading);
3336    }
3337
3338    /**
3339     * Refreshes this info with the latest state of the view it represents.
3340     * <p>
3341     * <strong>Note:</strong> If this method returns false this info is obsolete
3342     * since it represents a view that is no longer in the view tree and should
3343     * be recycled.
3344     * </p>
3345     * @return Whether the refresh succeeded.
3346     */
3347    public boolean refresh() {
3348        if (Build.VERSION.SDK_INT >= 18) {
3349            return mInfo.refresh();
3350        } else {
3351            return false;
3352        }
3353    }
3354
3355    /**
3356     * Gets the custom role description.
3357     * @return The role description.
3358     */
3359    public @Nullable CharSequence getRoleDescription() {
3360        if (Build.VERSION.SDK_INT >= 19) {
3361            return mInfo.getExtras().getCharSequence(ROLE_DESCRIPTION_KEY);
3362        } else {
3363            return null;
3364        }
3365    }
3366
3367    /**
3368     * Sets the custom role description.
3369     *
3370     * <p>
3371     *   The role description allows you to customize the name for the view's semantic
3372     *   role. For example, if you create a custom subclass of {@link android.view.View}
3373     *   to display a menu bar, you could assign it the role description of "menu bar".
3374     * </p>
3375     * <p>
3376     *   <strong>Warning:</strong> For consistency with other applications, you should
3377     *   not use the role description to force accessibility services to describe
3378     *   standard views (such as buttons or checkboxes) using specific wording. For
3379     *   example, you should not set a role description of "check box" or "tick box" for
3380     *   a standard {@link android.widget.CheckBox}. Instead let accessibility services
3381     *   decide what feedback to provide.
3382     * </p>
3383     * <p>
3384     *   <strong>Note:</strong> Cannot be called from an
3385     *   {@link android.accessibilityservice.AccessibilityService}.
3386     *   This class is made immutable before being delivered to an AccessibilityService.
3387     * </p>
3388     *
3389     * @param roleDescription The role description.
3390     */
3391    public void setRoleDescription(@Nullable CharSequence roleDescription) {
3392        if (Build.VERSION.SDK_INT >= 19) {
3393            mInfo.getExtras().putCharSequence(ROLE_DESCRIPTION_KEY, roleDescription);
3394        }
3395    }
3396
3397    @Override
3398    public int hashCode() {
3399        return (mInfo == null) ? 0 : mInfo.hashCode();
3400    }
3401
3402    @Override
3403    public boolean equals(Object obj) {
3404        if (this == obj) {
3405            return true;
3406        }
3407        if (obj == null) {
3408            return false;
3409        }
3410        if (getClass() != obj.getClass()) {
3411            return false;
3412        }
3413        AccessibilityNodeInfoCompat other = (AccessibilityNodeInfoCompat) obj;
3414        if (mInfo == null) {
3415            if (other.mInfo != null) {
3416                return false;
3417            }
3418        } else if (!mInfo.equals(other.mInfo)) {
3419            return false;
3420        }
3421        return true;
3422    }
3423
3424    @Override
3425    public String toString() {
3426        StringBuilder builder = new StringBuilder();
3427        builder.append(super.toString());
3428
3429        Rect bounds = new Rect();
3430
3431        getBoundsInParent(bounds);
3432        builder.append("; boundsInParent: " + bounds);
3433
3434        getBoundsInScreen(bounds);
3435        builder.append("; boundsInScreen: " + bounds);
3436
3437        builder.append("; packageName: ").append(getPackageName());
3438        builder.append("; className: ").append(getClassName());
3439        builder.append("; text: ").append(getText());
3440        builder.append("; contentDescription: ").append(getContentDescription());
3441        builder.append("; viewId: ").append(getViewIdResourceName());
3442
3443        builder.append("; checkable: ").append(isCheckable());
3444        builder.append("; checked: ").append(isChecked());
3445        builder.append("; focusable: ").append(isFocusable());
3446        builder.append("; focused: ").append(isFocused());
3447        builder.append("; selected: ").append(isSelected());
3448        builder.append("; clickable: ").append(isClickable());
3449        builder.append("; longClickable: ").append(isLongClickable());
3450        builder.append("; enabled: ").append(isEnabled());
3451        builder.append("; password: ").append(isPassword());
3452        builder.append("; scrollable: " + isScrollable());
3453
3454        builder.append("; [");
3455        for (int actionBits = getActions(); actionBits != 0;) {
3456            final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
3457            actionBits &= ~action;
3458            builder.append(getActionSymbolicName(action));
3459            if (actionBits != 0) {
3460                builder.append(", ");
3461            }
3462        }
3463        builder.append("]");
3464
3465        return builder.toString();
3466    }
3467
3468    private void setBooleanProperty(int property, boolean value) {
3469        Bundle extras = getExtras();
3470        if (extras != null) {
3471            int booleanProperties = extras.getInt(BOOLEAN_PROPERTY_KEY, 0);
3472            booleanProperties &= ~property;
3473            booleanProperties |= (value) ? property : 0;
3474            extras.putInt(BOOLEAN_PROPERTY_KEY, booleanProperties);
3475        }
3476    }
3477
3478    private boolean getBooleanProperty(int property) {
3479        Bundle extras = getExtras();
3480        if (extras == null) return false;
3481        return (extras.getInt(BOOLEAN_PROPERTY_KEY, 0) & property) == property;
3482    }
3483
3484    private static String getActionSymbolicName(int action) {
3485        switch (action) {
3486            case ACTION_FOCUS:
3487                return "ACTION_FOCUS";
3488            case ACTION_CLEAR_FOCUS:
3489                return "ACTION_CLEAR_FOCUS";
3490            case ACTION_SELECT:
3491                return "ACTION_SELECT";
3492            case ACTION_CLEAR_SELECTION:
3493                return "ACTION_CLEAR_SELECTION";
3494            case ACTION_CLICK:
3495                return "ACTION_CLICK";
3496            case ACTION_LONG_CLICK:
3497                return "ACTION_LONG_CLICK";
3498            case ACTION_ACCESSIBILITY_FOCUS:
3499                return "ACTION_ACCESSIBILITY_FOCUS";
3500            case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
3501                return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
3502            case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
3503                return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
3504            case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
3505                return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
3506            case ACTION_NEXT_HTML_ELEMENT:
3507                return "ACTION_NEXT_HTML_ELEMENT";
3508            case ACTION_PREVIOUS_HTML_ELEMENT:
3509                return "ACTION_PREVIOUS_HTML_ELEMENT";
3510            case ACTION_SCROLL_FORWARD:
3511                return "ACTION_SCROLL_FORWARD";
3512            case ACTION_SCROLL_BACKWARD:
3513                return "ACTION_SCROLL_BACKWARD";
3514            case ACTION_CUT:
3515                return "ACTION_CUT";
3516            case ACTION_COPY:
3517                return "ACTION_COPY";
3518            case ACTION_PASTE:
3519                return "ACTION_PASTE";
3520            case ACTION_SET_SELECTION:
3521                return "ACTION_SET_SELECTION";
3522            default:
3523                return"ACTION_UNKNOWN";
3524        }
3525    }
3526}
3527