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 
17 package androidx.core.view.accessibility;
18 
19 import static android.view.View.NO_ID;
20 
21 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
22 
23 import static java.util.Collections.emptyList;
24 
25 import android.accessibilityservice.AccessibilityService;
26 import android.annotation.SuppressLint;
27 import android.content.ClipData;
28 import android.graphics.Rect;
29 import android.graphics.Region;
30 import android.os.Build;
31 import android.os.Bundle;
32 import android.text.InputType;
33 import android.text.Spannable;
34 import android.text.SpannableString;
35 import android.text.Spanned;
36 import android.text.TextUtils;
37 import android.text.style.ClickableSpan;
38 import android.util.Log;
39 import android.util.SparseArray;
40 import android.view.View;
41 import android.view.accessibility.AccessibilityEvent;
42 import android.view.accessibility.AccessibilityNodeInfo;
43 import android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo;
44 
45 import androidx.annotation.IntDef;
46 import androidx.annotation.IntRange;
47 import androidx.annotation.OptIn;
48 import androidx.annotation.RequiresApi;
49 import androidx.annotation.RestrictTo;
50 import androidx.core.R;
51 import androidx.core.accessibilityservice.AccessibilityServiceInfoCompat;
52 import androidx.core.view.ViewCompat;
53 import androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments;
54 import androidx.core.view.accessibility.AccessibilityViewCommand.MoveAtGranularityArguments;
55 import androidx.core.view.accessibility.AccessibilityViewCommand.MoveHtmlArguments;
56 import androidx.core.view.accessibility.AccessibilityViewCommand.MoveWindowArguments;
57 import androidx.core.view.accessibility.AccessibilityViewCommand.ScrollToPositionArguments;
58 import androidx.core.view.accessibility.AccessibilityViewCommand.SetProgressArguments;
59 import androidx.core.view.accessibility.AccessibilityViewCommand.SetSelectionArguments;
60 import androidx.core.view.accessibility.AccessibilityViewCommand.SetTextArguments;
61 
62 import org.jspecify.annotations.NonNull;
63 import org.jspecify.annotations.Nullable;
64 
65 import java.lang.annotation.Retention;
66 import java.lang.annotation.RetentionPolicy;
67 import java.lang.ref.WeakReference;
68 import java.time.Duration;
69 import java.util.ArrayList;
70 import java.util.List;
71 import java.util.Map;
72 
73 /**
74  * Helper for accessing {@link android.view.accessibility.AccessibilityNodeInfo} in a backwards
75  * compatible fashion.
76  */
77 public class AccessibilityNodeInfoCompat {
78 
79     /**
80      * A class defining an action that can be performed on an {@link AccessibilityNodeInfo}.
81      * Each action has a unique id and a label.
82      * <p>
83      * There are three categories of actions:
84      * <ul>
85      * <li><strong>Standard actions</strong> - These are actions that are reported and
86      * handled by the standard UI widgets in the platform. Each standard action is associated with
87      * a resource id, e.g. {@link android.R.id#accessibilityActionScrollUp}. Note that actions were
88      * formerly associated with static constants defined in this class, e.g.
89      * {@link #ACTION_FOCUS}. These actions will have {@code null} labels.
90      * </li>
91      * <li><strong>Custom actions action</strong> - These are actions that are reported
92      * and handled by custom widgets. i.e. ones that are not part of the UI toolkit. For
93      * example, an application may define a custom action for clearing the user history.
94      * </li>
95      * <li><strong>Overriden standard actions</strong> - These are actions that override
96      * standard actions to customize them. For example, an app may add a label to the
97      * standard {@link #ACTION_CLICK} action to indicate to the user that this action clears
98      * browsing history.
99      * </ul>
100      * </p>
101      * <p class="note">
102      * <strong>Note:</strong> Views which support these actions should invoke
103      * {@link ViewCompat#setImportantForAccessibility(View, int)} with
104      * {@link ViewCompat#IMPORTANT_FOR_ACCESSIBILITY_YES} to ensure an
105      * {@link android.accessibilityservice.AccessibilityService} can discover the set of supported
106      * actions.
107      * </p>
108      */
109     public static class AccessibilityActionCompat {
110 
111         private static final String TAG = "A11yActionCompat";
112 
113         /**
114          * Action that gives input focus to the node.
115          * <p>The focus request sends an event of {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
116          * if successful. In the View system, this is handled by {@link View#requestFocus}.
117          *
118          * <p>The node that is focused should return {@code true} for
119          * {@link AccessibilityNodeInfoCompat#isFocused()}.
120          *
121          * @see #ACTION_ACCESSIBILITY_FOCUS for the difference between system focus and
122          * accessibility focus.
123          */
124         public static final AccessibilityActionCompat ACTION_FOCUS =
125                 new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_FOCUS, null);
126 
127         /**
128          * Action that clears input focus of the node.
129          * <p>The node that is cleared should return {@code false} for
130          * {@link AccessibilityNodeInfoCompat#isFocused()}.
131          */
132         public static final AccessibilityActionCompat ACTION_CLEAR_FOCUS =
133                 new AccessibilityActionCompat(
134                         AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS, null);
135 
136         /**
137          *  Action that selects the node.
138          */
139         public static final AccessibilityActionCompat ACTION_SELECT =
140                 new AccessibilityActionCompat(
141                         AccessibilityNodeInfoCompat.ACTION_SELECT, null);
142 
143         /**
144          * Action that deselects the node.
145          */
146         public static final AccessibilityActionCompat ACTION_CLEAR_SELECTION =
147                 new AccessibilityActionCompat(
148                         AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION, null);
149 
150         /**
151          * Action that clicks on the node info.
152          *
153          * <p>The UI element that implements this should send a
154          * {@link AccessibilityEvent#TYPE_VIEW_CLICKED} event. In the View system,
155          * the default handling of this action when performed by a service is to call
156          * {@link View#performClick()}, and setting a
157          * {@link View#setOnClickListener(View.OnClickListener)} automatically adds this action.
158          *
159          * <p>{@link #isClickable()} should return true if this action is available.
160          */
161         public static final AccessibilityActionCompat ACTION_CLICK =
162                 new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_CLICK, null);
163 
164         /**
165          * Action that long clicks on the node.
166          *
167          * <p>The UI element that implements this should send a
168          * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED} event. In the View system,
169          * the default handling of this action when performed by a service is to call
170          * {@link View#performLongClick()}, and setting a
171          * {@link View#setOnLongClickListener(View.OnLongClickListener)} automatically adds this
172          * action.
173          *
174          * <p>{@link #isLongClickable()} should return true if this action is available.
175          */
176         public static final AccessibilityActionCompat ACTION_LONG_CLICK =
177                 new AccessibilityActionCompat(
178                         AccessibilityNodeInfoCompat.ACTION_LONG_CLICK, null);
179 
180         /**
181          * Action that gives accessibility focus to the node.
182          * <p>The UI element that implements this should send a
183          * {@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED} event
184          * if successful. The node that is focused should return {@code true} for
185          * {@link AccessibilityNodeInfoCompat#isAccessibilityFocused()}.
186          *
187          * <p>This is intended to be used by screen readers to assist with user navigation. Apps
188          * changing focus can confuse screen readers, so the resulting behavior can vary by device
189          * and screen reader version.
190          * <p>This is distinct from {@link #ACTION_FOCUS}, which refers to system focus. System
191          * focus is typically used to convey targets for keyboard navigation.
192          */
193         public static final AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS =
194                 new AccessibilityActionCompat(
195                         AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null);
196 
197         /**
198          * Action that clears accessibility focus of the node.
199          * <p>The UI element that implements this should send a
200          * {@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED} event if successful. The
201          * node that is cleared should return {@code false} for
202          * {@link AccessibilityNodeInfoCompat#isAccessibilityFocused()}.
203          */
204         public static final AccessibilityActionCompat ACTION_CLEAR_ACCESSIBILITY_FOCUS =
205                 new AccessibilityActionCompat(
206                         AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
207 
208         /**
209          * Action that requests to go to the next entity in this node's text
210          * at a given movement granularity. For example, move to the next character,
211          * word, etc.
212          * <p>
213          * <strong>Arguments:</strong>
214          * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
215          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
216          * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
217          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
218          * <strong>Example:</strong> Move to the previous character and do not extend selection.
219          * <code><pre><p>
220          *   Bundle arguments = new Bundle();
221          *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
222          *           AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER);
223          *   arguments.putBoolean(
224          *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
225          *   info.performAction(
226          *           AccessibilityActionCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY.getId(),
227          *           arguments);
228          * </code></pre></p>
229          * </p>
230          *
231          * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
232          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
233          * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
234          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
235          *
236          * @see AccessibilityNodeInfoCompat#setMovementGranularities(int)
237          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
238          * @see AccessibilityNodeInfoCompat#getMovementGranularities()
239          *  AccessibilityNodeInfoCompat.getMovementGranularities()
240          *
241          * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_CHARACTER
242          *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER
243          * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_WORD
244          *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_WORD
245          * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_LINE
246          *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_LINE
247          * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PARAGRAPH
248          *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PARAGRAPH
249          * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PAGE
250          *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE
251          */
252         public static final AccessibilityActionCompat ACTION_NEXT_AT_MOVEMENT_GRANULARITY =
253                 new AccessibilityActionCompat(
254                         AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, null,
255                         MoveAtGranularityArguments.class);
256 
257         /**
258          * Action that requests to go to the previous entity in this node's text
259          * at a given movement granularity. For example, move to the next character,
260          * word, etc.
261          * <p>
262          * <strong>Arguments:</strong>
263          * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
264          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
265          * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
266          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
267          * <strong>Example:</strong> Move to the next character and do not extend selection.
268          * <code><pre><p>
269          *   Bundle arguments = new Bundle();
270          *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
271          *           AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER);
272          *   arguments.putBoolean(
273          *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
274          *   info.performAction(
275          *           AccessibilityActionCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY.getId(),
276          *           arguments);
277          * </code></pre></p>
278          * </p>
279          *
280          * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
281          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
282          * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
283          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
284          *
285          * @see AccessibilityNodeInfoCompat#setMovementGranularities(int)
286          *   AccessibilityNodeInfoCompat.setMovementGranularities(int)
287          * @see AccessibilityNodeInfoCompat#getMovementGranularities()
288          *  AccessibilityNodeInfoCompat.getMovementGranularities()
289          *
290          * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_CHARACTER
291          *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER
292          * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_WORD
293          *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_WORD
294          * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_LINE
295          *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_LINE
296          * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PARAGRAPH
297          *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PARAGRAPH
298          * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PAGE
299          *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE
300          */
301         public static final AccessibilityActionCompat ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY =
302                 new AccessibilityActionCompat(
303                         AccessibilityNodeInfoCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, null,
304                         MoveAtGranularityArguments.class);
305 
306         /**
307          * Action to move to the next HTML element of a given type. For example, move
308          * to the BUTTON, INPUT, TABLE, etc.
309          * <p>
310          * <strong>Arguments:</strong>
311          * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_HTML_ELEMENT_STRING
312          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
313          * <strong>Example:</strong>
314          * <code><pre><p>
315          *   Bundle arguments = new Bundle();
316          *   arguments.putString(
317          *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
318          *   info.performAction(
319          *           AccessibilityActionCompat.ACTION_NEXT_HTML_ELEMENT.getId(), arguments);
320          * </code></pre></p>
321          * </p>
322          */
323         public static final AccessibilityActionCompat ACTION_NEXT_HTML_ELEMENT =
324                 new AccessibilityActionCompat(
325                         AccessibilityNodeInfoCompat.ACTION_NEXT_HTML_ELEMENT, null,
326                         MoveHtmlArguments.class);
327 
328         /**
329          * Action to move to the previous HTML element of a given type. For example, move
330          * to the BUTTON, INPUT, TABLE, etc.
331          * <p>
332          * <strong>Arguments:</strong>
333          * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_HTML_ELEMENT_STRING
334          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
335          * <strong>Example:</strong>
336          * <code><pre><p>
337          *   Bundle arguments = new Bundle();
338          *   arguments.putString(
339          *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
340          *   info.performAction(
341          *           AccessibilityActionCompat.ACTION_PREVIOUS_HTML_ELEMENT.getId(), arguments);
342          * </code></pre></p>
343          * </p>
344          */
345         public static final AccessibilityActionCompat ACTION_PREVIOUS_HTML_ELEMENT =
346                 new AccessibilityActionCompat(
347                         AccessibilityNodeInfoCompat.ACTION_PREVIOUS_HTML_ELEMENT, null,
348                         MoveHtmlArguments.class);
349 
350         /**
351          * Action to scroll the node content forward.
352          */
353         public static final AccessibilityActionCompat ACTION_SCROLL_FORWARD =
354                 new AccessibilityActionCompat(
355                         AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, null);
356 
357         /**
358          * Action to scroll the node content backward.
359          */
360         public static final AccessibilityActionCompat ACTION_SCROLL_BACKWARD =
361                 new AccessibilityActionCompat(
362                         AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, null);
363 
364         /**
365          * Action to copy the current selection to the clipboard.
366          */
367         public static final AccessibilityActionCompat ACTION_COPY =
368                 new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_COPY, null);
369 
370         /**
371          * Action to paste the current clipboard content.
372          */
373         public static final AccessibilityActionCompat ACTION_PASTE =
374                 new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_PASTE, null);
375 
376         /**
377          * Action to cut the current selection and place it to the clipboard.
378          */
379         public static final AccessibilityActionCompat ACTION_CUT =
380                 new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_CUT, null);
381 
382         /**
383          * Action to set the selection. Performing this action with no arguments
384          * clears the selection.
385          * <p>
386          * <strong>Arguments:</strong>
387          * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_START_INT
388          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT},
389          * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_END_INT
390          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT}<br>
391          * <strong>Example:</strong>
392          * <code><pre><p>
393          *   Bundle arguments = new Bundle();
394          *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT, 1);
395          *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT, 2);
396          *   info.performAction(AccessibilityActionCompat.ACTION_SET_SELECTION.getId(), arguments);
397          * </code></pre></p>
398          * </p>
399          *
400          * <p> If this is a text selection, the UI element that implements this should send a
401          * {@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED} event if its selection is
402          * updated. This element should also return {@code true} for
403          * {@link AccessibilityNodeInfoCompat#isTextSelectable()}.
404          *
405          * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_START_INT
406          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT
407          * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_END_INT
408          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT
409          */
410         public static final AccessibilityActionCompat ACTION_SET_SELECTION =
411                 new AccessibilityActionCompat(
412                         AccessibilityNodeInfoCompat.ACTION_SET_SELECTION, null,
413                         SetSelectionArguments.class);
414 
415         /**
416          * Action to expand an expandable node.
417          */
418         public static final AccessibilityActionCompat ACTION_EXPAND =
419                 new AccessibilityActionCompat(
420                         AccessibilityNodeInfoCompat.ACTION_EXPAND, null);
421 
422         /**
423          * Action to collapse an expandable node.
424          */
425         public static final AccessibilityActionCompat ACTION_COLLAPSE =
426                 new AccessibilityActionCompat(
427                         AccessibilityNodeInfoCompat.ACTION_COLLAPSE, null);
428 
429         /**
430          * Action to dismiss a dismissable node.
431          */
432         public static final AccessibilityActionCompat ACTION_DISMISS =
433                 new AccessibilityActionCompat(
434                         AccessibilityNodeInfoCompat.ACTION_DISMISS, null);
435 
436         /**
437          * Action that sets the text of the node. Performing the action without argument,
438          * using <code> null</code> or empty {@link CharSequence} will clear the text. This
439          * action will also put the cursor at the end of text.
440          * <p>
441          * <strong>Arguments:</strong>
442          * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE
443          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
444          * <strong>Example:</strong>
445          * <code><pre><p>
446          *   Bundle arguments = new Bundle();
447          *   arguments.putCharSequence(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
448          *       "android");
449          *  info.performAction(AccessibilityActionCompat.ACTION_SET_TEXT.getId(), arguments);
450          * </code></pre></p>
451          * <p>The UI element that implements this should send a
452          * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED} event if its text is updated.
453          * This element should also return {@code true} for
454          * {@link AccessibilityNodeInfoCompat#isEditable()}.
455          */
456         public static final AccessibilityActionCompat ACTION_SET_TEXT =
457                 new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_SET_TEXT, null,
458                         SetTextArguments.class);
459 
460         /**
461          * Action that requests the node make its bounding rectangle visible
462          * on the screen, scrolling if necessary just enough.
463          *
464          * @see View#requestRectangleOnScreen(Rect)
465          */
466         public static final AccessibilityActionCompat ACTION_SHOW_ON_SCREEN =
467                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
468                         ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_ON_SCREEN : null,
469                         android.R.id.accessibilityActionShowOnScreen, null, null, null);
470 
471         /**
472          * Action that scrolls the node to make the specified collection
473          * position visible on screen.
474          * <p>
475          * <strong>Arguments:</strong>
476          * <ul>
477          *     <li>{@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_ROW_INT}</li>
478          *     <li>{@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_COLUMN_INT}</li>
479          * <ul>
480          *
481          * @see AccessibilityNodeInfoCompat#getCollectionInfo()
482          */
483         public static final AccessibilityActionCompat ACTION_SCROLL_TO_POSITION =
484                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
485                         ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_TO_POSITION
486                         : null, android.R.id.accessibilityActionScrollToPosition, null, null,
487                         ScrollToPositionArguments.class);
488 
489         /**
490          * Action to scroll the node content up.
491          */
492         public static final AccessibilityActionCompat ACTION_SCROLL_UP =
493                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
494                         ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP : null,
495                         android.R.id.accessibilityActionScrollUp, null, null, null);
496         /**
497          * Action to scroll the node content left.
498          */
499         public static final AccessibilityActionCompat ACTION_SCROLL_LEFT =
500                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
501                         ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_LEFT : null,
502                         android.R.id.accessibilityActionScrollLeft, null, null, null);
503 
504         /**
505          * Action to scroll the node content down.
506          */
507         public static final AccessibilityActionCompat ACTION_SCROLL_DOWN =
508                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
509                         ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN : null,
510                         android.R.id.accessibilityActionScrollDown, null, null, null);
511 
512         /**
513          * Action to scroll the node content right.
514          */
515         public static final AccessibilityActionCompat ACTION_SCROLL_RIGHT =
516                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
517                         ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_RIGHT : null,
518                         android.R.id.accessibilityActionScrollRight, null, null, null);
519 
520         /**
521          * Action to move to the page above.
522          */
523         public static final @NonNull AccessibilityActionCompat ACTION_PAGE_UP =
524                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 29
525                         ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_UP : null,
526                         android.R.id.accessibilityActionPageUp, null, null, null);
527 
528         /**
529          * Action to move to the page below.
530          */
531         public static final @NonNull AccessibilityActionCompat ACTION_PAGE_DOWN =
532                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 29
533                         ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_DOWN : null,
534                         android.R.id.accessibilityActionPageDown, null, null, null);
535 
536         /**
537          * Action to move to the page left.
538          */
539         public static final @NonNull AccessibilityActionCompat ACTION_PAGE_LEFT =
540                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 29
541                         ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT : null,
542                         android.R.id.accessibilityActionPageLeft, null, null, null);
543 
544         /**
545          * Action to move to the page right.
546          */
547         public static final @NonNull AccessibilityActionCompat ACTION_PAGE_RIGHT =
548                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 29
549                         ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT : null,
550                         android.R.id.accessibilityActionPageRight, null, null, null);
551 
552         /**
553          * Action that context clicks the node.
554          *
555          * <p>The UI element that implements this should send a
556          * {@link AccessibilityEvent#TYPE_VIEW_CONTEXT_CLICKED} event. In the View system,
557          * the default handling of this action when performed by a service is to call
558          * {@link View#performContextClick()}, and setting a
559          * {@link View#setOnContextClickListener(View.OnContextClickListener)} automatically adds
560          * this action.
561          *
562          * <p>A context click usually occurs from a mouse pointer right-click or a stylus button
563          * press.
564          *
565          * <p>{@link #isContextClickable()} should return true if this action is available.
566          */
567         public static final AccessibilityActionCompat ACTION_CONTEXT_CLICK =
568                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
569                         ? AccessibilityNodeInfo.AccessibilityAction.ACTION_CONTEXT_CLICK : null,
570                         android.R.id.accessibilityActionContextClick, null, null, null);
571 
572         /**
573          * Action that sets progress between {@link  RangeInfoCompat#getMin() RangeInfo.getMin()} and
574          * {@link  RangeInfoCompat#getMax() RangeInfo.getMax()}. It should use the same value type as
575          * {@link RangeInfoCompat#getType() RangeInfo.getType()}
576          * <p>
577          * <strong>Arguments:</strong>
578          * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_PROGRESS_VALUE}
579          *
580          * @see RangeInfoCompat
581          */
582         public static final AccessibilityActionCompat ACTION_SET_PROGRESS =
583                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 24
584                         ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS : null,
585                         android.R.id.accessibilityActionSetProgress, null, null,
586                         SetProgressArguments.class);
587 
588         /**
589          * Action to move a window to a new location.
590          * <p>
591          * <strong>Arguments:</strong>
592          * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVE_WINDOW_X}
593          * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVE_WINDOW_Y}
594          */
595         public static final AccessibilityActionCompat ACTION_MOVE_WINDOW =
596                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 26
597                         ? AccessibilityNodeInfo.AccessibilityAction.ACTION_MOVE_WINDOW : null,
598                         android.R.id.accessibilityActionMoveWindow, null, null,
599                         MoveWindowArguments.class);
600 
601         /**
602          * Action to show a tooltip.
603          */
604         public static final AccessibilityActionCompat ACTION_SHOW_TOOLTIP =
605                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 28
606                         ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_TOOLTIP : null,
607                         android.R.id.accessibilityActionShowTooltip, null, null, null);
608 
609         /**
610          * Action to hide a tooltip. A node should expose this action only for views that are
611          * currently showing a tooltip.
612          */
613         public static final AccessibilityActionCompat ACTION_HIDE_TOOLTIP =
614                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 28
615                         ? AccessibilityNodeInfo.AccessibilityAction.ACTION_HIDE_TOOLTIP : null,
616                         android.R.id.accessibilityActionHideTooltip, null, null, null);
617 
618         /**
619          * Action that presses and holds a node.
620          * <p>
621          * This action is for nodes that have distinct behavior that depends on how long a press is
622          * held. Nodes having a single action for long press should use {@link #ACTION_LONG_CLICK}
623          *  instead of this action, and nodes should not expose both actions.
624          * <p>
625          * When calling {@code performAction(ACTION_PRESS_AND_HOLD, bundle}, use
626          * {@link #ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT} to specify how long the
627          * node is pressed. The first time an accessibility service performs ACTION_PRES_AND_HOLD
628          * on a node, it must specify 0 as ACTION_ARGUMENT_PRESS_AND_HOLD, so the application is
629          * notified that the held state has started. To ensure reasonable behavior, the values
630          * must be increased incrementally and may not exceed 10,000. UIs requested
631          * to hold for times outside of this range should ignore the action.
632          * <p>
633          * The total time the element is held could be specified by an accessibility user up-front,
634          * or may depend on what happens on the UI as the user continues to request the hold.
635          * <p>
636          *   <strong>Note:</strong> The time between dispatching the action and it arriving in the
637          *     UI process is not guaranteed. It is possible on a busy system for the time to expire
638          *     unexpectedly. For the case of holding down a key for a repeating action, a delayed
639          *     arrival should be benign. Please do not use this sort of action in cases where such
640          *     delays will lead to unexpected UI behavior.
641          * <p>
642          */
643         public static final @NonNull AccessibilityActionCompat ACTION_PRESS_AND_HOLD =
644                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 30
645                         ? AccessibilityNodeInfo.AccessibilityAction.ACTION_PRESS_AND_HOLD : null,
646                         android.R.id.accessibilityActionPressAndHold, null, null, null);
647 
648         /**
649          * Action to send an ime actionId which is from
650          * {@link android.view.inputmethod.EditorInfo#actionId}. This ime actionId sets by
651          * {@link android.widget.TextView#setImeActionLabel(CharSequence, int)}, or it would be
652          * {@link android.view.inputmethod.EditorInfo#IME_ACTION_UNSPECIFIED} if no specific
653          * actionId has set. A node should expose this action only for views that are currently
654          * with input focus and editable.
655          */
656         public static final @NonNull AccessibilityActionCompat ACTION_IME_ENTER =
657                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 30
658                         ? AccessibilityNodeInfo.AccessibilityAction.ACTION_IME_ENTER : null,
659                         android.R.id.accessibilityActionImeEnter, null, null, null);
660 
661         /**
662          * Action to start a drag.
663          * <p>
664          * This action initiates a drag & drop within the system. The source's dragged content is
665          * prepared before the drag begins. In View, this action should prepare the arguments to
666          * {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)}} and then
667          * call the method. The equivalent should be performed for other UI toolkits.
668          * </p>
669          *
670          * @see AccessibilityEventCompat#CONTENT_CHANGE_TYPE_DRAG_STARTED
671          */
672         public static final @NonNull AccessibilityActionCompat ACTION_DRAG_START =
673                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 32
674                         ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_DRAG_START : null,
675                         android.R.id.accessibilityActionDragStart, null, null, null);
676 
677         /**
678          * Action to trigger a drop of the content being dragged.
679          * <p>
680          * This action is added to potential drop targets if the source started a drag with
681          * {@link #ACTION_DRAG_START}. In View, these targets are Views that accepted
682          * {@link android.view.DragEvent#ACTION_DRAG_STARTED} and have an
683          * {@link View.OnDragListener}.
684          * </p>
685          *
686          * @see AccessibilityEventCompat#CONTENT_CHANGE_TYPE_DRAG_DROPPED
687          */
688         public static final @NonNull AccessibilityActionCompat ACTION_DRAG_DROP =
689                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 32
690                         ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_DRAG_DROP : null,
691                         android.R.id.accessibilityActionDragDrop, null, null, null);
692 
693         /**
694          * Action to cancel a drag.
695          * <p>
696          * This action is added to the source that started a drag with {@link #ACTION_DRAG_START}.
697          * </p>
698          *
699          * @see AccessibilityEventCompat#CONTENT_CHANGE_TYPE_DRAG_CANCELLED
700          */
701         public static final @NonNull AccessibilityActionCompat ACTION_DRAG_CANCEL =
702                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 32
703                         ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_DRAG_CANCEL : null,
704                         android.R.id.accessibilityActionDragCancel, null, null, null);
705 
706         /**
707          * Action to show suggestions for editable text.
708          */
709         public static final @NonNull AccessibilityActionCompat ACTION_SHOW_TEXT_SUGGESTIONS =
710                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 33
711                         ?   AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_TEXT_SUGGESTIONS
712                         :   null, android.R.id.accessibilityActionShowTextSuggestions, null,
713                         null, null);
714 
715         /**
716          * Action that brings fully on screen the next node in the specified direction.
717          *
718          * <p>
719          *     This should include wrapping around to the next/previous row, column, etc. in a
720          *     collection if one is available. If there is no node in that direction, the action
721          *     should fail and return false.
722          * </p>
723          * <p>
724          *     This action should be used instead of
725          *     {@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION} when a widget does not
726          *     have clear row and column semantics or if a directional search is needed to find a
727          *     node in a complex ViewGroup where individual nodes may span multiple rows or
728          *     columns. The implementing widget must send a
729          *     {@link AccessibilityEventCompat#TYPE_VIEW_TARGETED_BY_SCROLL} accessibility event
730          *     with the scroll target as the source.  An accessibility service can listen for this
731          *     event, inspect its source, and use the result when determining where to place
732          *     accessibility focus.
733          * <p>
734          *     <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_DIRECTION_INT}. This is a
735          *     required argument.<br>
736          * </p>
737          */
738         @OptIn(markerClass = androidx.core.os.BuildCompat.PrereleaseSdkCheck.class)
739         public static final @NonNull AccessibilityActionCompat ACTION_SCROLL_IN_DIRECTION =
740                 new AccessibilityActionCompat(
741                         Build.VERSION.SDK_INT >= 34 ? Api34Impl.getActionScrollInDirection() : null,
742                         android.R.id.accessibilityActionScrollInDirection, null, null, null);
743 
744         final Object mAction;
745         private final int mId;
746         private final Class<? extends CommandArguments> mViewCommandArgumentClass;
747 
748         /**
749          */
750         @RestrictTo(LIBRARY_GROUP_PREFIX)
751         protected final AccessibilityViewCommand mCommand;
752 
753         /**
754          * Creates a new instance.
755          *
756          * @param actionId The action id.
757          * @param label The action label.
758          */
AccessibilityActionCompat(int actionId, CharSequence label)759         public AccessibilityActionCompat(int actionId, CharSequence label) {
760             this(null, actionId, label, null, null);
761         }
762 
763         /**
764          * Creates a new instance.
765          *
766          * @param actionId The action id.
767          * @param label The action label.
768          * @param command The command performed when the service requests the action
769          */
770         @RestrictTo(LIBRARY_GROUP_PREFIX)
AccessibilityActionCompat(int actionId, CharSequence label, AccessibilityViewCommand command)771         public AccessibilityActionCompat(int actionId, CharSequence label,
772                 AccessibilityViewCommand command) {
773             this(null, actionId, label, command, null);
774         }
775 
AccessibilityActionCompat(Object action)776         AccessibilityActionCompat(Object action) {
777             this(action, 0, null, null, null);
778         }
779 
AccessibilityActionCompat(int actionId, CharSequence label, Class<? extends CommandArguments> viewCommandArgumentClass)780         private AccessibilityActionCompat(int actionId, CharSequence label,
781                 Class<? extends CommandArguments> viewCommandArgumentClass) {
782             this(null, actionId, label, null, viewCommandArgumentClass);
783         }
784 
AccessibilityActionCompat(Object action, int id, CharSequence label, AccessibilityViewCommand command, Class<? extends CommandArguments> viewCommandArgumentClass)785         AccessibilityActionCompat(Object action, int id, CharSequence label,
786                 AccessibilityViewCommand command,
787                 Class<? extends CommandArguments> viewCommandArgumentClass) {
788             mId = id;
789             mCommand = command;
790             if (action == null) {
791                 mAction = new AccessibilityNodeInfo.AccessibilityAction(id, label);
792             } else {
793                 mAction = action;
794             }
795             mViewCommandArgumentClass = viewCommandArgumentClass;
796         }
797 
798         /**
799          * Gets the id for this action.
800          *
801          * @return The action id.
802          */
getId()803         public int getId() {
804             return ((AccessibilityNodeInfo.AccessibilityAction) mAction).getId();
805         }
806 
807         /**
808          * Gets the label for this action. Its purpose is to describe the
809          * action to user.
810          *
811          * @return The label.
812          */
getLabel()813         public CharSequence getLabel() {
814             return ((AccessibilityNodeInfo.AccessibilityAction) mAction).getLabel();
815         }
816 
817         /**
818          * Performs the action.
819          * @return If the action was handled.
820          * @param view View to act upon.
821          * @param arguments Optional action arguments.
822          */
823         @RestrictTo(LIBRARY_GROUP_PREFIX)
perform(View view, Bundle arguments)824         public boolean perform(View view, Bundle arguments) {
825             if (mCommand != null) {
826                 CommandArguments viewCommandArgument = null;
827                 if (mViewCommandArgumentClass != null) {
828                     try {
829                         viewCommandArgument =
830                                 mViewCommandArgumentClass.getDeclaredConstructor().newInstance();
831                         viewCommandArgument.setBundle(arguments);
832                     } catch (Exception e) {
833                         final String className = mViewCommandArgumentClass == null
834                                 ? "null" : mViewCommandArgumentClass.getName();
835                         Log.e(TAG, "Failed to execute command with argument class "
836                                 + "ViewCommandArgument: " + className, e);
837                     }
838                 }
839                 return mCommand.perform(view, viewCommandArgument);
840             }
841             return false;
842         }
843 
844         /**
845          */
846         @RestrictTo(LIBRARY_GROUP_PREFIX)
createReplacementAction(CharSequence label, AccessibilityViewCommand command)847         public AccessibilityActionCompat createReplacementAction(CharSequence label,
848                 AccessibilityViewCommand command) {
849             return new AccessibilityActionCompat(null, mId, label, command,
850                     mViewCommandArgumentClass);
851         }
852 
853         @Override
hashCode()854         public int hashCode() {
855             return mAction != null ? mAction.hashCode() : 0;
856         }
857 
858         @Override
equals(@ullable Object obj)859         public boolean equals(@Nullable Object obj) {
860             if (obj == null) {
861                 return false;
862             }
863             if (!(obj instanceof AccessibilityNodeInfoCompat.AccessibilityActionCompat)) {
864                 return false;
865             }
866             AccessibilityNodeInfoCompat.AccessibilityActionCompat other =
867                     (AccessibilityNodeInfoCompat.AccessibilityActionCompat) obj;
868             if (mAction == null) {
869                 if (other.mAction != null) {
870                     return false;
871                 }
872             } else if (!mAction.equals(other.mAction)) {
873                 return false;
874             }
875             return true;
876         }
877 
878         @Override
toString()879         public @NonNull String toString() {
880             StringBuilder builder = new StringBuilder();
881             builder.append("AccessibilityActionCompat: ");
882             // Mirror AccessibilityNodeInfoCompat.toString's action string.
883             String actionName = getActionSymbolicName(mId);
884             if (actionName.equals("ACTION_UNKNOWN") && getLabel() != null) {
885                 actionName = getLabel().toString();
886             }
887             builder.append(actionName);
888             return builder.toString();
889         }
890     }
891 
892     /**
893      * Class with information if a node is a collection.
894      * <p>
895      * A collection of items has rows and columns and may be marked as hierarchical.
896      *
897      * <p>
898      * For example, a list where the items are placed in a vertical layout is a collection with one
899      * column and as many rows as the list items. This collection has 3 rows and 1 column and should
900      * not be marked as hierarchical since items do not exist at different levels/ranks and there
901      * are no nested collections.
902      * <ul>
903      *     <li>Item 1</li>
904      *     <li>Item 2</li>
905      *     <li>Item 3</li>
906      * </ul>
907      *
908      * <p>
909      * A table is a collection with several rows and several columns. This collection has 2 rows and
910      * 3 columns and is not marked as hierarchical:
911      *<table>
912      *   <tr>
913      *     <td>Item 1</td>
914      *     <td>Item 2</td>
915      *     <td>Item 3</td>
916      *   </tr>
917      *   <tr>
918      *     <td>Item 4</td>
919      *     <td>Item 5</td>
920      *     <td>Item 6</td>
921      *   </tr>
922      * </table>
923      *
924      * <p>
925      * Nested collections could be marked as hierarchical. To add outer and inner collections to the
926      * same hierarchy, mark them both as hierarchical.
927      *
928      * <p> For example, if you have a collection with two lists - this collection has an outer
929      * list with 3 rows and 1 column and an inner list within "Item 2" with 2 rows and 1 -
930      * you can mark both the outer list and the inner list as hierarchical to make them part of
931      * the same hierarchy. If a collection does not have any ancestor or descendant hierarchical
932      * collections, it does not need to be marked as hierarchical.
933      *  <ul>
934      *      <li>Item 1</li>
935      *      <li> Item 2
936      *          <ul>
937      *              <li>Item 2A</li>
938      *              <li>Item 2B</li>
939      *          </ul>
940      *      </li>
941      *      <li>Item 3</li>
942      *  </ul>
943      *
944      * <p>
945      * To be a valid list, a collection has 1 row and any number of columns or 1 column and any
946      * number of rows.
947      * </p>
948      */
949     public static class CollectionInfoCompat {
950         /** Selection mode where items are not selectable. */
951         public static final int SELECTION_MODE_NONE = 0;
952 
953         /** Selection mode where a single item may be selected. */
954         public static final int SELECTION_MODE_SINGLE = 1;
955 
956         /** Selection mode where multiple items may be selected. */
957         public static final int SELECTION_MODE_MULTIPLE = 2;
958 
959         /**
960          * Constant to denote a missing collection count.
961          *
962          * This should be used for {@code mItemCount} and
963          * {@code mImportantForAccessibilityItemCount} when values for those fields are not known.
964          */
965         public static final int UNDEFINED = AccessibilityNodeInfo.CollectionInfo.UNDEFINED;
966 
967         final Object mInfo;
968 
969         /**
970          * Returns a cached instance if such is available otherwise a new one.
971          *
972          * @param rowCount The number of rows.
973          * @param columnCount The number of columns.
974          * @param hierarchical Whether the collection is hierarchical.
975          * @param selectionMode The collection's selection mode, one of:
976          *            <ul>
977          *            <li>{@link #SELECTION_MODE_NONE}
978          *            <li>{@link #SELECTION_MODE_SINGLE}
979          *            <li>{@link #SELECTION_MODE_MULTIPLE}
980          *            </ul>
981          *
982          * @return An instance.
983          */
obtain(int rowCount, int columnCount, boolean hierarchical, int selectionMode)984         public static CollectionInfoCompat obtain(int rowCount, int columnCount,
985                 boolean hierarchical, int selectionMode) {
986             return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain(
987                     rowCount, columnCount, hierarchical, selectionMode));
988         }
989 
990         /**
991          * Returns a cached instance if such is available otherwise a new one.
992          *
993          * @param rowCount The number of rows, or -1 if count is unknown.
994          * @param columnCount The number of columns , or -1 if count is unknown.
995          * @param hierarchical Whether the collection is hierarchical.
996          *
997          * @return An instance.
998          */
obtain(int rowCount, int columnCount, boolean hierarchical)999         public static CollectionInfoCompat obtain(int rowCount, int columnCount,
1000                 boolean hierarchical) {
1001             return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain(
1002                     rowCount, columnCount, hierarchical));
1003         }
1004 
CollectionInfoCompat(Object info)1005         CollectionInfoCompat(Object info) {
1006             mInfo = info;
1007         }
1008 
1009         /**
1010          * Gets the number of columns.
1011          *
1012          * @return The column count, or -1 if count is unknown.
1013          */
getColumnCount()1014         public int getColumnCount() {
1015             return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getColumnCount();
1016         }
1017 
1018         /**
1019          * Gets the number of rows.
1020          *
1021          * @return The row count, or -1 if count is unknown.
1022          */
getRowCount()1023         public int getRowCount() {
1024             return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getRowCount();
1025         }
1026 
1027         /**
1028          * Gets if the collection is a hierarchically ordered.
1029          *
1030          * @return Whether the collection is hierarchical.
1031          */
isHierarchical()1032         public boolean isHierarchical() {
1033             return ((AccessibilityNodeInfo.CollectionInfo) mInfo).isHierarchical();
1034         }
1035 
1036         /**
1037          * Gets the collection's selection mode.
1038          *
1039          * @return The collection's selection mode, one of:
1040          *         <ul>
1041          *         <li>{@link #SELECTION_MODE_NONE}
1042          *         <li>{@link #SELECTION_MODE_SINGLE}
1043          *         <li>{@link #SELECTION_MODE_MULTIPLE}
1044          *         </ul>
1045          */
getSelectionMode()1046         public int getSelectionMode() {
1047             return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getSelectionMode();
1048         }
1049 
1050         /**
1051          * Gets the number of items in the collection.
1052          *
1053          * @return The count of items, which may be {@code UNDEFINED} if the count is not known.
1054          */
getItemCount()1055         public int getItemCount() {
1056             if (Build.VERSION.SDK_INT >= 35) {
1057                 return Api35Impl.getItemCount(mInfo);
1058             }
1059             return UNDEFINED;
1060         }
1061 
1062         /**
1063          * Gets the number of items in the collection considered important for accessibility.
1064          *
1065          * @return The count of items important for accessibility, which may be {@code UNDEFINED}
1066          * if the count is not known.
1067          */
getImportantForAccessibilityItemCount()1068         public int getImportantForAccessibilityItemCount() {
1069             if (Build.VERSION.SDK_INT >= 35) {
1070                 return Api35Impl.getImportantForAccessibilityItemCount(mInfo);
1071             }
1072             return UNDEFINED;
1073         }
1074 
1075         /**
1076          * Class for building {@link CollectionInfoCompat} objects.
1077          */
1078         public static final class Builder {
1079             private int mRowCount = 0;
1080             private int mColumnCount = 0;
1081             private boolean mHierarchical = false;
1082             private int mSelectionMode;
1083             private int mItemCount = AccessibilityNodeInfo.CollectionInfo.UNDEFINED;
1084             private int mImportantForAccessibilityItemCount =
1085                     AccessibilityNodeInfo.CollectionInfo.UNDEFINED;
1086 
1087             /**
1088              * Creates a new Builder.
1089              */
Builder()1090             public Builder() {
1091             }
1092 
1093             /**
1094              * Sets the row count.
1095              * @param rowCount The number of rows in the collection.
1096              * @return This builder.
1097              */
setRowCount(int rowCount)1098             public CollectionInfoCompat.@NonNull Builder setRowCount(int rowCount) {
1099                 mRowCount = rowCount;
1100                 return this;
1101             }
1102 
1103             /**
1104              * Sets the column count.
1105              * @param columnCount The number of columns in the collection.
1106              * @return This builder.
1107              */
setColumnCount(int columnCount)1108             public CollectionInfoCompat.@NonNull Builder setColumnCount(int columnCount) {
1109                 mColumnCount = columnCount;
1110                 return this;
1111             }
1112             /**
1113              * Sets whether the collection is hierarchical.
1114              * @param hierarchical Whether the collection is hierarchical.
1115              * @return This builder.
1116              */
setHierarchical(boolean hierarchical)1117             public CollectionInfoCompat.@NonNull Builder setHierarchical(boolean hierarchical) {
1118                 mHierarchical = hierarchical;
1119                 return this;
1120             }
1121 
1122             /**
1123              * Sets the selection mode.
1124              * @param selectionMode The selection mode.
1125              * @return This builder.
1126              */
setSelectionMode(int selectionMode)1127             public CollectionInfoCompat.@NonNull Builder setSelectionMode(int selectionMode) {
1128                 mSelectionMode = selectionMode;
1129                 return this;
1130             }
1131 
1132             /**
1133              * Sets the number of items in the collection. Can be optionally set for ViewGroups with
1134              * clear row and column semantics; should be set for all other clients.
1135              *
1136              * @param itemCount The number of items in the collection. This should be set to
1137              *                  {@code UNDEFINED} if the item count is not known.
1138              * @return This builder.
1139              */
setItemCount(int itemCount)1140             public CollectionInfoCompat.@NonNull Builder setItemCount(int itemCount) {
1141                 mItemCount = itemCount;
1142                 return this;
1143             }
1144 
1145             /**
1146              * Sets the number of views considered important for accessibility.
1147              * @param importantForAccessibilityItemCount The number of items important for
1148              *                                            accessibility.
1149              * @return This builder.
1150              */
setImportantForAccessibilityItemCount( int importantForAccessibilityItemCount)1151             public CollectionInfoCompat.@NonNull Builder setImportantForAccessibilityItemCount(
1152                     int importantForAccessibilityItemCount) {
1153                 mImportantForAccessibilityItemCount = importantForAccessibilityItemCount;
1154                 return this;
1155             }
1156 
1157             /**
1158              * Creates a new {@link CollectionInfoCompat} instance.
1159              */
build()1160             public @NonNull CollectionInfoCompat build() {
1161                 if (Build.VERSION.SDK_INT >= 35) {
1162                     return Api35Impl.buildCollectionInfoCompat(mRowCount, mColumnCount,
1163                             mHierarchical, mSelectionMode, mItemCount,
1164                             mImportantForAccessibilityItemCount);
1165                 }
1166 
1167                 return CollectionInfoCompat.obtain(mRowCount, mColumnCount, mHierarchical,
1168                         mSelectionMode);
1169             }
1170         }
1171     }
1172 
1173     /**
1174      * Class with information if a node is a collection item.
1175      * <p>
1176      * A collection item is contained in a collection, it starts at
1177      * a given row and column in the collection, and spans one or
1178      * more rows and columns. For example, a header of two related
1179      * table columns starts at the first row and the first column,
1180      * spans one row and two columns.
1181      * </p>
1182      */
1183     public static class CollectionItemInfoCompat {
1184 
1185         final Object mInfo;
1186 
1187         /**
1188          * Returns a cached instance if such is available otherwise a new one.
1189          *
1190          * @param rowIndex The row index at which the item is located.
1191          * @param rowSpan The number of rows the item spans.
1192          * @param columnIndex The column index at which the item is located.
1193          * @param columnSpan The number of columns the item spans.
1194          * @param heading Whether the item is a heading. This should be set to false and the newer
1195          *                {@link AccessibilityNodeInfoCompat#setHeading(boolean)} used to identify
1196          *                headings.
1197          * @param selected Whether the item is selected.
1198          * @return An instance.
1199          */
obtain(int rowIndex, int rowSpan, int columnIndex, int columnSpan, boolean heading, boolean selected)1200         public static CollectionItemInfoCompat obtain(int rowIndex, int rowSpan,
1201                 int columnIndex, int columnSpan, boolean heading, boolean selected) {
1202             return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain(
1203                     rowIndex, rowSpan, columnIndex, columnSpan, heading, selected));
1204         }
1205 
1206         /**
1207          * Returns a cached instance if such is available otherwise a new one.
1208          *
1209          * @param rowIndex The row index at which the item is located.
1210          * @param rowSpan The number of rows the item spans.
1211          * @param columnIndex The column index at which the item is located.
1212          * @param columnSpan The number of columns the item spans.
1213          * @param heading Whether the item is a heading. This should be set to false and the newer
1214          *                {@link AccessibilityNodeInfoCompat#setHeading(boolean)} used to identify
1215          *                headings.
1216          * @return An instance.
1217          */
obtain(int rowIndex, int rowSpan, int columnIndex, int columnSpan, boolean heading)1218         public static CollectionItemInfoCompat obtain(int rowIndex, int rowSpan,
1219                 int columnIndex, int columnSpan, boolean heading) {
1220             return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain(
1221                     rowIndex, rowSpan, columnIndex, columnSpan, heading));
1222         }
1223 
CollectionItemInfoCompat(Object info)1224         CollectionItemInfoCompat(Object info) {
1225             mInfo = info;
1226         }
1227 
1228         /**
1229          * Gets the column index at which the item is located.
1230          *
1231          * @return The column index.
1232          */
getColumnIndex()1233         public int getColumnIndex() {
1234             return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getColumnIndex();
1235         }
1236 
1237         /**
1238          * Gets the number of columns the item spans.
1239          *
1240          * @return The column span.
1241          */
getColumnSpan()1242         public int getColumnSpan() {
1243             return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getColumnSpan();
1244         }
1245 
1246         /**
1247          * Gets the row index at which the item is located.
1248          *
1249          * @return The row index.
1250          */
getRowIndex()1251         public int getRowIndex() {
1252             return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getRowIndex();
1253         }
1254 
1255         /**
1256          * Gets the number of rows the item spans.
1257          *
1258          * @return The row span.
1259          */
getRowSpan()1260         public int getRowSpan() {
1261             return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getRowSpan();
1262         }
1263 
1264         /**
1265          * Gets if the collection item is a heading. For example, section
1266          * heading, table header, etc.
1267          *
1268          * @return If the item is a heading.
1269          * @deprecated Use {@link AccessibilityNodeInfoCompat#isHeading()}
1270          */
1271         @SuppressWarnings("deprecation")
1272         @Deprecated
isHeading()1273         public boolean isHeading() {
1274             return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).isHeading();
1275         }
1276 
1277         /**
1278          * Gets if the collection item is selected.
1279          *
1280          * @return If the item is selected.
1281          */
isSelected()1282         public boolean isSelected() {
1283             return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).isSelected();
1284         }
1285 
1286         /**
1287          * Gets the row title at which the item is located.
1288          *
1289          * @return The row title.
1290          */
getRowTitle()1291         public @Nullable String getRowTitle() {
1292             if (Build.VERSION.SDK_INT >= 33) {
1293                 return Api33Impl.getCollectionItemRowTitle(mInfo);
1294             } else {
1295                 return null;
1296             }
1297         }
1298 
1299         /**
1300          * Gets the column title at which the item is located.
1301          *
1302          * @return The column title.
1303          */
getColumnTitle()1304         public @Nullable String getColumnTitle() {
1305             if (Build.VERSION.SDK_INT >= 33) {
1306                 return Api33Impl.getCollectionItemColumnTitle(mInfo);
1307             } else {
1308                 return null;
1309             }
1310         }
1311 
1312         /**
1313          * Builder for creating {@link CollectionItemInfoCompat} objects.
1314          */
1315         public static final class Builder {
1316             private boolean mHeading;
1317             private int mColumnIndex;
1318             private int mRowIndex;
1319             private int mColumnSpan;
1320             private int mRowSpan;
1321             private boolean mSelected;
1322             private String mRowTitle;
1323             private String mColumnTitle;
1324 
1325             /**
1326              * Creates a new Builder.
1327              */
Builder()1328             public Builder() {
1329             }
1330 
1331             /**
1332              * Sets the collection item is a heading.
1333              *
1334              * @param heading The heading state
1335              * @return This builder
1336              */
setHeading(boolean heading)1337             public @NonNull Builder setHeading(boolean heading) {
1338                 mHeading = heading;
1339                 return this;
1340             }
1341 
1342             /**
1343              * Sets the column index at which the item is located.
1344              *
1345              * @param columnIndex The column index
1346              * @return This builder
1347              */
setColumnIndex(int columnIndex)1348             public @NonNull Builder setColumnIndex(int columnIndex) {
1349                 mColumnIndex = columnIndex;
1350                 return this;
1351             }
1352 
1353             /**
1354              * Sets the row index at which the item is located.
1355              *
1356              * @param rowIndex The row index
1357              * @return This builder
1358              */
setRowIndex(int rowIndex)1359             public @NonNull Builder setRowIndex(int rowIndex) {
1360                 mRowIndex = rowIndex;
1361                 return this;
1362             }
1363 
1364             /**
1365              * Sets the number of columns the item spans.
1366              *
1367              * @param columnSpan The number of columns spans
1368              * @return This builder
1369              */
setColumnSpan(int columnSpan)1370             public @NonNull Builder setColumnSpan(int columnSpan) {
1371                 mColumnSpan = columnSpan;
1372                 return this;
1373             }
1374 
1375             /**
1376              * Sets the number of rows the item spans.
1377              *
1378              * @param rowSpan The number of rows spans
1379              * @return This builder
1380              */
setRowSpan(int rowSpan)1381             public @NonNull Builder setRowSpan(int rowSpan) {
1382                 mRowSpan = rowSpan;
1383                 return this;
1384             }
1385 
1386             /**
1387              * Sets the collection item is selected.
1388              *
1389              * @param selected The number of rows spans
1390              * @return This builder
1391              */
setSelected(boolean selected)1392             public @NonNull Builder setSelected(boolean selected) {
1393                 mSelected = selected;
1394                 return this;
1395             }
1396 
1397             /**
1398              * Sets the row title at which the item is located.
1399              *
1400              * @param rowTitle The row title
1401              * @return This builder
1402              */
setRowTitle(@ullable String rowTitle)1403             public @NonNull Builder setRowTitle(@Nullable String rowTitle) {
1404                 mRowTitle = rowTitle;
1405                 return this;
1406             }
1407 
1408             /**
1409              * Sets the column title at which the item is located.
1410              *
1411              * @param columnTitle The column title
1412              * @return This builder
1413              */
setColumnTitle(@ullable String columnTitle)1414             public @NonNull Builder setColumnTitle(@Nullable String columnTitle) {
1415                 mColumnTitle = columnTitle;
1416                 return this;
1417             }
1418 
1419             /**
1420              * Builds and returns a {@link AccessibilityNodeInfo.CollectionItemInfo}.
1421              */
build()1422             public @NonNull CollectionItemInfoCompat build() {
1423                 if (Build.VERSION.SDK_INT >= 33) {
1424                     return Api33Impl.buildCollectionItemInfoCompat(mHeading, mColumnIndex,
1425                             mRowIndex, mColumnSpan, mRowSpan, mSelected, mRowTitle, mColumnTitle);
1426                 } else {
1427                     return new CollectionItemInfoCompat(
1428                             AccessibilityNodeInfo.CollectionItemInfo.obtain(mRowIndex, mRowSpan,
1429                                     mColumnIndex, mColumnSpan, mHeading, mSelected));
1430                 }
1431             }
1432         }
1433     }
1434 
1435     /**
1436      * Class with information if a node is a range.
1437      */
1438     public static class RangeInfoCompat {
1439         /** Range type: integer. */
1440         public static final int RANGE_TYPE_INT = 0;
1441         /** Range type: float. */
1442         public static final int RANGE_TYPE_FLOAT = 1;
1443         /** Range type: percent with values from zero to one.*/
1444         public static final int RANGE_TYPE_PERCENT = 2;
1445 
1446         /**
1447          * Obtains a cached instance if such is available otherwise a new one.
1448          *
1449          * @param type The type of the range.
1450          * @param min The min value.
1451          * @param max The max value.
1452          * @param current The current value.
1453          * @return The instance
1454          */
obtain(int type, float min, float max, float current)1455         public static RangeInfoCompat obtain(int type, float min, float max, float current) {
1456             return new RangeInfoCompat(
1457                     AccessibilityNodeInfo.RangeInfo.obtain(type, min, max, current));
1458         }
1459 
1460         final Object mInfo;
1461 
RangeInfoCompat(Object info)1462         RangeInfoCompat(Object info) {
1463             mInfo = info;
1464         }
1465 
1466         /**
1467          * Creates a new range.
1468          *
1469          * @param type The type of the range.
1470          * @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no
1471          *            minimum.
1472          * @param max The maximum value. Use {@code Float.POSITIVE_INFINITY} if the range has no
1473          *            maximum.
1474          * @param current The current value.
1475          */
RangeInfoCompat(int type, float min, float max, float current)1476         public RangeInfoCompat(int type, float min, float max, float current) {
1477             if (Build.VERSION.SDK_INT >= 30) {
1478                 mInfo = Api30Impl.createRangeInfo(type, min, max, current);
1479             } else {
1480                 mInfo = AccessibilityNodeInfo.RangeInfo.obtain(type, min, max, current);
1481             }
1482         }
1483 
1484         /**
1485          * Gets the current value.
1486          *
1487          * @return The current value.
1488          */
getCurrent()1489         public float getCurrent() {
1490             return ((AccessibilityNodeInfo.RangeInfo) mInfo).getCurrent();
1491         }
1492 
1493         /**
1494          * Gets the max value.
1495          *
1496          * @return The max value.
1497          */
getMax()1498         public float getMax() {
1499             return ((AccessibilityNodeInfo.RangeInfo) mInfo).getMax();
1500         }
1501 
1502         /**
1503          * Gets the min value.
1504          *
1505          * @return The min value.
1506          */
getMin()1507         public float getMin() {
1508             return ((AccessibilityNodeInfo.RangeInfo) mInfo).getMin();
1509         }
1510 
1511         /**
1512          * Gets the range type.
1513          *
1514          * @return The range type.
1515          *
1516          * @see #RANGE_TYPE_INT
1517          * @see #RANGE_TYPE_FLOAT
1518          * @see #RANGE_TYPE_PERCENT
1519          */
getType()1520         public int getType() {
1521             return ((AccessibilityNodeInfo.RangeInfo) mInfo).getType();
1522         }
1523     }
1524 
1525     /**
1526      * Class with information of touch delegated views and regions.
1527      */
1528     public static final class TouchDelegateInfoCompat {
1529         final TouchDelegateInfo mInfo;
1530 
1531         /**
1532          * Create a new instance of {@link TouchDelegateInfoCompat}.
1533          *
1534          * @param targetMap A map from regions (in view coordinates) to delegated views.
1535          */
TouchDelegateInfoCompat(@onNull Map<Region, View> targetMap)1536         public TouchDelegateInfoCompat(@NonNull Map<Region, View> targetMap) {
1537             if (Build.VERSION.SDK_INT >= 29) {
1538                 mInfo = new TouchDelegateInfo(targetMap);
1539             } else {
1540                 mInfo = null;
1541             }
1542         }
1543 
TouchDelegateInfoCompat(@onNull TouchDelegateInfo info)1544         TouchDelegateInfoCompat(@NonNull TouchDelegateInfo info) {
1545             mInfo = info;
1546         }
1547 
1548         /**
1549          * Returns the number of touch delegate target region.
1550          * <p>
1551          * Compatibility:
1552          * <ul>
1553          *     <li>API &lt; 29: Always returns {@code 0}</li>
1554          * </ul>
1555          *
1556          * @return Number of touch delegate target region.
1557          */
getRegionCount()1558         public @IntRange(from = 0) int getRegionCount() {
1559             if (Build.VERSION.SDK_INT >= 29) {
1560                 return mInfo.getRegionCount();
1561             }
1562             return 0;
1563         }
1564 
1565         /**
1566          * Return the {@link Region} at the given index.
1567          * <p>
1568          * Compatibility:
1569          * <ul>
1570          *     <li>API &lt; 29: Always returns {@code null}</li>
1571          * </ul>
1572          *
1573          * @param index The desired index, must be between 0 and {@link #getRegionCount()}-1.
1574          * @return Returns the {@link Region} stored at the given index.
1575          */
getRegionAt(@ntRangefrom = 0) int index)1576         public @Nullable Region getRegionAt(@IntRange(from = 0) int index) {
1577             if (Build.VERSION.SDK_INT >= 29) {
1578                 return mInfo.getRegionAt(index);
1579             }
1580             return null;
1581         }
1582 
1583         /**
1584          * Return the target {@link AccessibilityNodeInfoCompat} for the given {@link Region}.
1585          * <p>
1586          *   <strong>Note:</strong> This api can only be called from
1587          *   {@link android.accessibilityservice.AccessibilityService}.
1588          * </p>
1589          * <p>
1590          * Compatibility:
1591          * <ul>
1592          *     <li>API &lt; 29: Always returns {@code null}</li>
1593          * </ul>
1594          *
1595          * @param region The region retrieved from {@link #getRegionAt(int)}.
1596          * @return The target node associates with the given region.
1597          */
getTargetForRegion(@onNull Region region)1598         public @Nullable AccessibilityNodeInfoCompat getTargetForRegion(@NonNull Region region) {
1599             if (Build.VERSION.SDK_INT >= 29) {
1600                 AccessibilityNodeInfo info = mInfo.getTargetForRegion(region);
1601                 if (info != null) {
1602                     return AccessibilityNodeInfoCompat.wrap(info);
1603                 }
1604             }
1605             return null;
1606         }
1607     }
1608 
1609     private static final String ROLE_DESCRIPTION_KEY =
1610             "AccessibilityNodeInfo.roleDescription";
1611 
1612     private static final String PANE_TITLE_KEY =
1613             "androidx.view.accessibility.AccessibilityNodeInfoCompat.PANE_TITLE_KEY";
1614 
1615     private static final String TOOLTIP_TEXT_KEY =
1616             "androidx.view.accessibility.AccessibilityNodeInfoCompat.TOOLTIP_TEXT_KEY";
1617 
1618     private static final String HINT_TEXT_KEY =
1619             "androidx.view.accessibility.AccessibilityNodeInfoCompat.HINT_TEXT_KEY";
1620 
1621     private static final String BOOLEAN_PROPERTY_KEY =
1622             "androidx.view.accessibility.AccessibilityNodeInfoCompat.BOOLEAN_PROPERTY_KEY";
1623 
1624     private static final String SPANS_ID_KEY =
1625             "androidx.view.accessibility.AccessibilityNodeInfoCompat.SPANS_ID_KEY";
1626 
1627     private static final String SPANS_START_KEY =
1628             "androidx.view.accessibility.AccessibilityNodeInfoCompat.SPANS_START_KEY";
1629 
1630     private static final String SPANS_END_KEY =
1631             "androidx.view.accessibility.AccessibilityNodeInfoCompat.SPANS_END_KEY";
1632 
1633     private static final String SPANS_FLAGS_KEY =
1634             "androidx.view.accessibility.AccessibilityNodeInfoCompat.SPANS_FLAGS_KEY";
1635 
1636     private static final String SPANS_ACTION_ID_KEY =
1637             "androidx.view.accessibility.AccessibilityNodeInfoCompat.SPANS_ACTION_ID_KEY";
1638 
1639     private static final String STATE_DESCRIPTION_KEY =
1640             "androidx.view.accessibility.AccessibilityNodeInfoCompat.STATE_DESCRIPTION_KEY";
1641 
1642     private static final String SUPPLEMENTAL_DESCRIPTION_KEY =
1643             "androidx.view.accessibility.AccessibilityNodeInfoCompat.SUPPLEMENTAL_DESCRIPTION_KEY";
1644 
1645     private static final String UNIQUE_ID_KEY =
1646             "androidx.view.accessibility.AccessibilityNodeInfoCompat.UNIQUE_ID_KEY";
1647 
1648     private static final String CONTAINER_TITLE_KEY =
1649             "androidx.view.accessibility.AccessibilityNodeInfoCompat.CONTAINER_TITLE_KEY";
1650 
1651     private static final String BOUNDS_IN_WINDOW_KEY =
1652             "androidx.view.accessibility.AccessibilityNodeInfoCompat.BOUNDS_IN_WINDOW_KEY";
1653 
1654     private static final String EXPANDED_STATE_KEY =
1655             "androidx.view.accessibility.AccessibilityNodeInfoCompat.EXPANDED_STATE_KEY";
1656 
1657     private static final String MIN_DURATION_BETWEEN_CONTENT_CHANGES_KEY =
1658             "androidx.view.accessibility.AccessibilityNodeInfoCompat."
1659                     + "MIN_DURATION_BETWEEN_CONTENT_CHANGES_KEY";
1660     private static final String IS_REQUIRED_KEY =
1661             "androidx.view.accessibility.AccessibilityNodeInfoCompat.IS_REQUIRED_KEY";
1662 
1663     private static final String CHECKED_KEY =
1664             "androidx.view.accessibility.AccessibilityNodeInfoCompat.CHECKED_KEY";
1665 
1666     // These don't line up with the internal framework constants, since they are independent
1667     // and we might as well get all 32 bits of utility here.
1668     private static final int BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE = 0x00000001;
1669     private static final int BOOLEAN_PROPERTY_IS_HEADING = 0x00000002;
1670     private static final int BOOLEAN_PROPERTY_IS_SHOWING_HINT = 0x00000004;
1671     private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x00000008;
1672 
1673     private static final int BOOLEAN_PROPERTY_HAS_REQUEST_INITIAL_ACCESSIBILITY_FOCUS = 1 << 5;
1674     private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_DATA_SENSITIVE = 1 << 6;
1675     private static final int BOOLEAN_PROPERTY_TEXT_SELECTABLE = 1 << 23;
1676     private static final int BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING = 1 << 26;
1677 
1678     private final AccessibilityNodeInfo mInfo;
1679 
1680     /**
1681      *  androidx.customview.widget.ExploreByTouchHelper.HOST_ID = -1;
1682      */
1683     @RestrictTo(LIBRARY_GROUP_PREFIX)
1684     public int mParentVirtualDescendantId = NO_ID;
1685 
1686     private int mVirtualDescendantId = NO_ID;
1687 
1688     // Actions introduced in IceCreamSandwich
1689 
1690     /**
1691      * Action that focuses the node.
1692      * @see AccessibilityActionCompat#ACTION_FOCUS
1693      */
1694     public static final int ACTION_FOCUS = 0x00000001;
1695 
1696     /**
1697      * Action that unfocuses the node.
1698      * @see AccessibilityActionCompat#ACTION_CLEAR_FOCUS
1699      */
1700     public static final int ACTION_CLEAR_FOCUS = 0x00000002;
1701 
1702     /**
1703      * Action that selects the node.
1704      * @see AccessibilityActionCompat#ACTION_SELECT
1705      */
1706     public static final int ACTION_SELECT = 0x00000004;
1707 
1708     /**
1709      * Action that unselects the node.
1710      * @see AccessibilityActionCompat#ACTION_CLEAR_SELECTION
1711      */
1712     public static final int ACTION_CLEAR_SELECTION = 0x00000008;
1713 
1714     /**
1715      * Action that clicks on the node info.
1716      * @see AccessibilityActionCompat#ACTION_CLICK
1717      */
1718     public static final int ACTION_CLICK = 0x00000010;
1719 
1720     /**
1721      * Action that long clicks on the node.
1722      * @see AccessibilityActionCompat#ACTION_LONG_CLICK
1723      */
1724     public static final int ACTION_LONG_CLICK = 0x00000020;
1725 
1726     // Actions introduced in JellyBean
1727 
1728     /**
1729      * Action that gives accessibility focus to the node.
1730      * @see AccessibilityActionCompat#ACTION_ACCESSIBILITY_FOCUS
1731      */
1732     public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040;
1733 
1734     /**
1735      * Action that clears accessibility focus of the node.
1736      */
1737     public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080;
1738 
1739     /**
1740      * Action that requests to go to the next entity in this node's text
1741      * at a given movement granularity. For example, move to the next character,
1742      * word, etc.
1743      * <p>
1744      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
1745      * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
1746      * <strong>Example:</strong> Move to the previous character and do not extend selection.
1747      * <code><pre><p>
1748      *   Bundle arguments = new Bundle();
1749      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
1750      *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
1751      *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
1752      *           false);
1753      *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
1754      * </code></pre></p>
1755      * </p>
1756      *
1757      * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
1758      * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
1759      *
1760      * @see #setMovementGranularities(int)
1761      * @see #getMovementGranularities()
1762      *
1763      * @see #MOVEMENT_GRANULARITY_CHARACTER
1764      * @see #MOVEMENT_GRANULARITY_WORD
1765      * @see #MOVEMENT_GRANULARITY_LINE
1766      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
1767      * @see #MOVEMENT_GRANULARITY_PAGE
1768      * @see AccessibilityActionCompat#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
1769      */
1770     public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100;
1771 
1772     /**
1773      * Action that requests to go to the previous entity in this node's text
1774      * at a given movement granularity. For example, move to the next character,
1775      * word, etc.
1776      * <p>
1777      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
1778      * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
1779      * <strong>Example:</strong> Move to the next character and do not extend selection.
1780      * <code><pre><p>
1781      *   Bundle arguments = new Bundle();
1782      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
1783      *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
1784      *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
1785      *           false);
1786      *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
1787      *           arguments);
1788      * </code></pre></p>
1789      * </p>
1790      *
1791      * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
1792      * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
1793      *
1794      * @see #setMovementGranularities(int)
1795      * @see #getMovementGranularities()
1796      *
1797      * @see #MOVEMENT_GRANULARITY_CHARACTER
1798      * @see #MOVEMENT_GRANULARITY_WORD
1799      * @see #MOVEMENT_GRANULARITY_LINE
1800      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
1801      * @see #MOVEMENT_GRANULARITY_PAGE
1802      * @see AccessibilityActionCompat#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
1803      */
1804     public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200;
1805 
1806     /**
1807      * Action to move to the next HTML element of a given type. For example, move
1808      * to the BUTTON, INPUT, TABLE, etc.
1809      * <p>
1810      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
1811      * <strong>Example:</strong>
1812      * <code><pre><p>
1813      *   Bundle arguments = new Bundle();
1814      *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
1815      *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
1816      * </code></pre></p>
1817      * </p>
1818      * @see AccessibilityActionCompat#ACTION_NEXT_HTML_ELEMENT
1819      */
1820     public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;
1821 
1822     /**
1823      * Action to move to the previous HTML element of a given type. For example, move
1824      * to the BUTTON, INPUT, TABLE, etc.
1825      * <p>
1826      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
1827      * <strong>Example:</strong>
1828      * <code><pre><p>
1829      *   Bundle arguments = new Bundle();
1830      *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
1831      *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
1832      * </code></pre></p>
1833      * </p>
1834      *
1835      * @see AccessibilityActionCompat#ACTION_PREVIOUS_HTML_ELEMENT
1836      */
1837     public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
1838 
1839     /**
1840      * Action to scroll the node content forward.
1841      * @see AccessibilityActionCompat#ACTION_SCROLL_FORWARD
1842      */
1843     public static final int ACTION_SCROLL_FORWARD = 0x00001000;
1844 
1845     /**
1846      * Action to scroll the node content backward.
1847      * @see AccessibilityActionCompat#ACTION_SCROLL_BACKWARD
1848      */
1849     public static final int ACTION_SCROLL_BACKWARD = 0x00002000;
1850 
1851     // Actions introduced in JellyBeanMr2
1852 
1853     /**
1854      * Action to copy the current selection to the clipboard.
1855      * @see AccessibilityActionCompat#ACTION_COPY
1856      */
1857     public static final int ACTION_COPY = 0x00004000;
1858 
1859     /**
1860      * Action to paste the current clipboard content.
1861      * @see AccessibilityActionCompat#ACTION_PASTE
1862      */
1863     public static final int ACTION_PASTE = 0x00008000;
1864 
1865     /**
1866      * Action to cut the current selection and place it to the clipboard.
1867      * @see AccessibilityActionCompat#ACTION_CUT
1868      */
1869     public static final int ACTION_CUT = 0x00010000;
1870 
1871     /**
1872      * Action to set the selection. Performing this action with no arguments
1873      * clears the selection.
1874      * <p>
1875      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SELECTION_START_INT},
1876      * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br>
1877      * <strong>Example:</strong>
1878      * <code><pre><p>
1879      *   Bundle arguments = new Bundle();
1880      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1);
1881      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2);
1882      *   info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments);
1883      * </code></pre></p>
1884      * </p>
1885      *
1886      * @see #ACTION_ARGUMENT_SELECTION_START_INT
1887      * @see #ACTION_ARGUMENT_SELECTION_END_INT
1888      * @see AccessibilityActionCompat#ACTION_SET_SELECTION
1889      */
1890     public static final int ACTION_SET_SELECTION = 0x00020000;
1891 
1892     /**
1893      * Action to expand an expandable node.
1894      * @see AccessibilityActionCompat#ACTION_EXPAND
1895      */
1896     public static final int ACTION_EXPAND = 0x00040000;
1897 
1898     /**
1899      * Action to collapse an expandable node.
1900      * @see AccessibilityActionCompat#ACTION_COLLAPSE
1901      */
1902     public static final int ACTION_COLLAPSE = 0x00080000;
1903 
1904     /**
1905      * Action to dismiss a dismissible node.
1906      * @see AccessibilityActionCompat#ACTION_DISMISS
1907      */
1908     public static final int ACTION_DISMISS = 0x00100000;
1909 
1910     /**
1911      * Action that sets the text of the node. Performing the action without argument, using <code>
1912      * null</code> or empty {@link CharSequence} will clear the text. This action will also put the
1913      * cursor at the end of text.
1914      * <p>
1915      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
1916      * <strong>Example:</strong>
1917      * <code><pre><p>
1918      *   Bundle arguments = new Bundle();
1919      *   arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
1920      *       "android");
1921      *   info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
1922      * </code></pre></p>
1923      * @see AccessibilityActionCompat#ACTION_SET_TEXT
1924      */
1925     public static final int ACTION_SET_TEXT = 0x00200000;
1926 
1927     // Action arguments
1928 
1929     /**
1930      * Argument for which movement granularity to be used when traversing the node text.
1931      * <p>
1932      * <strong>Type:</strong> int<br>
1933      * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
1934      * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
1935      * </p>
1936      */
1937     public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
1938         "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
1939 
1940     /**
1941      * Argument for which HTML element to get moving to the next/previous HTML element.
1942      * <p>
1943      * <strong>Type:</strong> String<br>
1944      * <strong>Actions:</strong> {@link AccessibilityActionCompat#ACTION_NEXT_HTML_ELEMENT},
1945      *         {@link AccessibilityActionCompat#ACTION_PREVIOUS_HTML_ELEMENT}
1946      * </p>
1947      */
1948     public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
1949         "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
1950 
1951     /**
1952      * Argument for whether when moving at granularity to extend the selection
1953      * or to move it otherwise.
1954      * <p>
1955      * <strong>Type:</strong> boolean<br>
1956      * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
1957      * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
1958      * </p>
1959      *
1960      * @see AccessibilityActionCompat#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
1961      * @see AccessibilityActionCompat#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
1962      */
1963     public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN =
1964             "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
1965 
1966     /**
1967      * Argument for specifying the selection start.
1968      * <p>
1969      * <strong>Type:</strong> int<br>
1970      * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
1971      * </p>
1972      *
1973      * @see AccessibilityActionCompat#ACTION_SET_SELECTION
1974      */
1975     public static final String ACTION_ARGUMENT_SELECTION_START_INT =
1976             "ACTION_ARGUMENT_SELECTION_START_INT";
1977 
1978     /**
1979      * Argument for specifying the selection end.
1980      * <p>
1981      * <strong>Type:</strong> int<br>
1982      * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
1983      * </p>
1984      *
1985      * @see AccessibilityActionCompat#ACTION_SET_SELECTION
1986      */
1987     public static final String ACTION_ARGUMENT_SELECTION_END_INT =
1988             "ACTION_ARGUMENT_SELECTION_END_INT";
1989 
1990     /**
1991      * Argument for specifying the text content to set
1992      * <p>
1993      * <strong>Type:</strong> CharSequence<br>
1994      * <strong>Actions:</strong> {@link #ACTION_SET_TEXT}
1995      * </p>
1996      *
1997      * @see AccessibilityActionCompat#ACTION_SET_TEXT
1998      */
1999     public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE =
2000             "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
2001 
2002     /**
2003      * Argument for specifying the collection row to make visible on screen.
2004      * <p>
2005      * <strong>Type:</strong> int<br>
2006      * <strong>Actions:</strong>
2007      * <ul>
2008      *     <li>{@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION}</li>
2009      * </ul>
2010      *
2011      * @see AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION
2012      */
2013     public static final String ACTION_ARGUMENT_ROW_INT =
2014             "android.view.accessibility.action.ARGUMENT_ROW_INT";
2015 
2016     /**
2017      * Argument for specifying the collection column to make visible on screen.
2018      * <p>
2019      * <strong>Type:</strong> int<br>
2020      * <strong>Actions:</strong>
2021      * <ul>
2022      *     <li>{@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION}</li>
2023      * </ul>
2024      *
2025      * @see AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION
2026      */
2027     public static final String ACTION_ARGUMENT_COLUMN_INT =
2028             "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
2029 
2030     /**
2031      * Argument for specifying the progress value to set.
2032      * <p>
2033      * <strong>Type:</strong> float<br>
2034      * <strong>Actions:</strong>
2035      * <ul>
2036      *     <li>{@link AccessibilityActionCompat#ACTION_SET_PROGRESS}</li>
2037      * </ul>
2038      *
2039      * @see AccessibilityActionCompat#ACTION_SET_PROGRESS
2040      */
2041     public static final String ACTION_ARGUMENT_PROGRESS_VALUE =
2042             "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
2043 
2044     /**
2045      * Argument for specifying the x coordinate to which to move a window.
2046      * <p>
2047      * <strong>Type:</strong> int<br>
2048      * <strong>Actions:</strong>
2049      * <ul>
2050      *     <li>{@link AccessibilityActionCompat#ACTION_MOVE_WINDOW}</li>
2051      * </ul>
2052      *
2053      * @see AccessibilityActionCompat#ACTION_MOVE_WINDOW
2054      */
2055     public static final String ACTION_ARGUMENT_MOVE_WINDOW_X =
2056             "ACTION_ARGUMENT_MOVE_WINDOW_X";
2057 
2058     /**
2059      * Argument for specifying the y coordinate to which to move a window.
2060      * <p>
2061      * <strong>Type:</strong> int<br>
2062      * <strong>Actions:</strong>
2063      * <ul>
2064      *     <li>{@link AccessibilityActionCompat#ACTION_MOVE_WINDOW}</li>
2065      * </ul>
2066      *
2067      * @see AccessibilityActionCompat#ACTION_MOVE_WINDOW
2068      */
2069     public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y =
2070             "ACTION_ARGUMENT_MOVE_WINDOW_Y";
2071 
2072     /**
2073      * Argument to represent the duration in milliseconds to press and hold a node.
2074      * <p>
2075      * <strong>Type:</strong> int<br>
2076      * <strong>Actions:</strong>
2077      * <ul>
2078      *     <li>{@link AccessibilityActionCompat#ACTION_PRESS_AND_HOLD}</li>
2079      * </ul>
2080      *
2081      * @see AccessibilityActionCompat#ACTION_PRESS_AND_HOLD
2082      */
2083     @SuppressLint("ActionValue")
2084     public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT =
2085             "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
2086 
2087     /**
2088      * <p>Argument to represent the direction when using
2089      * {@link AccessibilityActionCompat#ACTION_SCROLL_IN_DIRECTION}.</p>
2090      *
2091      * <p>
2092      *     The value of this argument can be one of:
2093      *     <ul>
2094      *         <li>{@link View#FOCUS_DOWN}</li>
2095      *         <li>{@link View#FOCUS_UP}</li>
2096      *         <li>{@link View#FOCUS_LEFT}</li>
2097      *         <li>{@link View#FOCUS_RIGHT}</li>
2098      *         <li>{@link View#FOCUS_FORWARD}</li>
2099      *         <li>{@link View#FOCUS_BACKWARD}</li>
2100      *     </ul>
2101      * </p>
2102      */
2103     public static final String ACTION_ARGUMENT_DIRECTION_INT =
2104             "androidx.core.view.accessibility.action.ARGUMENT_DIRECTION_INT";
2105 
2106     /**
2107      * <p>Argument to represent the scroll amount as a percent of the visible area of a node, with
2108      * 1.0F as the default. Values smaller than 1.0F represent a partial scroll of the node, and
2109      * values larger than 1.0F represent a scroll that extends beyond the currently visible node
2110      * Rect. Setting this to {@link Float#POSITIVE_INFINITY} or to another "too large" value should
2111      * scroll to the end of the node. Negative values should not be used with this argument.
2112      * </p>
2113      *
2114      * <p>
2115      *     This argument should be used with the following scroll actions:
2116      *     <ul>
2117      *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_FORWARD}</li>
2118      *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_BACKWARD}</li>
2119      *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_UP}</li>
2120      *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_DOWN}</li>
2121      *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_LEFT}</li>
2122      *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_RIGHT}</li>
2123      *     </ul>
2124      * </p>
2125      * <p>
2126      *     Example: if a view representing a list of items implements
2127      *     {@link AccessibilityActionCompat#ACTION_SCROLL_FORWARD} to scroll forward by an entire
2128      *     screen
2129      *     (one "page"), then passing a value of .25F via this argument should scroll that view
2130      *     only by 1/4th of a screen. Passing a value of 1.50F via this argument should scroll the
2131      *     view by 1 1/2 screens or to end of the node if the node doesn't extend to 1 1/2 screens.
2132      * </p>
2133      *
2134      * <p>
2135      *     This argument should not be used with the following scroll actions, which don't cleanly
2136      *     conform to granular scroll semantics:
2137      *     <ul>
2138      *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_IN_DIRECTION}</li>
2139      *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION}</li>
2140      *     </ul>
2141      * </p>
2142      *
2143      * <p>
2144      *     Views that support this argument should set
2145      *     {@link #setGranularScrollingSupported(boolean)} to true. Clients should use
2146      *     {@link #isGranularScrollingSupported()} to check if granular scrolling is supported.
2147      * </p>
2148      */
2149     public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT =
2150             "androidx.core.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
2151 
2152     // Focus types
2153 
2154     /**
2155      * The input focus.
2156      */
2157     public static final int FOCUS_INPUT = 1;
2158 
2159     /**
2160      * The accessibility focus.
2161      */
2162     public static final int FOCUS_ACCESSIBILITY = 2;
2163 
2164     // Movement granularities
2165 
2166     /**
2167      * Movement granularity bit for traversing the text of a node by character.
2168      */
2169     public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001;
2170 
2171     /**
2172      * Movement granularity bit for traversing the text of a node by word.
2173      */
2174     public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002;
2175 
2176     /**
2177      * Movement granularity bit for traversing the text of a node by line.
2178      */
2179     public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004;
2180 
2181     /**
2182      * Movement granularity bit for traversing the text of a node by paragraph.
2183      */
2184     public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008;
2185 
2186     /**
2187      * Movement granularity bit for traversing the text of a node by page.
2188      */
2189     public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010;
2190 
2191     /**
2192      * Key used to request and locate extra data for text character location. This key requests that
2193      * an array of {@link android.graphics.RectF}s be added to the extras. This request is made with
2194      * {@link android.view.accessibility.AccessibilityNodeInfo#refreshWithExtraData(String, Bundle)}.
2195      * The arguments taken by this request are two integers:
2196      * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX} and
2197      * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH}. The starting index must be valid
2198      * inside the CharSequence returned by {@link #getText()}, and the length must be positive.
2199      * <p>
2200      * The data can be retrieved from the {@code Bundle} returned by {@link #getExtras()} using this
2201      * string as a key for {@link Bundle#getParcelableArray(String)}. The
2202      * {@link android.graphics.RectF} will be {@code null} for characters that either do not exist
2203      * or are off the screen.
2204      * <p>
2205      * Note that character locations returned are modified by changes in display magnification.
2206      *
2207      * {@see android.view.accessibility.AccessibilityNodeInfo#refreshWithExtraData(String, Bundle)}
2208      */
2209     @SuppressWarnings("ActionValue")
2210     public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY =
2211             "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
2212 
2213     /**
2214      * Key used to request and locate extra data for text character location in
2215      * window coordinates. This key requests that an array of
2216      * {@link android.graphics.RectF}s be added to the extras. This request is made with
2217      * {@link android.view.accessibility.AccessibilityNodeInfo#refreshWithExtraData(String, Bundle)}.
2218      * The arguments taken by this request are two integers:
2219      * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX} and
2220      * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH}. The starting index
2221      * must be valid inside the CharSequence returned by {@link #getText()}, and
2222      * the length must be positive.
2223      * <p>
2224      * Providers may advertise that they support text characters in window coordinates using
2225      * {@link #setAvailableExtraData(List)}. Services may check if an implementation supports text
2226      * characters in window coordinates with {@link #getAvailableExtraData()}.
2227      * <p>
2228      * The data can be retrieved from the {@code Bundle} returned by
2229      * {@link #getExtras()} using this string as a key for
2230      * {@link Bundle#getParcelableArray(String, Class)}. The
2231      * {@link android.graphics.RectF} will be {@code null} for characters that either do
2232      * not exist or are outside of the window bounds.
2233      * <p>
2234      * Note that character locations in window bounds are not modified by
2235      * changes in display magnification.
2236      *
2237      * {@see android.view.accessibility.AccessibilityNodeInfo#refreshWithExtraData(String, Bundle)}
2238      */
2239     @SuppressWarnings("ActionValue")
2240     public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY =
2241             "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY";
2242 
2243     /**
2244      * Integer argument specifying the start index of the requested text location data. Must be
2245      * valid inside the CharSequence returned by {@link #getText()}.
2246      *
2247      * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
2248      */
2249     @SuppressWarnings("ActionValue")
2250     public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX =
2251             "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
2252 
2253     /**
2254      * Integer argument specifying the end index of the requested text location data. Must be
2255      * positive and no larger than {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH}.
2256      *
2257      * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
2258      */
2259     @SuppressWarnings("ActionValue")
2260     public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH =
2261             "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
2262 
2263     /**
2264      * The maximum allowed length of the requested text location data.
2265      */
2266     public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000;
2267 
2268     /**
2269      * Prefetching strategy that prefetches the ancestors of the requested node.
2270      * <p> Ancestors will be prefetched before siblings and descendants.
2271      *
2272      * @see #getChild(int, int)
2273      * @see #getParent(int)
2274      * @see AccessibilityWindowInfoCompat#getRoot(int)
2275      * @see AccessibilityService#getRootInActiveWindow(int)
2276      * @see AccessibilityEvent#getSource(int)
2277      */
2278     public static final int FLAG_PREFETCH_ANCESTORS = 0x00000001;
2279 
2280     /**
2281      * Prefetching strategy that prefetches the siblings of the requested node.
2282      * <p> To avoid disconnected trees, this flag will also prefetch the parent. Siblings will be
2283      * prefetched before descendants.
2284      *
2285      * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
2286      */
2287     public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002;
2288 
2289     /**
2290      * Prefetching strategy that prefetches the descendants in a hybrid depth first and breadth
2291      * first approach.
2292      * <p> The children of the root node is prefetched before recursing on the children. This
2293      * must not be combined with {@link #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST} or
2294      * {@link #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST} or this will trigger an
2295      * IllegalArgumentException.
2296      *
2297      * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
2298      */
2299     public static final int FLAG_PREFETCH_DESCENDANTS_HYBRID = 0x00000004;
2300 
2301     /**
2302      * Prefetching strategy that prefetches the descendants of the requested node depth-first.
2303      * <p> This must not be combined with {@link #FLAG_PREFETCH_DESCENDANTS_HYBRID} or
2304      * {@link #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST} or this will trigger an
2305      * IllegalArgumentException.
2306      *
2307      * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
2308      */
2309     public static final int FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST = 0x00000008;
2310 
2311     /**
2312      * Prefetching strategy that prefetches the descendants of the requested node breadth-first.
2313      * <p> This must not be combined with {@link #FLAG_PREFETCH_DESCENDANTS_HYBRID} or
2314      * {@link #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST} or this will trigger an
2315      * IllegalArgumentException.
2316      *
2317      * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
2318      */
2319     public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST = 0x00000010;
2320 
2321     /**
2322      * Prefetching flag that specifies prefetching should not be interrupted by a request to
2323      * retrieve a node or perform an action on a node.
2324      *
2325      * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
2326      */
2327     public static final int FLAG_PREFETCH_UNINTERRUPTIBLE = 0x00000020;
2328 
2329     /**
2330      * Maximum batch size of prefetched nodes for a request.
2331      */
2332     @SuppressLint("MinMaxConstant")
2333     public static final int MAX_NUMBER_OF_PREFETCHED_NODES = 50;
2334 
2335     @IntDef(
2336             flag = false,
2337             value = {
2338                 AccessibilityNodeInfo.EXPANDED_STATE_UNDEFINED,
2339                 AccessibilityNodeInfo.EXPANDED_STATE_COLLAPSED,
2340                 AccessibilityNodeInfo.EXPANDED_STATE_PARTIAL,
2341                 AccessibilityNodeInfo.EXPANDED_STATE_FULL,
2342             })
2343     @Retention(RetentionPolicy.SOURCE)
2344     private @interface ExpandedState {}
2345 
2346     @IntDef(
2347             flag = false,
2348             value = {
2349                 AccessibilityNodeInfo.CHECKED_STATE_FALSE,
2350                 AccessibilityNodeInfo.CHECKED_STATE_TRUE,
2351                 AccessibilityNodeInfo.CHECKED_STATE_PARTIAL,
2352             })
2353     @Retention(RetentionPolicy.SOURCE)
2354     private @interface CheckedState {}
2355 
2356     private static int sClickableSpanId = 0;
2357 
2358     /**
2359      * Creates a wrapper for info implementation.
2360      *
2361      * @param object The info to wrap.
2362      * @return A wrapper for if the object is not null, null otherwise.
2363      */
2364     @SuppressWarnings("deprecation")
wrapNonNullInstance(Object object)2365     static AccessibilityNodeInfoCompat wrapNonNullInstance(Object object) {
2366         if (object != null) {
2367             return new AccessibilityNodeInfoCompat(object);
2368         }
2369         return null;
2370     }
2371 
2372     /**
2373      * Creates a new instance wrapping an
2374      * {@link android.view.accessibility.AccessibilityNodeInfo}.
2375      *
2376      * @param info The info.
2377      *
2378      * @deprecated Use {@link #wrap(AccessibilityNodeInfo)} instead.
2379      */
2380     @Deprecated
AccessibilityNodeInfoCompat(Object info)2381     public AccessibilityNodeInfoCompat(Object info) {
2382         mInfo = (AccessibilityNodeInfo) info;
2383     }
2384 
AccessibilityNodeInfoCompat(AccessibilityNodeInfo info)2385     private AccessibilityNodeInfoCompat(AccessibilityNodeInfo info) {
2386         mInfo = info;
2387     }
2388 
2389     /**
2390      * Creates a new instance wrapping an
2391      * {@link android.view.accessibility.AccessibilityNodeInfo}.
2392      *
2393      * @param info The info.
2394      */
wrap(@onNull AccessibilityNodeInfo info)2395     public static AccessibilityNodeInfoCompat wrap(@NonNull AccessibilityNodeInfo info) {
2396         return new AccessibilityNodeInfoCompat(info);
2397     }
2398 
2399     /**
2400      * @return The unwrapped {@link android.view.accessibility.AccessibilityNodeInfo}.
2401      */
unwrap()2402     public AccessibilityNodeInfo unwrap() {
2403         return mInfo;
2404     }
2405 
2406     /**
2407      * @return The wrapped {@link android.view.accessibility.AccessibilityNodeInfo}.
2408      *
2409      * @deprecated Use {@link #unwrap()} instead.
2410      */
2411     @Deprecated
getInfo()2412     public Object getInfo() {
2413         return mInfo;
2414     }
2415 
2416     /**
2417      * Returns a cached instance if such is available otherwise a new one and
2418      * sets the source.
2419      *
2420      * @return An instance.
2421      * @see #setSource(View)
2422      */
obtain(View source)2423     public static AccessibilityNodeInfoCompat obtain(View source) {
2424         return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain(source));
2425     }
2426 
2427     /**
2428      * Returns a cached instance if such is available otherwise a new one
2429      * and sets the source.
2430      *
2431      * @param root The root of the virtual subtree.
2432      * @param virtualDescendantId The id of the virtual descendant.
2433      * @return An instance.
2434      *
2435      * @see #setSource(View, int)
2436      */
obtain(View root, int virtualDescendantId)2437     public static AccessibilityNodeInfoCompat obtain(View root, int virtualDescendantId) {
2438         return AccessibilityNodeInfoCompat.wrapNonNullInstance(
2439                 AccessibilityNodeInfo.obtain(root, virtualDescendantId));
2440     }
2441 
2442     /**
2443      * Returns a cached instance if such is available otherwise a new one.
2444      *
2445      * @return An instance.
2446      */
obtain()2447     public static AccessibilityNodeInfoCompat obtain() {
2448         return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain());
2449     }
2450 
2451     /**
2452      * Returns a cached instance if such is available or a new one is create.
2453      * The returned instance is initialized from the given <code>info</code>.
2454      *
2455      * @param info The other info.
2456      * @return An instance.
2457      */
obtain(AccessibilityNodeInfoCompat info)2458     public static AccessibilityNodeInfoCompat obtain(AccessibilityNodeInfoCompat info) {
2459         return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain(info.mInfo));
2460     }
2461 
2462     /**
2463      * Sets the source.
2464      *
2465      * @param source The info source.
2466      */
setSource(View source)2467     public void setSource(View source) {
2468         mVirtualDescendantId = NO_ID;
2469 
2470         mInfo.setSource(source);
2471     }
2472 
2473     /**
2474      * Sets the source to be a virtual descendant of the given <code>root</code>.
2475      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
2476      * is set as the source.
2477      * <p>
2478      * A virtual descendant is an imaginary View that is reported as a part of the view
2479      * hierarchy for accessibility purposes. This enables custom views that draw complex
2480      * content to report themselves as a tree of virtual views, thus conveying their
2481      * logical structure.
2482      * <p>
2483      * <strong>Note:</strong> Cannot be called from an
2484      * {@link android.accessibilityservice.AccessibilityService}.
2485      * This class is made immutable before being delivered to an AccessibilityService.
2486      * <p>
2487      * This method is not supported on devices running API level < 16 since the platform did
2488      * not support virtual descendants of real views.
2489      *
2490      * @param root The root of the virtual subtree.
2491      * @param virtualDescendantId The id of the virtual descendant.
2492      */
setSource(View root, int virtualDescendantId)2493     public void setSource(View root, int virtualDescendantId) {
2494         // Store the ID anyway, since we may need it for equality checks.
2495         mVirtualDescendantId = virtualDescendantId;
2496 
2497         mInfo.setSource(root, virtualDescendantId);
2498     }
2499 
2500     /**
2501      * Find the view that has the specified focus type. The search starts from
2502      * the view represented by this node info.
2503      *
2504      * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
2505      *         {@link #FOCUS_ACCESSIBILITY}.
2506      * @return The node info of the focused view or null.
2507      *
2508      * @see #FOCUS_INPUT
2509      * @see #FOCUS_ACCESSIBILITY
2510      */
findFocus(int focus)2511     public AccessibilityNodeInfoCompat findFocus(int focus) {
2512         return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.findFocus(focus));
2513     }
2514 
2515     /**
2516      * Searches for the nearest view in the specified direction that can take
2517      * input focus.
2518      *
2519      * @param direction The direction. Can be one of:
2520      *     {@link View#FOCUS_DOWN},
2521      *     {@link View#FOCUS_UP},
2522      *     {@link View#FOCUS_LEFT},
2523      *     {@link View#FOCUS_RIGHT},
2524      *     {@link View#FOCUS_FORWARD},
2525      *     {@link View#FOCUS_BACKWARD}.
2526      *
2527      * @return The node info for the view that can take accessibility focus.
2528      */
focusSearch(int direction)2529     public AccessibilityNodeInfoCompat focusSearch(int direction) {
2530         return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.focusSearch(direction));
2531     }
2532 
2533     /**
2534      * Gets the id of the window from which the info comes from.
2535      *
2536      * @return The window id.
2537      */
getWindowId()2538     public int getWindowId() {
2539         return mInfo.getWindowId();
2540     }
2541 
2542     /**
2543      * Gets the number of children.
2544      *
2545      * @return The child count.
2546      */
getChildCount()2547     public int getChildCount() {
2548         return mInfo.getChildCount();
2549     }
2550 
2551     /**
2552      * Get the child at given index.
2553      *
2554      * @param index The child index.
2555      * @return The child node.
2556      * @throws IllegalStateException If called outside of an
2557      *             AccessibilityService.
2558      */
getChild(int index)2559     public AccessibilityNodeInfoCompat getChild(int index) {
2560         return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getChild(index));
2561     }
2562 
2563     /**
2564      * Get the child at given index.
2565      *
2566      * @param index The child index.
2567      * @param prefetchingStrategy the prefetching strategy.
2568      * @return The child node.
2569      *
2570      * @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
2571      *                               calling {@link #setQueryFromAppProcessEnabled}.
2572      *
2573      * @see AccessibilityNodeInfoCompat#getParent(int) for a description of prefetching.
2574      */
getChild(int index, int prefetchingStrategy)2575     public @Nullable AccessibilityNodeInfoCompat getChild(int index, int prefetchingStrategy) {
2576         if (Build.VERSION.SDK_INT >= 33) {
2577             return Api33Impl.getChild(mInfo, index, prefetchingStrategy);
2578         }
2579         return getChild(index);
2580     }
2581 
2582     /**
2583      * Adds a child.
2584      * <p>
2585      * <strong>Note:</strong> Cannot be called from an
2586      * {@link android.accessibilityservice.AccessibilityService}. This class is
2587      * made immutable before being delivered to an AccessibilityService.
2588      * </p>
2589      *
2590      * @param child The child.
2591      * @throws IllegalStateException If called from an AccessibilityService.
2592      */
addChild(View child)2593     public void addChild(View child) {
2594         mInfo.addChild(child);
2595     }
2596 
2597     /**
2598      * Adds a virtual child which is a descendant of the given <code>root</code>.
2599      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
2600      * is added as a child.
2601      * <p>
2602      * A virtual descendant is an imaginary View that is reported as a part of the view
2603      * hierarchy for accessibility purposes. This enables custom views that draw complex
2604      * content to report them selves as a tree of virtual views, thus conveying their
2605      * logical structure.
2606      * </p>
2607      *
2608      * @param root The root of the virtual subtree.
2609      * @param virtualDescendantId The id of the virtual child.
2610      */
addChild(View root, int virtualDescendantId)2611     public void addChild(View root, int virtualDescendantId) {
2612         mInfo.addChild(root, virtualDescendantId);
2613     }
2614 
2615     /**
2616      * Removes a child. If the child was not previously added to the node,
2617      * calling this method has no effect.
2618      * <p>
2619      * <strong>Note:</strong> Cannot be called from an
2620      * {@link android.accessibilityservice.AccessibilityService}.
2621      * This class is made immutable before being delivered to an AccessibilityService.
2622      * </p>
2623      *
2624      * @param child The child.
2625      * @return true if the child was present
2626      *
2627      * @throws IllegalStateException If called from an AccessibilityService.
2628      */
removeChild(View child)2629     public boolean removeChild(View child) {
2630         return mInfo.removeChild(child);
2631     }
2632 
2633     /**
2634      * Removes a virtual child which is a descendant of the given
2635      * <code>root</code>. If the child was not previously added to the node,
2636      * calling this method has no effect.
2637      *
2638      * @param root The root of the virtual subtree.
2639      * @param virtualDescendantId The id of the virtual child.
2640      * @return true if the child was present
2641      * @see #addChild(View, int)
2642      */
removeChild(View root, int virtualDescendantId)2643     public boolean removeChild(View root, int virtualDescendantId) {
2644         return mInfo.removeChild(root, virtualDescendantId);
2645     }
2646 
2647     /**
2648      * Gets the actions that can be performed on the node.
2649      *
2650      * @return The bit mask of with actions.
2651      * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_FOCUS
2652      * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
2653      * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_SELECT
2654      * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
2655      *
2656      * @deprecated Use {@link #getActionList()} instead.
2657      */
2658     @Deprecated
getActions()2659     public int getActions() {
2660         return mInfo.getActions();
2661     }
2662 
2663     /**
2664      * Adds an action that can be performed on the node.
2665      * <p>
2666      * <strong>Note:</strong> Cannot be called from an
2667      * {@link android.accessibilityservice.AccessibilityService}. This class is
2668      * made immutable before being delivered to an AccessibilityService.
2669      * </p>
2670      *
2671      * @param action The action.
2672      * @throws IllegalStateException If called from an AccessibilityService.
2673      */
addAction(int action)2674     public void addAction(int action) {
2675         mInfo.addAction(action);
2676     }
2677 
extrasIntList(String key)2678     private List<Integer> extrasIntList(String key) {
2679         ArrayList<Integer> list = mInfo.getExtras()
2680                 .getIntegerArrayList(key);
2681         if (list == null) {
2682             list = new ArrayList<Integer>();
2683             mInfo.getExtras().putIntegerArrayList(key, list);
2684         }
2685         return list;
2686     }
2687 
2688     /**
2689      * Adds an action that can be performed on the node.
2690      * <p>
2691      * <strong>Note:</strong> Cannot be called from an
2692      * {@link android.accessibilityservice.AccessibilityService}. This class is
2693      * made immutable before being delivered to an AccessibilityService.
2694      * </p>
2695      *
2696      * @param action The action.
2697      * @throws IllegalStateException If called from an AccessibilityService.
2698      */
addAction(AccessibilityActionCompat action)2699     public void addAction(AccessibilityActionCompat action) {
2700         mInfo.addAction((AccessibilityNodeInfo.AccessibilityAction) action.mAction);
2701     }
2702 
2703     /**
2704      * Removes an action that can be performed on the node. If the action was
2705      * not already added to the node, calling this method has no effect.
2706      * <p>
2707      *   <strong>Note:</strong> Cannot be called from an
2708      *   {@link android.accessibilityservice.AccessibilityService}.
2709      *   This class is made immutable before being delivered to an AccessibilityService.
2710      * </p>
2711      *
2712      * @param action The action to be removed.
2713      * @return The action removed from the list of actions.
2714      *
2715      * @throws IllegalStateException If called from an AccessibilityService.
2716      */
removeAction(AccessibilityActionCompat action)2717     public boolean removeAction(AccessibilityActionCompat action) {
2718         return mInfo.removeAction((AccessibilityNodeInfo.AccessibilityAction) action.mAction);
2719     }
2720 
2721     /**
2722      * Performs an action on the node.
2723      * <p>
2724      * <strong>Note:</strong> An action can be performed only if the request is
2725      * made from an {@link android.accessibilityservice.AccessibilityService}.
2726      * </p>
2727      *
2728      * @param action The action to perform.
2729      * @return True if the action was performed.
2730      * @throws IllegalStateException If called outside of an
2731      *             AccessibilityService.
2732      */
performAction(int action)2733     public boolean performAction(int action) {
2734         return mInfo.performAction(action);
2735     }
2736 
2737     /**
2738      * Performs an action on the node.
2739      * <p>
2740      *   <strong>Note:</strong> An action can be performed only if the request is made
2741      *   from an {@link android.accessibilityservice.AccessibilityService}.
2742      * </p>
2743      *
2744      * @param action The action to perform.
2745      * @param arguments A bundle with additional arguments.
2746      * @return True if the action was performed.
2747      *
2748      * @throws IllegalStateException If called outside of an AccessibilityService.
2749      */
performAction(int action, Bundle arguments)2750     public boolean performAction(int action, Bundle arguments) {
2751         return mInfo.performAction(action, arguments);
2752     }
2753 
2754     /**
2755      * Sets the movement granularities for traversing the text of this node.
2756      * <p>
2757      *   <strong>Note:</strong> Cannot be called from an
2758      *   {@link android.accessibilityservice.AccessibilityService}.
2759      *   This class is made immutable before being delivered to an AccessibilityService.
2760      * </p>
2761      *
2762      * @param granularities The bit mask with granularities.
2763      *
2764      * @throws IllegalStateException If called from an AccessibilityService.
2765      */
setMovementGranularities(int granularities)2766     public void setMovementGranularities(int granularities) {
2767         mInfo.setMovementGranularities(granularities);
2768     }
2769 
2770     /**
2771      * Gets the movement granularities for traversing the text of this node.
2772      *
2773      * @return The bit mask with granularities.
2774      */
getMovementGranularities()2775     public int getMovementGranularities() {
2776         return mInfo.getMovementGranularities();
2777     }
2778 
2779     /**
2780      * Gets the expanded state for this node.
2781      *
2782      * @return The expanded state, one of:
2783      *     <ul>
2784      *       <li>{@link AccessibilityNodeInfo#EXPANDED_STATE_UNDEFINED}
2785      *       <li>{@link AccessibilityNodeInfo#EXPANDED_STATE_COLLAPSED}
2786      *       <li>{@link AccessibilityNodeInfo#EXPANDED_STATE_FULL}
2787      *       <li>{@link AccessibilityNodeInfo#EXPANDED_STATE_PARTIAL}
2788      *     </ul>
2789      */
2790     @ExpandedState
getExpandedState()2791     public int getExpandedState() {
2792         if (Build.VERSION.SDK_INT >= 36) {
2793             return Api36Impl.getExpandedState(mInfo);
2794         } else {
2795             return mInfo.getExtras().getInt(EXPANDED_STATE_KEY,
2796                 AccessibilityNodeInfo.EXPANDED_STATE_UNDEFINED);
2797         }
2798     }
2799 
2800     /**
2801      * Sets the expanded state of the node.
2802      *
2803      * <p><strong>Note:</strong> Cannot be called from an {@link
2804      * android.accessibilityservice.AccessibilityService}. This class is made immutable before being
2805      * delivered to an {@link android.accessibilityservice.AccessibilityService}.
2806      *
2807      * @param state new expanded state of this node.
2808      * @throws IllegalArgumentException If state is not one of:
2809      *     <ul>
2810      *       <li>{@link AccessibilityNodeInfo#EXPANDED_STATE_UNDEFINED}
2811      *       <li>{@link AccessibilityNodeInfo#EXPANDED_STATE_COLLAPSED}
2812      *       <li>{@link AccessibilityNodeInfo#EXPANDED_STATE_PARTIAL}
2813      *       <li>{@link AccessibilityNodeInfo#EXPANDED_STATE_FULL}
2814      *     </ul>
2815      *
2816      * @throws IllegalStateException If called from an AccessibilityService
2817      */
setExpandedState(@xpandedState int state)2818     public void setExpandedState(@ExpandedState int state) {
2819         if (Build.VERSION.SDK_INT >= 36) {
2820             Api36Impl.setExpandedState(mInfo, state);
2821         } else {
2822             mInfo.getExtras().putInt(EXPANDED_STATE_KEY, state);
2823         }
2824     }
2825 
2826     /**
2827      * Finds {@link android.view.accessibility.AccessibilityNodeInfo}s by text. The match
2828      * is case insensitive containment. The search is relative to this info i.e. this
2829      * info is the root of the traversed tree.
2830      *
2831      * @param text The searched text.
2832      * @return A list of node info.
2833      */
findAccessibilityNodeInfosByText(String text)2834     public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(String text) {
2835         List<AccessibilityNodeInfoCompat> result = new ArrayList<AccessibilityNodeInfoCompat>();
2836         List<AccessibilityNodeInfo> infos = mInfo.findAccessibilityNodeInfosByText(text);
2837         final int infoCount = infos.size();
2838         for (int i = 0; i < infoCount; i++) {
2839             AccessibilityNodeInfo info = infos.get(i);
2840             result.add(AccessibilityNodeInfoCompat.wrap(info));
2841         }
2842         return result;
2843     }
2844 
2845     /**
2846      * Gets the parent.
2847      *
2848      * @return The parent.
2849      */
getParent()2850     public AccessibilityNodeInfoCompat getParent() {
2851         return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getParent());
2852     }
2853 
2854     /**
2855      * Gets the parent.
2856      *
2857      * <p>
2858      * Use {@code prefetchingStrategy} to determine the types of
2859      * nodes prefetched from the app if the requested node is not in the cache and must be retrieved
2860      * by the app. The default strategy for {@link #getParent()} is a combination of ancestor and
2861      * sibling strategies. The app will prefetch until all nodes fulfilling the strategies are
2862      * fetched, another node request is sent, or the maximum prefetch batch size of
2863      * {@link #MAX_NUMBER_OF_PREFETCHED_NODES} nodes is reached. To prevent interruption by another
2864      * request and to force prefetching of the max batch size, use
2865      * {@link AccessibilityNodeInfoCompat#FLAG_PREFETCH_UNINTERRUPTIBLE}.
2866      * </p>
2867      *
2868      * @param prefetchingStrategy the prefetching strategy.
2869      * @return The parent.
2870      *
2871      * @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
2872      *                               calling {@link #setQueryFromAppProcessEnabled}.
2873      *
2874      * @see #FLAG_PREFETCH_ANCESTORS
2875      * @see #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST
2876      * @see #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST
2877      * @see #FLAG_PREFETCH_DESCENDANTS_HYBRID
2878      * @see #FLAG_PREFETCH_SIBLINGS
2879      * @see #FLAG_PREFETCH_UNINTERRUPTIBLE
2880      */
getParent(int prefetchingStrategy)2881     public @Nullable AccessibilityNodeInfoCompat getParent(int prefetchingStrategy) {
2882         if (Build.VERSION.SDK_INT >= 33) {
2883             return Api33Impl.getParent(mInfo, prefetchingStrategy);
2884         }
2885         return getParent();
2886     }
2887 
2888     /**
2889      * Sets the parent.
2890      * <p>
2891      * <strong>Note:</strong> Cannot be called from an
2892      * {@link android.accessibilityservice.AccessibilityService}. This class is
2893      * made immutable before being delivered to an AccessibilityService.
2894      * </p>
2895      *
2896      * @param parent The parent.
2897      * @throws IllegalStateException If called from an AccessibilityService.
2898      */
setParent(View parent)2899     public void setParent(View parent) {
2900         mParentVirtualDescendantId = NO_ID;
2901 
2902         mInfo.setParent(parent);
2903     }
2904 
2905     /**
2906      * Sets the parent to be a virtual descendant of the given <code>root</code>.
2907      * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
2908      * is set as the parent.
2909      * <p>
2910      * A virtual descendant is an imaginary View that is reported as a part of the view
2911      * hierarchy for accessibility purposes. This enables custom views that draw complex
2912      * content to report them selves as a tree of virtual views, thus conveying their
2913      * logical structure.
2914      * <p>
2915      * <strong>Note:</strong> Cannot be called from an
2916      * {@link android.accessibilityservice.AccessibilityService}.
2917      * This class is made immutable before being delivered to an AccessibilityService.
2918      * <p>
2919      * This method is not supported on devices running API level < 16 since the platform did
2920      * not support virtual descendants of real views.
2921      *
2922      * @param root The root of the virtual subtree.
2923      * @param virtualDescendantId The id of the virtual descendant.
2924      */
setParent(View root, int virtualDescendantId)2925     public void setParent(View root, int virtualDescendantId) {
2926         // Store the ID anyway, since we may need it for equality checks.
2927         mParentVirtualDescendantId = virtualDescendantId;
2928 
2929         mInfo.setParent(root, virtualDescendantId);
2930     }
2931 
2932     /**
2933      * Gets the node bounds in the viewParent's coordinates.
2934      * {@link #getParent()} does not represent the source's viewParent.
2935      * Instead it represents the result of {@link View#getParentForAccessibility()},
2936      * which returns the closest ancestor where {@link View#isImportantForAccessibility()} is true.
2937      * So this method is not reliable.
2938      *
2939      * @param outBounds The output node bounds.
2940      *
2941      * @deprecated Use {@link #getBoundsInScreen(Rect)} instead.
2942      */
2943     @Deprecated
getBoundsInParent(Rect outBounds)2944     public void getBoundsInParent(Rect outBounds) {
2945         mInfo.getBoundsInParent(outBounds);
2946     }
2947 
2948     /**
2949      * Sets the node bounds in the viewParent's coordinates.
2950      * {@link #getParent()} does not represent the source's viewParent.
2951      * Instead it represents the result of {@link View#getParentForAccessibility()},
2952      * which returns the closest ancestor where {@link View#isImportantForAccessibility()} is true.
2953      * So this method is not reliable.
2954      *
2955      * <p>
2956      * <strong>Note:</strong> Cannot be called from an
2957      * {@link android.accessibilityservice.AccessibilityService}. This class is
2958      * made immutable before being delivered to an AccessibilityService.
2959      * </p>
2960      *
2961      * @param bounds The node bounds.
2962      * @throws IllegalStateException If called from an AccessibilityService.
2963      *
2964      * @deprecated Accessibility services should not care about these bounds.
2965      */
2966     @Deprecated
setBoundsInParent(Rect bounds)2967     public void setBoundsInParent(Rect bounds) {
2968         mInfo.setBoundsInParent(bounds);
2969     }
2970 
2971     /**
2972      * Gets the node bounds in screen coordinates.
2973      *
2974      * @param outBounds The output node bounds.
2975      */
getBoundsInScreen(Rect outBounds)2976     public void getBoundsInScreen(Rect outBounds) {
2977         mInfo.getBoundsInScreen(outBounds);
2978     }
2979 
2980     /**
2981      * Sets the node bounds in screen coordinates.
2982      * <p>
2983      * <strong>Note:</strong> Cannot be called from an
2984      * {@link android.accessibilityservice.AccessibilityService}. This class is
2985      * made immutable before being delivered to an AccessibilityService.
2986      * </p>
2987      *
2988      * @param bounds The node bounds.
2989      * @throws IllegalStateException If called from an AccessibilityService.
2990      */
setBoundsInScreen(Rect bounds)2991     public void setBoundsInScreen(Rect bounds) {
2992         mInfo.setBoundsInScreen(bounds);
2993     }
2994 
2995     /**
2996      * Gets the node bounds in window coordinates.
2997      * <p>
2998      * When magnification is enabled, the bounds in window are scaled up by magnification scale
2999      * and the positions are also adjusted according to the offset of magnification viewport.
3000      * For example, it returns Rect(-180, -180, 0, 0) for original bounds Rect(10, 10, 100, 100),
3001      * when the magnification scale is 2 and offsets for X and Y are both 200.
3002      * <p/>
3003      * <p>
3004      * Compatibility:
3005      * <ul>
3006      *     <li>API &lt; 19: No-op</li>
3007      * </ul>
3008      * @param outBounds The output node bounds.
3009      */
getBoundsInWindow(@onNull Rect outBounds)3010     public void getBoundsInWindow(@NonNull Rect outBounds) {
3011         if (Build.VERSION.SDK_INT >= 34) {
3012             Api34Impl.getBoundsInWindow(mInfo, outBounds);
3013         } else {
3014             Rect extraBounds = mInfo.getExtras().getParcelable(BOUNDS_IN_WINDOW_KEY);
3015             if (extraBounds != null) {
3016                 outBounds.set(extraBounds.left, extraBounds.top, extraBounds.right,
3017                         extraBounds.bottom);
3018             }
3019         }
3020     }
3021 
3022     /**
3023      * Sets the node bounds in window coordinates.
3024      * <p>
3025      *   <strong>Note:</strong> Cannot be called from an
3026      *   {@link android.accessibilityservice.AccessibilityService}.
3027      *   This class is made immutable before being delivered to an AccessibilityService.
3028      * </p>
3029      * <p>
3030      * Compatibility:
3031      * <ul>
3032      *     <li>API &lt; 19: No-op</li>
3033      * </ul>
3034      * @param bounds The node bounds.
3035      *
3036      * @throws IllegalStateException If called from an AccessibilityService.
3037      */
setBoundsInWindow(@onNull Rect bounds)3038     public void setBoundsInWindow(@NonNull Rect bounds) {
3039         if (Build.VERSION.SDK_INT >= 34) {
3040             Api34Impl.setBoundsInWindow(mInfo, bounds);
3041         } else {
3042             mInfo.getExtras().putParcelable(BOUNDS_IN_WINDOW_KEY, bounds);
3043         }
3044     }
3045 
3046     /**
3047      * Gets whether this node is checkable.
3048      *
3049      * @return True if the node is checkable.
3050      */
isCheckable()3051     public boolean isCheckable() {
3052         return mInfo.isCheckable();
3053     }
3054 
3055     /**
3056      * Sets whether this node is checkable.
3057      * <p>
3058      * <strong>Note:</strong> Cannot be called from an
3059      * {@link android.accessibilityservice.AccessibilityService}. This class is
3060      * made immutable before being delivered to an AccessibilityService.
3061      * </p>
3062      *
3063      * @param checkable True if the node is checkable.
3064      * @throws IllegalStateException If called from an AccessibilityService.
3065      */
setCheckable(boolean checkable)3066     public void setCheckable(boolean checkable) {
3067         mInfo.setCheckable(checkable);
3068     }
3069 
3070     /**
3071      * Gets whether this node is checked.
3072      *
3073      * @return True if the node is checked.
3074      *
3075      * @deprecated Use {@link #getChecked()} instead.
3076      */
3077     @Deprecated
isChecked()3078     public boolean isChecked() {
3079         return mInfo.isChecked();
3080     }
3081 
3082     /**
3083      * Sets whether this node is checked.
3084      * <p>
3085      * <strong>Note:</strong> Cannot be called from an
3086      * {@link android.accessibilityservice.AccessibilityService}. This class is
3087      * made immutable before being delivered to an AccessibilityService.
3088      * </p>
3089      *
3090      * @param checked True if the node is checked.
3091      * @throws IllegalStateException If called from an AccessibilityService.
3092      *
3093      * @deprecated Use {@link #setChecked(int)} instead.
3094      */
3095     @Deprecated
setChecked(boolean checked)3096     public void setChecked(boolean checked) {
3097         mInfo.setChecked(checked);
3098     }
3099 
3100     /**
3101      * Gets the checked state of this node.
3102      * <p>
3103      * Note that this is only meaningful when {@link #isCheckable()} returns {@code true}.
3104      *
3105      * @see #setCheckable(boolean)
3106      * @see #isCheckable()
3107      * @see #setChecked(int)
3108      * @return The checked state, one of:
3109      *          <ul>
3110      *          <li>{@link AccessibilityNodeInfo#CHECKED_STATE_FALSE}
3111      *          <li>{@link AccessibilityNodeInfo#CHECKED_STATE_TRUE}
3112      *          <li>{@link AccessibilityNodeInfo#CHECKED_STATE_PARTIAL}
3113      *          </ul>
3114      */
3115     @CheckedState
getChecked()3116     public int getChecked() {
3117         if (Build.VERSION.SDK_INT >= 36) {
3118             return Api36Impl.getChecked(mInfo);
3119         } else {
3120             return mInfo.getExtras().getInt(CHECKED_KEY,
3121                     mInfo.isChecked() ? AccessibilityNodeInfo.CHECKED_STATE_TRUE
3122                             : AccessibilityNodeInfo.CHECKED_STATE_FALSE);
3123         }
3124     }
3125 
3126     /**
3127      * Sets the checked state of this node. This is only meaningful
3128      * when {@link #isCheckable()} returns {@code true}.
3129      * <p><strong>Note:</strong> Cannot be called from an
3130      *   {@link android.accessibilityservice.AccessibilityService}. This class is made immutable
3131      *   before being delivered to an AccessibilityService.
3132      *
3133      * @see #setCheckable(boolean)
3134      * @see #isCheckable()
3135      * @see #getChecked()
3136      * @param checked The checked state. One of
3137      *          <ul>
3138      *          <li>{@link AccessibilityNodeInfo#CHECKED_STATE_FALSE}
3139      *          <li>{@link AccessibilityNodeInfo#CHECKED_STATE_TRUE}
3140      *          <li>{@link AccessibilityNodeInfo#CHECKED_STATE_PARTIAL}
3141      *          </ul>
3142      * @throws IllegalStateException If called from an AccessibilityService.
3143      * @throws IllegalArgumentException if {@code checked} is not one of
3144      * {@link AccessibilityNodeInfo#CHECKED_STATE_FALSE},
3145      *          {@link AccessibilityNodeInfo#CHECKED_STATE_TRUE}, or
3146      *          {@link AccessibilityNodeInfo#CHECKED_STATE_PARTIAL}.
3147      */
setChecked(@heckedState int checked)3148     public void setChecked(@CheckedState int checked) {
3149         if (Build.VERSION.SDK_INT >= 36) {
3150             Api36Impl.setChecked(mInfo, checked);
3151             return;
3152         }
3153 
3154         if (checked == AccessibilityNodeInfo.CHECKED_STATE_TRUE
3155                 || checked == AccessibilityNodeInfo.CHECKED_STATE_PARTIAL
3156                 || checked == AccessibilityNodeInfo.CHECKED_STATE_FALSE) {
3157             mInfo.setChecked(checked == AccessibilityNodeInfo.CHECKED_STATE_TRUE);
3158             mInfo.getExtras().putInt(CHECKED_KEY, checked);
3159         } else {
3160             throw new IllegalArgumentException("Unknown checked argument: " + checked);
3161         }
3162     }
3163 
3164     /**
3165      * Gets whether a node representing a form field requires input or selection.
3166      *
3167      * @return {@code true} if {@code this} node represents a form field that requires input or
3168      *     selection, {@code false} otherwise.
3169      */
isFieldRequired()3170     public boolean isFieldRequired() {
3171         if (Build.VERSION.SDK_INT >= 36) {
3172             return Api36Impl.isFieldRequired(mInfo);
3173         } else {
3174             return mInfo.getExtras().getBoolean(IS_REQUIRED_KEY);
3175         }
3176     }
3177 
3178     /**
3179      * Sets whether {@code this} node represents a form field that requires input or selection.
3180      *
3181      * <p><strong>Note:</strong> Cannot be called from an AccessibilityService. This class is made
3182      * immutable before being delivered to an AccessibilityService.
3183      *
3184      * @param required {@code true} if input or selection of this node should be required, {@code
3185      *     false} otherwise.
3186      * @throws IllegalStateException If called from an AccessibilityService
3187      */
setFieldRequired(boolean required)3188     public void setFieldRequired(boolean required) {
3189         if (Build.VERSION.SDK_INT >= 36) {
3190             Api36Impl.setFieldRequired(mInfo, required);
3191         } else {
3192             mInfo.getExtras().putBoolean(IS_REQUIRED_KEY, required);
3193         }
3194     }
3195 
3196     /**
3197      * Gets whether this node is focusable.
3198      *
3199      * @return True if the node is focusable.
3200      */
isFocusable()3201     public boolean isFocusable() {
3202         return mInfo.isFocusable();
3203     }
3204 
3205     /**
3206      * Sets whether this node is focusable.
3207      * <p>
3208      * <strong>Note:</strong> Cannot be called from an
3209      * {@link android.accessibilityservice.AccessibilityService}. This class is
3210      * made immutable before being delivered to an AccessibilityService.
3211      * </p>
3212      *
3213      * @param focusable True if the node is focusable.
3214      * @throws IllegalStateException If called from an AccessibilityService.
3215      */
setFocusable(boolean focusable)3216     public void setFocusable(boolean focusable) {
3217         mInfo.setFocusable(focusable);
3218     }
3219 
3220     /**
3221      * Gets whether this node is focused.
3222      *
3223      * @return True if the node is focused.
3224      */
isFocused()3225     public boolean isFocused() {
3226         return mInfo.isFocused();
3227     }
3228 
3229     /**
3230      * Sets whether this node is focused.
3231      * <p>
3232      * <strong>Note:</strong> Cannot be called from an
3233      * {@link android.accessibilityservice.AccessibilityService}. This class is
3234      * made immutable before being delivered to an AccessibilityService.
3235      * </p>
3236      *
3237      * @param focused True if the node is focused.
3238      * @throws IllegalStateException If called from an AccessibilityService.
3239      */
setFocused(boolean focused)3240     public void setFocused(boolean focused) {
3241         mInfo.setFocused(focused);
3242     }
3243 
3244     /**
3245      * Gets whether this node is visible to the user.
3246      *
3247      * @return Whether the node is visible to the user.
3248      */
isVisibleToUser()3249     public boolean isVisibleToUser() {
3250         return mInfo.isVisibleToUser();
3251     }
3252 
3253     /**
3254      * Sets whether this node is visible to the user.
3255      * <p>
3256      *   <strong>Note:</strong> Cannot be called from an
3257      *   {@link android.accessibilityservice.AccessibilityService}.
3258      *   This class is made immutable before being delivered to an AccessibilityService.
3259      * </p>
3260      *
3261      * @param visibleToUser Whether the node is visible to the user.
3262      *
3263      * @throws IllegalStateException If called from an AccessibilityService.
3264      */
setVisibleToUser(boolean visibleToUser)3265     public void setVisibleToUser(boolean visibleToUser) {
3266         mInfo.setVisibleToUser(visibleToUser);
3267     }
3268 
3269     /**
3270      * Gets whether this node is accessibility focused.
3271      *
3272      * @return True if the node is accessibility focused.
3273      */
isAccessibilityFocused()3274     public boolean isAccessibilityFocused() {
3275         return mInfo.isAccessibilityFocused();
3276     }
3277 
3278     /**
3279      * Sets whether this node is accessibility focused.
3280      * <p>
3281      *   <strong>Note:</strong> Cannot be called from an
3282      *   {@link android.accessibilityservice.AccessibilityService}.
3283      *   This class is made immutable before being delivered to an AccessibilityService.
3284      * </p>
3285      *
3286      * @param focused True if the node is accessibility focused.
3287      *
3288      * @throws IllegalStateException If called from an AccessibilityService.
3289      */
setAccessibilityFocused(boolean focused)3290     public void setAccessibilityFocused(boolean focused) {
3291         mInfo.setAccessibilityFocused(focused);
3292     }
3293 
3294     /**
3295      * Gets whether this node is selected.
3296      *
3297      * @return True if the node is selected.
3298      */
isSelected()3299     public boolean isSelected() {
3300         return mInfo.isSelected();
3301     }
3302 
3303     /**
3304      * Sets whether this node is selected.
3305      * <p>
3306      * <strong>Note:</strong> Cannot be called from an
3307      * {@link android.accessibilityservice.AccessibilityService}. This class is
3308      * made immutable before being delivered to an AccessibilityService.
3309      * </p>
3310      *
3311      * @param selected True if the node is selected.
3312      * @throws IllegalStateException If called from an AccessibilityService.
3313      */
setSelected(boolean selected)3314     public void setSelected(boolean selected) {
3315         mInfo.setSelected(selected);
3316     }
3317 
3318     /**
3319      * Gets whether this node is clickable.
3320      *
3321      * @return True if the node is clickable.
3322      */
isClickable()3323     public boolean isClickable() {
3324         return mInfo.isClickable();
3325     }
3326 
3327     /**
3328      * Sets whether this node is clickable.
3329      * <p>
3330      * <strong>Note:</strong> Cannot be called from an
3331      * {@link android.accessibilityservice.AccessibilityService}. This class is
3332      * made immutable before being delivered to an AccessibilityService.
3333      * </p>
3334      *
3335      * @param clickable True if the node is clickable.
3336      * @throws IllegalStateException If called from an AccessibilityService.
3337      */
setClickable(boolean clickable)3338     public void setClickable(boolean clickable) {
3339         mInfo.setClickable(clickable);
3340     }
3341 
3342     /**
3343      * Gets whether this node is long clickable.
3344      *
3345      * @return True if the node is long clickable.
3346      */
isLongClickable()3347     public boolean isLongClickable() {
3348         return mInfo.isLongClickable();
3349     }
3350 
3351     /**
3352      * Sets whether this node is long clickable.
3353      * <p>
3354      * <strong>Note:</strong> Cannot be called from an
3355      * {@link android.accessibilityservice.AccessibilityService}. This class is
3356      * made immutable before being delivered to an AccessibilityService.
3357      * </p>
3358      *
3359      * @param longClickable True if the node is long clickable.
3360      * @throws IllegalStateException If called from an AccessibilityService.
3361      */
setLongClickable(boolean longClickable)3362     public void setLongClickable(boolean longClickable) {
3363         mInfo.setLongClickable(longClickable);
3364     }
3365 
3366     /**
3367      * Gets whether this node is enabled.
3368      *
3369      * @return True if the node is enabled.
3370      */
isEnabled()3371     public boolean isEnabled() {
3372         return mInfo.isEnabled();
3373     }
3374 
3375     /**
3376      * Sets whether this node is enabled.
3377      * <p>
3378      * <strong>Note:</strong> Cannot be called from an
3379      * {@link android.accessibilityservice.AccessibilityService}. This class is
3380      * made immutable before being delivered to an AccessibilityService.
3381      * </p>
3382      *
3383      * @param enabled True if the node is enabled.
3384      * @throws IllegalStateException If called from an AccessibilityService.
3385      */
setEnabled(boolean enabled)3386     public void setEnabled(boolean enabled) {
3387         mInfo.setEnabled(enabled);
3388     }
3389 
3390     /**
3391      * Gets whether this node is a password.
3392      *
3393      * @return True if the node is a password.
3394      */
isPassword()3395     public boolean isPassword() {
3396         return mInfo.isPassword();
3397     }
3398 
3399     /**
3400      * Sets whether this node is a password.
3401      * <p>
3402      * <strong>Note:</strong> Cannot be called from an
3403      * {@link android.accessibilityservice.AccessibilityService}. This class is
3404      * made immutable before being delivered to an AccessibilityService.
3405      * </p>
3406      *
3407      * @param password True if the node is a password.
3408      * @throws IllegalStateException If called from an AccessibilityService.
3409      */
setPassword(boolean password)3410     public void setPassword(boolean password) {
3411         mInfo.setPassword(password);
3412     }
3413 
3414     /**
3415      * Gets if the node is scrollable.
3416      *
3417      * @return True if the node is scrollable, false otherwise.
3418      */
isScrollable()3419     public boolean isScrollable() {
3420         return mInfo.isScrollable();
3421     }
3422 
3423     /**
3424      * Sets if the node is scrollable.
3425      * <p>
3426      * <strong>Note:</strong> Cannot be called from an
3427      * {@link android.accessibilityservice.AccessibilityService}. This class is
3428      * made immutable before being delivered to an AccessibilityService.
3429      * </p>
3430      *
3431      * @param scrollable True if the node is scrollable, false otherwise.
3432      * @throws IllegalStateException If called from an AccessibilityService.
3433      */
setScrollable(boolean scrollable)3434     public void setScrollable(boolean scrollable) {
3435         mInfo.setScrollable(scrollable);
3436     }
3437 
3438 
3439     /**
3440      * Gets if the node supports granular scrolling.
3441      * <p>
3442      * Compatibility:
3443      * <ul>
3444      *     <li>Api &lt; 19: Returns false.</li>
3445      * </ul>
3446      * @return True if all scroll actions that could support
3447      * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT} have done so, false otherwise.
3448      */
isGranularScrollingSupported()3449     public boolean isGranularScrollingSupported() {
3450         return getBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING);
3451     }
3452 
3453     /**
3454      * Sets if the node supports granular scrolling. This should be set to true if all scroll
3455      * actions which could support {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT} have done so.
3456      * <p>
3457      *   <strong>Note:</strong> Cannot be called from an
3458      *   {@link android.accessibilityservice.AccessibilityService}.
3459      *   This class is made immutable before being delivered to an AccessibilityService.
3460      * </p>
3461      * <p>
3462      * Compatibility:
3463      * <ul>
3464      *     <li>Api &lt; 19: No-op.</li>
3465      * </ul>
3466      * @param granularScrollingSupported True if the node supports granular scrolling, false
3467      *                                  otherwise.
3468      *
3469      * @throws IllegalStateException If called from an AccessibilityService.
3470      */
setGranularScrollingSupported(boolean granularScrollingSupported)3471     public void setGranularScrollingSupported(boolean granularScrollingSupported) {
3472         setBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING,
3473                 granularScrollingSupported);
3474     }
3475 
3476     /**
3477      * Gets if the node has selectable text.
3478      *
3479      * <p>
3480      *     Services should use {@link #ACTION_SET_SELECTION} for selection. Editable text nodes must
3481      *     also be selectable. But not all UIs will populate this field, so services should consider
3482      *     'isTextSelectable | isEditable' to ensure they don't miss nodes with selectable text.
3483      * </p>
3484      * <p>
3485      * Compatibility:
3486      * <ul>
3487      *     <li>Api &lt; 19: Returns false.</li>
3488      * </ul>
3489      *
3490      * @see #isEditable
3491      * @return True if the node has selectable text.
3492      */
isTextSelectable()3493     public boolean isTextSelectable() {
3494         if (Build.VERSION.SDK_INT >= 33) {
3495             return Api33Impl.isTextSelectable(mInfo);
3496         } else {
3497             return getBooleanProperty(BOOLEAN_PROPERTY_TEXT_SELECTABLE);
3498         }
3499     }
3500 
3501     /**
3502      * Sets if the node has selectable text.
3503      * <p>
3504      *   <strong>Note:</strong> Cannot be called from an
3505      *   {@link android.accessibilityservice.AccessibilityService}.
3506      *   This class is made immutable before being delivered to an AccessibilityService.
3507      * </p>
3508      * <p>
3509      * Compatibility:
3510      * <ul>
3511      *     <li>Api &lt; 19: Does not operate.</li>
3512      * </ul>
3513      * </p>
3514      *
3515      * @param selectableText True if the node has selectable text, false otherwise.
3516      *
3517      * @throws IllegalStateException If called from an AccessibilityService.
3518      */
setTextSelectable(boolean selectableText)3519     public void setTextSelectable(boolean selectableText) {
3520         if (Build.VERSION.SDK_INT >= 33) {
3521             Api33Impl.setTextSelectable(mInfo, selectableText);
3522         } else {
3523             setBooleanProperty(BOOLEAN_PROPERTY_TEXT_SELECTABLE, selectableText);
3524         }
3525     }
3526 
3527     /**
3528      * Gets the minimum time duration between two content change events.
3529      */
getMinDurationBetweenContentChangesMillis()3530     public long getMinDurationBetweenContentChangesMillis() {
3531         if (Build.VERSION.SDK_INT >= 34) {
3532             return Api34Impl.getMinDurationBetweenContentChangeMillis(mInfo);
3533         } else {
3534             return mInfo.getExtras().getLong(MIN_DURATION_BETWEEN_CONTENT_CHANGES_KEY);
3535         }
3536     }
3537 
3538     /**
3539      * Sets the minimum time duration between two content change events, which is used in throttling
3540      * content change events in accessibility services.
3541      *
3542      * <p>
3543      * Example: An app can set MinDurationBetweenContentChanges as 1 min for a view which sends
3544      * content change events to accessibility services one event per second.
3545      * Accessibility service will throttle those content change events and only handle one event
3546      * per minute for that view.
3547      * </p>
3548      *
3549      * @see AccessibilityEventCompat#getContentChangeTypes for all content change types.
3550      * @param duration the minimum duration between content change events.
3551      */
setMinDurationBetweenContentChangesMillis(long duration)3552     public void setMinDurationBetweenContentChangesMillis(long duration) {
3553         if (Build.VERSION.SDK_INT >= 34) {
3554             Api34Impl.setMinDurationBetweenContentChangeMillis(mInfo, duration);
3555         } else {
3556             mInfo.getExtras().putLong(MIN_DURATION_BETWEEN_CONTENT_CHANGES_KEY, duration);
3557         }
3558     }
3559 
3560     /**
3561      * Returns whether the node originates from a view considered important for accessibility.
3562      *
3563      * @return {@code true} if the node originates from a view considered important for
3564      *         accessibility, {@code false} otherwise
3565      *
3566      * @see View#isImportantForAccessibility()
3567      */
isImportantForAccessibility()3568     public boolean isImportantForAccessibility() {
3569         if (Build.VERSION.SDK_INT >= 24) {
3570             return mInfo.isImportantForAccessibility();
3571         } else {
3572             return true;
3573         }
3574     }
3575 
3576     /**
3577      * Sets whether the node is considered important for accessibility.
3578      * <p>
3579      *   <strong>Note:</strong> Cannot be called from an
3580      *   {@link android.accessibilityservice.AccessibilityService}.
3581      *   This class is made immutable before being delivered to an AccessibilityService.
3582      * </p>
3583      *
3584      * @param important {@code true} if the node is considered important for accessibility,
3585      *                  {@code false} otherwise
3586      */
setImportantForAccessibility(boolean important)3587     public void setImportantForAccessibility(boolean important) {
3588         if (Build.VERSION.SDK_INT >= 24) {
3589             mInfo.setImportantForAccessibility(important);
3590         }
3591     }
3592 
3593     /**
3594      * Gets if the node's accessibility data is considered sensitive.
3595      *
3596      * @return True if the node's data is considered sensitive, false otherwise.
3597      * @see View#isAccessibilityDataSensitive()
3598      */
isAccessibilityDataSensitive()3599     public boolean isAccessibilityDataSensitive() {
3600         if (Build.VERSION.SDK_INT >= 34) {
3601             return Api34Impl.isAccessibilityDataSensitive(mInfo);
3602         } else {
3603             return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_DATA_SENSITIVE);
3604         }
3605     }
3606 
3607     /**
3608      * Sets whether this node's accessibility data is considered sensitive.
3609      *
3610      * <p>
3611      * For SDK 34 and higher: when set to true the framework will hide this node from
3612      * accessibility services with the
3613      * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool}
3614      * property set to false.
3615      * </p>
3616      * <p>
3617      * Otherwise, for SDK 19 and higher: the framework cannot hide this node but this property may
3618      * be read by accessibility services to provide modified behavior for sensitive nodes.
3619      * </p>
3620      * <p>
3621      *   <strong>Note:</strong> Cannot be called from an
3622      *   {@link android.accessibilityservice.AccessibilityService}.
3623      *   This class is made immutable before being delivered to an AccessibilityService.
3624      * </p>
3625      *
3626      * @param accessibilityDataSensitive True if the node's accessibility data is considered
3627      *                                   sensitive.
3628      * @throws IllegalStateException If called from an AccessibilityService.
3629      */
setAccessibilityDataSensitive(boolean accessibilityDataSensitive)3630     public void setAccessibilityDataSensitive(boolean accessibilityDataSensitive) {
3631         if (Build.VERSION.SDK_INT >= 34) {
3632             Api34Impl.setAccessibilityDataSensitive(mInfo, accessibilityDataSensitive);
3633         } else {
3634             setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_DATA_SENSITIVE,
3635                     accessibilityDataSensitive);
3636         }
3637     }
3638 
3639     /**
3640      * Gets the package this node comes from.
3641      *
3642      * @return The package name.
3643      */
getPackageName()3644     public CharSequence getPackageName() {
3645         return mInfo.getPackageName();
3646     }
3647 
3648     /**
3649      * Sets the package this node comes from.
3650      * <p>
3651      * <strong>Note:</strong> Cannot be called from an
3652      * {@link android.accessibilityservice.AccessibilityService}. This class is
3653      * made immutable before being delivered to an AccessibilityService.
3654      * </p>
3655      *
3656      * @param packageName The package name.
3657      * @throws IllegalStateException If called from an AccessibilityService.
3658      */
setPackageName(CharSequence packageName)3659     public void setPackageName(CharSequence packageName) {
3660         mInfo.setPackageName(packageName);
3661     }
3662 
3663     /**
3664      * Gets the class this node comes from.
3665      *
3666      * @return The class name.
3667      */
getClassName()3668     public CharSequence getClassName() {
3669         return mInfo.getClassName();
3670     }
3671 
3672     /**
3673      * Sets the class this node comes from.
3674      * <p>
3675      * <strong>Note:</strong> Cannot be called from an
3676      * {@link android.accessibilityservice.AccessibilityService}. This class is
3677      * made immutable before being delivered to an AccessibilityService.
3678      * </p>
3679      *
3680      * @param className The class name.
3681      * @throws IllegalStateException If called from an AccessibilityService.
3682      */
setClassName(CharSequence className)3683     public void setClassName(CharSequence className) {
3684         mInfo.setClassName(className);
3685     }
3686 
3687     /**
3688      * Gets the text of this node.
3689      *
3690      * @return The text.
3691      */
getText()3692     public CharSequence getText() {
3693         if (hasSpans()) {
3694             List<Integer> starts = extrasIntList(SPANS_START_KEY);
3695             List<Integer> ends = extrasIntList(SPANS_END_KEY);
3696             List<Integer> flags = extrasIntList(SPANS_FLAGS_KEY);
3697             List<Integer> ids = extrasIntList(SPANS_ID_KEY);
3698             Spannable spannable = new SpannableString(TextUtils.substring(mInfo.getText(),
3699                     0, mInfo.getText().length()));
3700             for (int i = 0; i < starts.size(); i++) {
3701                 spannable.setSpan(new AccessibilityClickableSpanCompat(ids.get(i), this,
3702                                 getExtras().getInt(SPANS_ACTION_ID_KEY)),
3703                         starts.get(i), ends.get(i), flags.get(i));
3704             }
3705             return spannable;
3706         } else {
3707             return mInfo.getText();
3708         }
3709     }
3710 
3711     /**
3712      * Sets the text of this node.
3713      * <p>
3714      * <strong>Note:</strong> Cannot be called from an
3715      * {@link android.accessibilityservice.AccessibilityService}. This class is
3716      * made immutable before being delivered to an AccessibilityService.
3717      * </p>
3718      *
3719      * @param text The text.
3720      * @throws IllegalStateException If called from an AccessibilityService.
3721      */
setText(CharSequence text)3722     public void setText(CharSequence text) {
3723         mInfo.setText(text);
3724     }
3725 
3726     /**
3727      */
3728     @RestrictTo(LIBRARY_GROUP_PREFIX)
addSpansToExtras(CharSequence text, View view)3729     public void addSpansToExtras(CharSequence text, View view) {
3730         if (Build.VERSION.SDK_INT < 26) {
3731             clearExtrasSpans();
3732             removeCollectedSpans(view);
3733             ClickableSpan[] spans = getClickableSpans(text);
3734             if (spans != null && spans.length > 0) {
3735                 getExtras().putInt(SPANS_ACTION_ID_KEY, R.id.accessibility_action_clickable_span);
3736                 SparseArray<WeakReference<ClickableSpan>> tagSpans =
3737                         getOrCreateSpansFromViewTags(view);
3738                 for (int i = 0; spans != null && i < spans.length; i++) {
3739                     int id = idForClickableSpan(spans[i], tagSpans);
3740                     tagSpans.put(id, new WeakReference<>(spans[i]));
3741                     addSpanLocationToExtras(spans[i], (Spanned) text, id);
3742                 }
3743             }
3744         }
3745     }
3746 
getOrCreateSpansFromViewTags(View host)3747     private SparseArray<WeakReference<ClickableSpan>> getOrCreateSpansFromViewTags(View host) {
3748         SparseArray<WeakReference<ClickableSpan>> spans = getSpansFromViewTags(host);
3749         if (spans == null) {
3750             spans = new SparseArray<>();
3751             host.setTag(R.id.tag_accessibility_clickable_spans, spans);
3752         }
3753         return spans;
3754     }
3755 
3756     @SuppressWarnings("unchecked")
getSpansFromViewTags(View host)3757     private SparseArray<WeakReference<ClickableSpan>> getSpansFromViewTags(View host) {
3758         return (SparseArray<WeakReference<ClickableSpan>>) host.getTag(
3759                 R.id.tag_accessibility_clickable_spans);
3760     }
3761 
3762     /**
3763      */
3764     @RestrictTo(LIBRARY_GROUP_PREFIX)
getClickableSpans(CharSequence text)3765     public static ClickableSpan[] getClickableSpans(CharSequence text) {
3766         if (text instanceof Spanned) {
3767             Spanned spanned = (Spanned) text;
3768             return spanned.getSpans(0, text.length(), ClickableSpan.class);
3769         }
3770         return null;
3771     }
3772 
idForClickableSpan(ClickableSpan span, SparseArray<WeakReference<ClickableSpan>> spans)3773     private int idForClickableSpan(ClickableSpan span,
3774             SparseArray<WeakReference<ClickableSpan>> spans) {
3775         if (spans != null) {
3776             for (int i = 0; i < spans.size(); i++) {
3777                 ClickableSpan aSpan = spans.valueAt(i).get();
3778                 if (span.equals(aSpan)) {
3779                     return spans.keyAt(i);
3780                 }
3781             }
3782         }
3783         return sClickableSpanId++;
3784     }
3785 
hasSpans()3786     private boolean hasSpans() {
3787         return !extrasIntList(SPANS_START_KEY).isEmpty();
3788     }
3789 
clearExtrasSpans()3790     private void clearExtrasSpans() {
3791         mInfo.getExtras().remove(SPANS_START_KEY);
3792         mInfo.getExtras().remove(SPANS_END_KEY);
3793         mInfo.getExtras().remove(SPANS_FLAGS_KEY);
3794         mInfo.getExtras().remove(SPANS_ID_KEY);
3795     }
3796 
addSpanLocationToExtras(ClickableSpan span, Spanned spanned, int id)3797     private void addSpanLocationToExtras(ClickableSpan span, Spanned spanned, int id) {
3798         extrasIntList(SPANS_START_KEY).add(spanned.getSpanStart(span));
3799         extrasIntList(SPANS_END_KEY).add(spanned.getSpanEnd(span));
3800         extrasIntList(SPANS_FLAGS_KEY).add(spanned.getSpanFlags(span));
3801         extrasIntList(SPANS_ID_KEY).add(id);
3802     }
3803 
removeCollectedSpans(View view)3804     private void removeCollectedSpans(View view) {
3805         SparseArray<WeakReference<ClickableSpan>> spans = getSpansFromViewTags(view);
3806         if (spans != null) {
3807             List<Integer> toBeRemovedIndices = new ArrayList<>();
3808             for (int i = 0; i < spans.size(); i++) {
3809                 if (spans.valueAt(i).get() == null) {
3810                     toBeRemovedIndices.add(i);
3811                 }
3812             }
3813             for (int i = 0; i < toBeRemovedIndices.size(); i++) {
3814                 spans.remove(toBeRemovedIndices.get(i));
3815             }
3816         }
3817     }
3818 
3819     /**
3820      * Gets the content description of this node.
3821      *
3822      * @return The content description.
3823      */
getContentDescription()3824     public CharSequence getContentDescription() {
3825         return mInfo.getContentDescription();
3826     }
3827 
3828     /**
3829      * Gets the state description of this node.
3830      *
3831      * @return the state description or null if android version smaller
3832      * than 19.
3833      */
getStateDescription()3834     public @Nullable CharSequence getStateDescription() {
3835         if (Build.VERSION.SDK_INT >= 30) {
3836             return Api30Impl.getStateDescription(mInfo);
3837         } else {
3838             return mInfo.getExtras().getCharSequence(STATE_DESCRIPTION_KEY);
3839         }
3840     }
3841 
3842     /**
3843      * Sets the content description of this node.
3844      * <p>
3845      * <strong>Note:</strong> Cannot be called from an
3846      * {@link android.accessibilityservice.AccessibilityService}. This class is
3847      * made immutable before being delivered to an AccessibilityService.
3848      * </p>
3849      *
3850      * @param contentDescription The content description.
3851      * @throws IllegalStateException If called from an AccessibilityService.
3852      */
setContentDescription(CharSequence contentDescription)3853     public void setContentDescription(CharSequence contentDescription) {
3854         mInfo.setContentDescription(contentDescription);
3855     }
3856 
3857     /**
3858      * Sets the state description of this node.
3859      * <p>
3860      *   <strong>Note:</strong> Cannot be called from an
3861      *   {@link android.accessibilityservice.AccessibilityService}.
3862      *   This class is made immutable before being delivered to an AccessibilityService.
3863      * </p>
3864      *
3865      * @param stateDescription the state description of this node.
3866      * @throws IllegalStateException If called from an AccessibilityService.
3867      */
setStateDescription(@ullable CharSequence stateDescription)3868     public void setStateDescription(@Nullable CharSequence stateDescription) {
3869         if (Build.VERSION.SDK_INT >= 30) {
3870             Api30Impl.setStateDescription(mInfo, stateDescription);
3871         } else {
3872             mInfo.getExtras().putCharSequence(STATE_DESCRIPTION_KEY, stateDescription);
3873         }
3874     }
3875 
3876     /**
3877      * Gets the unique id of this node.
3878      *
3879      * @return the unique id or null if android version smaller
3880      * than 19.
3881      */
getUniqueId()3882     public @Nullable String getUniqueId() {
3883         if (Build.VERSION.SDK_INT >= 33) {
3884             return Api33Impl.getUniqueId(mInfo);
3885         } else {
3886             return mInfo.getExtras().getString(UNIQUE_ID_KEY);
3887         }
3888     }
3889 
3890     /**
3891      * Sets the unique id of this node.
3892      * <p>
3893      *   <strong>Note:</strong> Cannot be called from an
3894      *   {@link android.accessibilityservice.AccessibilityService}.
3895      *   This class is made immutable before being delivered to an AccessibilityService.
3896      * </p>
3897      *
3898      * @param uniqueId the unique id of this node.
3899      * @throws IllegalStateException If called from an AccessibilityService.
3900      */
setUniqueId(@ullable String uniqueId)3901     public void setUniqueId(@Nullable String uniqueId) {
3902         if (Build.VERSION.SDK_INT >= 33) {
3903             Api33Impl.setUniqueId(mInfo, uniqueId);
3904         } else {
3905             mInfo.getExtras().putString(UNIQUE_ID_KEY, uniqueId);
3906         }
3907     }
3908 
3909     /**
3910      * Sets the container title for app-developer-defined container which can be any type of
3911      * ViewGroup or layout.
3912      * Container title will be used to group together related controls, similar to HTML fieldset.
3913      * Or container title may identify a large piece of the UI that is visibly grouped together,
3914      * such as a toolbar or a card, etc.
3915      * <p>
3916      * Container title helps to assist in navigation across containers and other groups.
3917      * For example, a screen reader may use this to determine where to put accessibility focus.
3918      * </p>
3919      * <p>
3920      * Container title is different from pane title{@link #setPaneTitle} which indicates that the
3921      * node represents a window or activity.
3922      * </p>
3923      *
3924      * <p>
3925      *  Example: An app can set container titles on several non-modal menus, containing TextViews
3926      *  or ImageButtons that have content descriptions, text, etc. Screen readers can quickly
3927      *  switch accessibility focus among menus instead of child views.  Other accessibility-services
3928      *  can easily find the menu.
3929      * </p>
3930      * <p>
3931      * Compatibility:
3932      * <ul>
3933      *     <li>API &lt; 19: No-op</li>
3934      * </ul>
3935      * @param containerTitle The container title that is associated with a ViewGroup/Layout on the
3936      *                       screen.
3937      */
setContainerTitle(@ullable CharSequence containerTitle)3938     public void setContainerTitle(@Nullable CharSequence containerTitle) {
3939         if (Build.VERSION.SDK_INT >= 34) {
3940             Api34Impl.setContainerTitle(mInfo, containerTitle);
3941         } else {
3942             mInfo.getExtras().putCharSequence(CONTAINER_TITLE_KEY, containerTitle);
3943         }
3944     }
3945 
3946     /**
3947      * Returns the container title.
3948      * <p>
3949      * Compatibility:
3950      * <ul>
3951      *     <li>API &lt; 19: Returns null</li>
3952      * </ul>
3953      * @see #setContainerTitle for details.
3954      */
getContainerTitle()3955     public @Nullable CharSequence getContainerTitle() {
3956         if (Build.VERSION.SDK_INT >= 34) {
3957             return Api34Impl.getContainerTitle(mInfo);
3958         } else {
3959             return mInfo.getExtras().getCharSequence(CONTAINER_TITLE_KEY);
3960         }
3961     }
3962 
3963     /**
3964      * Return an instance back to be reused.
3965      * <p>
3966      * <strong>Note:</strong> You must not touch the object after calling this function.
3967      *
3968      * @throws IllegalStateException If the info is already recycled.
3969      * @deprecated Accessibility Object recycling is no longer necessary or functional.
3970      */
3971     @Deprecated
recycle()3972     public void recycle() { }
3973 
3974     /**
3975      * Sets the fully qualified resource name of the source view's id.
3976      *
3977      * <p>
3978      *   <strong>Note:</strong> Cannot be called from an
3979      *   {@link android.accessibilityservice.AccessibilityService}.
3980      *   This class is made immutable before being delivered to an AccessibilityService.
3981      * </p>
3982      *
3983      * @param viewId The id resource name.
3984      */
setViewIdResourceName(String viewId)3985     public void setViewIdResourceName(String viewId) {
3986         mInfo.setViewIdResourceName(viewId);
3987     }
3988 
3989     /**
3990      * Gets the fully qualified resource name of the source view's id.
3991      *
3992      * <p>
3993      *   <strong>Note:</strong> The primary usage of this API is for UI test automation
3994      *   and in order to report the source view id of an {@link AccessibilityNodeInfoCompat}
3995      *   the client has to set the {@link AccessibilityServiceInfoCompat#FLAG_REPORT_VIEW_IDS}
3996      *   flag when configuring their {@link android.accessibilityservice.AccessibilityService}.
3997      * </p>
3998      *
3999      * @return The id resource name.
4000      */
getViewIdResourceName()4001     public String getViewIdResourceName() {
4002         return mInfo.getViewIdResourceName();
4003     }
4004 
4005     /**
4006      * Gets the node's live region mode.
4007      * <p>
4008      * A live region is a node that contains information that is important for
4009      * the user and when it changes the user should be notified. For example,
4010      * in a login screen with a TextView that displays an "incorrect password"
4011      * notification, that view should be marked as a live region with mode
4012      * {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_POLITE}.
4013      * <p>
4014      * It is the responsibility of the accessibility service to monitor
4015      * {@link AccessibilityEventCompat#TYPE_WINDOW_CONTENT_CHANGED} events
4016      * indicating changes to live region nodes and their children.
4017      *
4018      * @return The live region mode, or
4019      *         {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_NONE} if the view is
4020      *         not a live region.
4021      * @see ViewCompat#getAccessibilityLiveRegion(View)
4022      */
getLiveRegion()4023     public int getLiveRegion() {
4024         return mInfo.getLiveRegion();
4025     }
4026 
4027     /**
4028      * Sets the node's live region mode.
4029      * <p>
4030      * <strong>Note:</strong> Cannot be called from an
4031      * {@link android.accessibilityservice.AccessibilityService}. This class is
4032      * made immutable before being delivered to an AccessibilityService.
4033      *
4034      * @param mode The live region mode, or
4035      *        {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_NONE} if the view is
4036      *        not a live region.
4037      * @see ViewCompat#setAccessibilityLiveRegion(View, int)
4038      */
setLiveRegion(int mode)4039     public void setLiveRegion(int mode) {
4040         mInfo.setLiveRegion(mode);
4041     }
4042 
4043     /**
4044      * Get the drawing order of the view corresponding it this node.
4045      * <p>
4046      * Drawing order is determined only within the node's parent, so this index is only relative
4047      * to its siblings.
4048      * <p>
4049      * In some cases, the drawing order is essentially simultaneous, so it is possible for two
4050      * siblings to return the same value. It is also possible that values will be skipped.
4051      *
4052      * @return The drawing position of the view corresponding to this node relative to its siblings.
4053      */
getDrawingOrder()4054     public int getDrawingOrder() {
4055         if (Build.VERSION.SDK_INT >= 24) {
4056             return mInfo.getDrawingOrder();
4057         } else {
4058             return 0;
4059         }
4060     }
4061 
4062     /**
4063      * Set the drawing order of the view corresponding it this node.
4064      *
4065      * <p>
4066      *   <strong>Note:</strong> Cannot be called from an
4067      *   {@link android.accessibilityservice.AccessibilityService}.
4068      *   This class is made immutable before being delivered to an AccessibilityService.
4069      * </p>
4070      * @param drawingOrderInParent
4071      * @throws IllegalStateException If called from an AccessibilityService.
4072      */
setDrawingOrder(int drawingOrderInParent)4073     public void setDrawingOrder(int drawingOrderInParent) {
4074         if (Build.VERSION.SDK_INT >= 24) {
4075             mInfo.setDrawingOrder(drawingOrderInParent);
4076         }
4077     }
4078 
4079     /**
4080      * Gets the collection info if the node is a collection. A collection
4081      * child is always a collection item.
4082      *
4083      * @return The collection info.
4084      */
getCollectionInfo()4085     public CollectionInfoCompat getCollectionInfo() {
4086         AccessibilityNodeInfo.CollectionInfo info = mInfo.getCollectionInfo();
4087         if (info != null) {
4088             return new CollectionInfoCompat(info);
4089         }
4090         return null;
4091     }
4092 
setCollectionInfo(Object collectionInfo)4093     public void setCollectionInfo(Object collectionInfo) {
4094         mInfo.setCollectionInfo((collectionInfo == null) ? null
4095                 : (AccessibilityNodeInfo.CollectionInfo) ((CollectionInfoCompat)
4096                         collectionInfo).mInfo);
4097 
4098     }
4099 
setCollectionItemInfo(Object collectionItemInfo)4100     public void setCollectionItemInfo(Object collectionItemInfo) {
4101         mInfo.setCollectionItemInfo((collectionItemInfo == null) ? null
4102                 : (AccessibilityNodeInfo.CollectionItemInfo) ((CollectionItemInfoCompat)
4103                         collectionItemInfo).mInfo);
4104     }
4105 
4106     /**
4107      * Gets the collection item info if the node is a collection item. A collection
4108      * item is always a child of a collection.
4109      *
4110      * @return The collection item info.
4111      */
getCollectionItemInfo()4112     public CollectionItemInfoCompat getCollectionItemInfo() {
4113         AccessibilityNodeInfo.CollectionItemInfo info = mInfo.getCollectionItemInfo();
4114         if (info != null) {
4115             return new CollectionItemInfoCompat(info);
4116         }
4117         return null;
4118     }
4119 
4120     /**
4121      * Gets the range info if this node is a range.
4122      *
4123      * @return The range.
4124      */
getRangeInfo()4125     public RangeInfoCompat getRangeInfo() {
4126         AccessibilityNodeInfo.RangeInfo info = mInfo.getRangeInfo();
4127         if (info != null) {
4128             return new RangeInfoCompat(info);
4129         }
4130         return null;
4131     }
4132 
4133     /**
4134      * Sets the range info if this node is a range.
4135      * <p>
4136      *   <strong>Note:</strong> Cannot be called from an
4137      *   {@link android.accessibilityservice.AccessibilityService}.
4138      *   This class is made immutable before being delivered to an AccessibilityService.
4139      * </p>
4140      *
4141      * @param rangeInfo The range info.
4142      */
setRangeInfo(RangeInfoCompat rangeInfo)4143     public void setRangeInfo(RangeInfoCompat rangeInfo) {
4144         mInfo.setRangeInfo((AccessibilityNodeInfo.RangeInfo) rangeInfo.mInfo);
4145     }
4146 
4147     /**
4148      * Gets the {@link android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo
4149      * extra rendering info} if the node is meant to be refreshed with extra data
4150      * to examine rendering related accessibility issues.
4151      *
4152      * @return The {@link android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo
4153      * extra rendering info}.
4154      */
getExtraRenderingInfo()4155     public AccessibilityNodeInfo.@Nullable ExtraRenderingInfo getExtraRenderingInfo() {
4156         if (Build.VERSION.SDK_INT >= 33) {
4157             return Api33Impl.getExtraRenderingInfo(mInfo);
4158         } else {
4159             return null;
4160         }
4161     }
4162 
4163     /**
4164      * Gets the actions that can be performed on the node.
4165      *
4166      * @return A list of AccessibilityActions.
4167      */
4168     @SuppressWarnings({"unchecked", "MixedMutabilityReturnType"})
getActionList()4169     public List<AccessibilityActionCompat> getActionList() {
4170         List<Object> actions = (List<Object>) (List<?>) mInfo.getActionList();
4171         List<AccessibilityActionCompat> result = new ArrayList<>();
4172         final int actionCount = actions.size();
4173         for (int i = 0; i < actionCount; i++) {
4174             Object action = actions.get(i);
4175             result.add(new AccessibilityActionCompat(action));
4176         }
4177         return result;
4178     }
4179 
4180     /**
4181      * Sets if the content of this node is invalid. For example,
4182      * a date is not well-formed.
4183      * <p>
4184      *   <strong>Note:</strong> Cannot be called from an
4185      *   {@link android.accessibilityservice.AccessibilityService}.
4186      *   This class is made immutable before being delivered to an AccessibilityService.
4187      * </p>
4188      *
4189      * @param contentInvalid If the node content is invalid.
4190      */
setContentInvalid(boolean contentInvalid)4191     public void setContentInvalid(boolean contentInvalid) {
4192         mInfo.setContentInvalid(contentInvalid);
4193     }
4194 
4195     /**
4196      * Gets if the content of this node is invalid. For example,
4197      * a date is not well-formed.
4198      *
4199      * @return If the node content is invalid.
4200      */
isContentInvalid()4201     public boolean isContentInvalid() {
4202         return mInfo.isContentInvalid();
4203     }
4204 
4205     /**
4206      * Gets whether this node is context clickable.
4207      *
4208      * @return True if the node is context clickable.
4209      */
isContextClickable()4210     public boolean isContextClickable() {
4211         if (Build.VERSION.SDK_INT >= 23) {
4212             return mInfo.isContextClickable();
4213         } else {
4214             return false;
4215         }
4216     }
4217 
4218     /**
4219      * Sets whether this node is context clickable.
4220      * <p>
4221      * <strong>Note:</strong> Cannot be called from an
4222      * {@link android.accessibilityservice.AccessibilityService}. This class is made immutable
4223      * before being delivered to an AccessibilityService.
4224      * </p>
4225      *
4226      * @param contextClickable True if the node is context clickable.
4227      * @throws IllegalStateException If called from an AccessibilityService.
4228      */
setContextClickable(boolean contextClickable)4229     public void setContextClickable(boolean contextClickable) {
4230         if (Build.VERSION.SDK_INT >= 23) {
4231             mInfo.setContextClickable(contextClickable);
4232         }
4233     }
4234 
4235     /**
4236      * Gets the hint text of this node. Only applies to nodes where text can be entered.
4237      *
4238      * @return The hint text.
4239      */
getHintText()4240     public @Nullable CharSequence getHintText() {
4241         if (Build.VERSION.SDK_INT >= 26) {
4242             return mInfo.getHintText();
4243         } else {
4244             return mInfo.getExtras().getCharSequence(HINT_TEXT_KEY);
4245         }
4246     }
4247 
4248     /**
4249      * Sets the hint text of this node. Only applies to nodes where text can be entered.
4250      * <p>This method has no effect below API 19</p>
4251      * <p>
4252      *   <strong>Note:</strong> Cannot be called from an
4253      *   {@link android.accessibilityservice.AccessibilityService}.
4254      *   This class is made immutable before being delivered to an AccessibilityService.
4255      * </p>
4256      *
4257      * @param hintText The hint text for this mode.
4258      *
4259      * @throws IllegalStateException If called from an AccessibilityService.
4260      */
setHintText(@ullable CharSequence hintText)4261     public void setHintText(@Nullable CharSequence hintText) {
4262         if (Build.VERSION.SDK_INT >= 26) {
4263             mInfo.setHintText(hintText);
4264         } else {
4265             mInfo.getExtras().putCharSequence(HINT_TEXT_KEY, hintText);
4266         }
4267     }
4268 
4269 
4270     /**
4271      * Sets the error text of this node.
4272      * <p>
4273      *   <strong>Note:</strong> Cannot be called from an
4274      *   {@link android.accessibilityservice.AccessibilityService}.
4275      *   This class is made immutable before being delivered to an AccessibilityService.
4276      * </p>
4277      *
4278      * @param error The error text.
4279      *
4280      * @throws IllegalStateException If called from an AccessibilityService.
4281      */
setError(CharSequence error)4282     public void setError(CharSequence error) {
4283         mInfo.setError(error);
4284     }
4285 
4286     /**
4287      * Gets the error text of this node.
4288      *
4289      * @return The error text.
4290      */
getError()4291     public CharSequence getError() {
4292         return mInfo.getError();
4293     }
4294 
4295     /**
4296      * Sets the view for which the view represented by this info serves as a
4297      * label for accessibility purposes.
4298      *
4299      * @param labeled The view for which this info serves as a label.
4300      */
setLabelFor(View labeled)4301     public void setLabelFor(View labeled) {
4302         mInfo.setLabelFor(labeled);
4303     }
4304 
4305     /**
4306      * Sets the view for which the view represented by this info serves as a
4307      * label for accessibility purposes. If <code>virtualDescendantId</code>
4308      * is {@link View#NO_ID} the root is set as the labeled.
4309      * <p>
4310      * A virtual descendant is an imaginary View that is reported as a part of the view
4311      * hierarchy for accessibility purposes. This enables custom views that draw complex
4312      * content to report themselves as a tree of virtual views, thus conveying their
4313      * logical structure.
4314      * </p>
4315      *
4316      * @param root The root whose virtual descendant serves as a label.
4317      * @param virtualDescendantId The id of the virtual descendant.
4318      */
setLabelFor(View root, int virtualDescendantId)4319     public void setLabelFor(View root, int virtualDescendantId) {
4320         mInfo.setLabelFor(root, virtualDescendantId);
4321     }
4322 
4323     /**
4324      * Gets the node info for which the view represented by this info serves as
4325      * a label for accessibility purposes.
4326      *
4327      * @return The labeled info.
4328      */
getLabelFor()4329     public AccessibilityNodeInfoCompat getLabelFor() {
4330         return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getLabelFor());
4331     }
4332 
4333     /**
4334      * Sets the view which serves as the label of the view represented by
4335      * this info for accessibility purposes.
4336      *
4337      * @param label The view that labels this node's source.
4338      */
setLabeledBy(View label)4339     public void setLabeledBy(View label) {
4340         mInfo.setLabeledBy(label);
4341     }
4342 
4343     /**
4344      * Sets the view which serves as the label of the view represented by
4345      * this info for accessibility purposes. If <code>virtualDescendantId</code>
4346      * is {@link View#NO_ID} the root is set as the label.
4347      * <p>
4348      * A virtual descendant is an imaginary View that is reported as a part of the view
4349      * hierarchy for accessibility purposes. This enables custom views that draw complex
4350      * content to report themselves as a tree of virtual views, thus conveying their
4351      * logical structure.
4352      * </p>
4353      * <p>
4354      *   <strong>Note:</strong> Cannot be called from an
4355      *   {@link android.accessibilityservice.AccessibilityService}.
4356      *   This class is made immutable before being delivered to an AccessibilityService.
4357      * </p>
4358      *
4359      * @param root The root whose virtual descendant labels this node's source.
4360      * @param virtualDescendantId The id of the virtual descendant.
4361      */
setLabeledBy(View root, int virtualDescendantId)4362     public void setLabeledBy(View root, int virtualDescendantId) {
4363         mInfo.setLabeledBy(root, virtualDescendantId);
4364     }
4365 
4366     /**
4367      * Gets the node info which serves as the label of the view represented by
4368      * this info for accessibility purposes.
4369      *
4370      * @return The label.
4371      */
getLabeledBy()4372     public AccessibilityNodeInfoCompat getLabeledBy() {
4373         return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getLabeledBy());
4374     }
4375 
4376     /**
4377      * Gets if this node opens a popup or a dialog.
4378      *
4379      * @return If the the node opens a popup.
4380      */
canOpenPopup()4381     public boolean canOpenPopup() {
4382         return mInfo.canOpenPopup();
4383     }
4384 
4385     /**
4386      * Sets if this node opens a popup or a dialog.
4387      * <p>
4388      *   <strong>Note:</strong> Cannot be called from an
4389      *   {@link android.accessibilityservice.AccessibilityService}.
4390      *   This class is made immutable before being delivered to an AccessibilityService.
4391      * </p>
4392      *
4393      * @param opensPopup If the the node opens a popup.
4394      */
setCanOpenPopup(boolean opensPopup)4395     public void setCanOpenPopup(boolean opensPopup) {
4396         mInfo.setCanOpenPopup(opensPopup);
4397     }
4398 
4399     /**
4400      * Finds {@link AccessibilityNodeInfoCompat}s by the fully qualified view id's resource
4401      * name where a fully qualified id is of the from "package:id/id_resource_name".
4402      * For example, if the target application's package is "foo.bar" and the id
4403      * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz".
4404      *
4405      * <p>
4406      *   <strong>Note:</strong> The primary usage of this API is for UI test automation
4407      *   and in order to report the fully qualified view id if an
4408      *   {@link AccessibilityNodeInfoCompat} the client has to set the
4409      *   {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
4410      *   flag when configuring their {@link android.accessibilityservice.AccessibilityService}.
4411      * </p>
4412      *
4413      * @param viewId The fully qualified resource name of the view id to find.
4414      * @return A list of node info.
4415      */
4416     @SuppressWarnings("MixedMutabilityReturnType")
findAccessibilityNodeInfosByViewId(String viewId)4417     public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(String viewId) {
4418         List<AccessibilityNodeInfo> nodes = mInfo.findAccessibilityNodeInfosByViewId(viewId);
4419         List<AccessibilityNodeInfoCompat> result = new ArrayList<>();
4420         for (AccessibilityNodeInfo node : nodes) {
4421                 result.add(AccessibilityNodeInfoCompat.wrap(node));
4422             }
4423         return result;
4424     }
4425 
4426     /**
4427      * Gets an optional bundle with extra data. The bundle
4428      * is lazily created and never <code>null</code>.
4429      * <p>
4430      * <strong>Note:</strong> It is recommended to use the package
4431      * name of your application as a prefix for the keys to avoid
4432      * collisions which may confuse an accessibility service if the
4433      * same key has different meaning when emitted from different
4434      * applications.
4435      * </p>
4436      *
4437      * @return The bundle.
4438      */
getExtras()4439     public Bundle getExtras() {
4440         return mInfo.getExtras();
4441     }
4442 
4443     /**
4444      * Gets the input type of the source as defined by {@link InputType}.
4445      *
4446      * @return The input type.
4447      */
getInputType()4448     public int getInputType() {
4449         return mInfo.getInputType();
4450     }
4451 
4452     /**
4453      * Sets the input type of the source as defined by {@link InputType}.
4454      * <p>
4455      *   <strong>Note:</strong> Cannot be called from an
4456      *   {@link android.accessibilityservice.AccessibilityService}.
4457      *   This class is made immutable before being delivered to an
4458      *   AccessibilityService.
4459      * </p>
4460      *
4461      * @param inputType The input type.
4462      *
4463      * @throws IllegalStateException If called from an AccessibilityService.
4464      */
setInputType(int inputType)4465     public void setInputType(int inputType) {
4466         mInfo.setInputType(inputType);
4467     }
4468 
4469     /**
4470      * Get the extra data available for this node.
4471      * <p>
4472      * Some data that is useful for some accessibility services is expensive to compute, and would
4473      * place undue overhead on apps to compute all the time. That data can be requested with
4474      * {@link android.view.accessibility.AccessibilityNodeInfo#refreshWithExtraData(String, Bundle)}.
4475      *
4476      * @return An unmodifiable list of keys corresponding to extra data that can be requested.
4477      * @see #EXTRA_DATA_RENDERING_INFO_KEY
4478      * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
4479      */
getAvailableExtraData()4480     public @NonNull List<String> getAvailableExtraData() {
4481         if (Build.VERSION.SDK_INT >= 26) {
4482             return mInfo.getAvailableExtraData();
4483         } else {
4484             return emptyList();
4485         }
4486     }
4487 
4488     /**
4489      * Set the extra data available for this node.
4490      * <p>
4491      * <strong>Note:</strong> When a {@code View} passes in a non-empty list, it promises that
4492      * it will populate the node's extras with corresponding pieces of information in
4493      * {@link View#addExtraDataToAccessibilityNodeInfo(AccessibilityNodeInfo, String, Bundle)}.
4494      * <p>
4495      * <strong>Note:</strong> Cannot be called from an
4496      * {@link android.accessibilityservice.AccessibilityService}.
4497      * This class is made immutable before being delivered to an AccessibilityService.
4498      *
4499      * @param extraDataKeys A list of types of extra data that are available.
4500      * @see #getAvailableExtraData()
4501      *
4502      * @throws IllegalStateException If called from an AccessibilityService.
4503      */
setAvailableExtraData(@onNull List<String> extraDataKeys)4504     public void setAvailableExtraData(@NonNull List<String> extraDataKeys) {
4505         if (Build.VERSION.SDK_INT >= 26) {
4506             mInfo.setAvailableExtraData(extraDataKeys);
4507         }
4508     }
4509 
4510     /**
4511      * Sets the maximum text length, or -1 for no limit.
4512      * <p>
4513      * Typically used to indicate that an editable text field has a limit on
4514      * the number of characters entered.
4515      * <p>
4516      * <strong>Note:</strong> Cannot be called from an
4517      * {@link android.accessibilityservice.AccessibilityService}.
4518      * This class is made immutable before being delivered to an AccessibilityService.
4519      *
4520      * @param max The maximum text length.
4521      * @see #getMaxTextLength()
4522      *
4523      * @throws IllegalStateException If called from an AccessibilityService.
4524      */
setMaxTextLength(int max)4525     public void setMaxTextLength(int max) {
4526         mInfo.setMaxTextLength(max);
4527     }
4528 
4529     /**
4530      * Returns the maximum text length for this node.
4531      *
4532      * @return The maximum text length, or -1 for no limit.
4533      * @see #setMaxTextLength(int)
4534      */
getMaxTextLength()4535     public int getMaxTextLength() {
4536         return mInfo.getMaxTextLength();
4537     }
4538 
4539     /**
4540      * Sets the text selection start and end.
4541      * <p>
4542      *   <strong>Note:</strong> Cannot be called from an
4543      *   {@link android.accessibilityservice.AccessibilityService}.
4544      *   This class is made immutable before being delivered to an AccessibilityService.
4545      * </p>
4546      *
4547      * @param start The text selection start.
4548      * @param end The text selection end.
4549      *
4550      * @throws IllegalStateException If called from an AccessibilityService.
4551      */
setTextSelection(int start, int end)4552     public void setTextSelection(int start, int end) {
4553         mInfo.setTextSelection(start, end);
4554     }
4555 
4556     /**
4557      * Gets the text selection start.
4558      *
4559      * @return The text selection start if there is selection or -1.
4560      */
getTextSelectionStart()4561     public int getTextSelectionStart() {
4562         return mInfo.getTextSelectionStart();
4563     }
4564 
4565     /**
4566      * Gets the text selection end.
4567      *
4568      * @return The text selection end if there is selection or -1.
4569      */
getTextSelectionEnd()4570     public int getTextSelectionEnd() {
4571         return mInfo.getTextSelectionEnd();
4572     }
4573 
4574     /**
4575      * Gets the node before which this one is visited during traversal. A screen-reader
4576      * must visit the content of this node before the content of the one it precedes.
4577      *
4578      * @return The succeeding node if such or <code>null</code>.
4579      *
4580      * @see #setTraversalBefore(android.view.View)
4581      * @see #setTraversalBefore(android.view.View, int)
4582      */
getTraversalBefore()4583     public AccessibilityNodeInfoCompat getTraversalBefore() {
4584         if (Build.VERSION.SDK_INT >= 22) {
4585             return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getTraversalBefore());
4586         } else {
4587             return null;
4588         }
4589     }
4590 
4591     /**
4592      * Sets the view before whose node this one should be visited during traversal. A
4593      * screen-reader must visit the content of this node before the content of the one
4594      * it precedes.
4595      * <p>
4596      *   <strong>Note:</strong> Cannot be called from an
4597      *   {@link android.accessibilityservice.AccessibilityService}.
4598      *   This class is made immutable before being delivered to an AccessibilityService.
4599      * </p>
4600      *
4601      * @param view The view providing the preceding node.
4602      *
4603      * @see #getTraversalBefore()
4604      */
setTraversalBefore(View view)4605     public void setTraversalBefore(View view) {
4606         if (Build.VERSION.SDK_INT >= 22) {
4607             mInfo.setTraversalBefore(view);
4608         }
4609     }
4610 
4611     /**
4612      * Sets the node before which this one is visited during traversal. A screen-reader
4613      * must visit the content of this node before the content of the one it precedes.
4614      * The successor is a virtual descendant of the given <code>root</code>. If
4615      * <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root is set
4616      * as the successor.
4617      * <p>
4618      * A virtual descendant is an imaginary View that is reported as a part of the view
4619      * hierarchy for accessibility purposes. This enables custom views that draw complex
4620      * content to report them selves as a tree of virtual views, thus conveying their
4621      * logical structure.
4622      * </p>
4623      * <p>
4624      *   <strong>Note:</strong> Cannot be called from an
4625      *   {@link android.accessibilityservice.AccessibilityService}.
4626      *   This class is made immutable before being delivered to an AccessibilityService.
4627      * </p>
4628      *
4629      * @param root The root of the virtual subtree.
4630      * @param virtualDescendantId The id of the virtual descendant.
4631      */
setTraversalBefore(View root, int virtualDescendantId)4632     public void setTraversalBefore(View root, int virtualDescendantId) {
4633         if (Build.VERSION.SDK_INT >= 22) {
4634             mInfo.setTraversalBefore(root, virtualDescendantId);
4635         }
4636     }
4637 
4638     /**
4639      * Gets the node after which this one is visited in accessibility traversal.
4640      * A screen-reader must visit the content of the other node before the content
4641      * of this one.
4642      *
4643      * @return The succeeding node if such or <code>null</code>.
4644      *
4645      * @see #setTraversalAfter(android.view.View)
4646      * @see #setTraversalAfter(android.view.View, int)
4647      */
getTraversalAfter()4648     public AccessibilityNodeInfoCompat getTraversalAfter() {
4649         if (Build.VERSION.SDK_INT >= 22) {
4650             return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getTraversalAfter());
4651         } else {
4652             return null;
4653         }
4654     }
4655 
4656     /**
4657      * Sets the view whose node is visited after this one in accessibility traversal.
4658      * A screen-reader must visit the content of the other node before the content
4659      * of this one.
4660      * <p>
4661      *   <strong>Note:</strong> Cannot be called from an
4662      *   {@link android.accessibilityservice.AccessibilityService}.
4663      *   This class is made immutable before being delivered to an AccessibilityService.
4664      * </p>
4665      *
4666      * @param view The previous view.
4667      *
4668      * @see #getTraversalAfter()
4669      */
setTraversalAfter(View view)4670     public void setTraversalAfter(View view) {
4671         if (Build.VERSION.SDK_INT >= 22) {
4672             mInfo.setTraversalAfter(view);
4673         }
4674     }
4675 
4676     /**
4677      * Sets the node after which this one is visited in accessibility traversal.
4678      * A screen-reader must visit the content of the other node before the content
4679      * of this one. If <code>virtualDescendantId</code> equals to {@link View#NO_ID}
4680      * the root is set as the predecessor.
4681      * <p>
4682      * A virtual descendant is an imaginary View that is reported as a part of the view
4683      * hierarchy for accessibility purposes. This enables custom views that draw complex
4684      * content to report them selves as a tree of virtual views, thus conveying their
4685      * logical structure.
4686      * </p>
4687      * <p>
4688      *   <strong>Note:</strong> Cannot be called from an
4689      *   {@link android.accessibilityservice.AccessibilityService}.
4690      *   This class is made immutable before being delivered to an AccessibilityService.
4691      * </p>
4692      *
4693      * @param root The root of the virtual subtree.
4694      * @param virtualDescendantId The id of the virtual descendant.
4695      */
setTraversalAfter(View root, int virtualDescendantId)4696     public void setTraversalAfter(View root, int virtualDescendantId) {
4697         if (Build.VERSION.SDK_INT >= 22) {
4698             mInfo.setTraversalAfter(root, virtualDescendantId);
4699         }
4700     }
4701 
4702     /**
4703      * Gets the window to which this node belongs.
4704      *
4705      * @return The window.
4706      *
4707      * @see android.accessibilityservice.AccessibilityService#getWindows()
4708      */
getWindow()4709     public AccessibilityWindowInfoCompat getWindow() {
4710         return AccessibilityWindowInfoCompat.wrapNonNullInstance(mInfo.getWindow());
4711     }
4712 
4713     /**
4714      * Gets if the node can be dismissed.
4715      *
4716      * @return If the node can be dismissed.
4717      */
isDismissable()4718     public boolean isDismissable() {
4719         return mInfo.isDismissable();
4720     }
4721 
4722     /**
4723      * Sets if the node can be dismissed.
4724      * <p>
4725      *   <strong>Note:</strong> Cannot be called from an
4726      *   {@link android.accessibilityservice.AccessibilityService}.
4727      *   This class is made immutable before being delivered to an AccessibilityService.
4728      * </p>
4729      *
4730      * @param dismissable If the node can be dismissed.
4731      */
setDismissable(boolean dismissable)4732     public void setDismissable(boolean dismissable) {
4733         mInfo.setDismissable(dismissable);
4734     }
4735 
4736     /**
4737      * Gets if the node is editable.
4738      *
4739      * @return True if the node is editable, false otherwise.
4740      */
isEditable()4741     public boolean isEditable() {
4742         return mInfo.isEditable();
4743     }
4744 
4745     /**
4746      * Sets whether this node is editable.
4747      * <p>
4748      *   <strong>Note:</strong> Cannot be called from an
4749      *   {@link android.accessibilityservice.AccessibilityService}.
4750      *   This class is made immutable before being delivered to an AccessibilityService.
4751      * </p>
4752      *
4753      * @param editable True if the node is editable.
4754      *
4755      * @throws IllegalStateException If called from an AccessibilityService.
4756      */
setEditable(boolean editable)4757     public void setEditable(boolean editable) {
4758         mInfo.setEditable(editable);
4759     }
4760 
4761     /**
4762      * Gets if the node is a multi line editable text.
4763      *
4764      * @return True if the node is multi line.
4765      */
isMultiLine()4766     public boolean isMultiLine() {
4767         return mInfo.isMultiLine();
4768     }
4769 
4770     /**
4771      * Sets if the node is a multi line editable text.
4772      * <p>
4773      *   <strong>Note:</strong> Cannot be called from an
4774      *   {@link android.accessibilityservice.AccessibilityService}.
4775      *   This class is made immutable before being delivered to an AccessibilityService.
4776      * </p>
4777      *
4778      * @param multiLine True if the node is multi line.
4779      */
setMultiLine(boolean multiLine)4780     public void setMultiLine(boolean multiLine) {
4781         mInfo.setMultiLine(multiLine);
4782     }
4783 
4784     /**
4785      * Gets the tooltip text of this node.
4786      *
4787      * @return The tooltip text.
4788      */
getTooltipText()4789     public @Nullable CharSequence getTooltipText() {
4790         if (Build.VERSION.SDK_INT >= 28) {
4791             return mInfo.getTooltipText();
4792         } else {
4793             return mInfo.getExtras().getCharSequence(TOOLTIP_TEXT_KEY);
4794         }
4795     }
4796 
4797     /**
4798      * Sets the tooltip text of this node.
4799      * <p>This method has no effect below API 19</p>
4800      * <p>
4801      *   <strong>Note:</strong> Cannot be called from an
4802      *   {@link android.accessibilityservice.AccessibilityService}.
4803      *   This class is made immutable before being delivered to an AccessibilityService.
4804      * </p>
4805      *
4806      * @param tooltipText The tooltip text.
4807      *
4808      * @throws IllegalStateException If called from an AccessibilityService.
4809      */
setTooltipText(@ullable CharSequence tooltipText)4810     public void setTooltipText(@Nullable CharSequence tooltipText) {
4811         if (Build.VERSION.SDK_INT >= 28) {
4812             mInfo.setTooltipText(tooltipText);
4813         } else {
4814             mInfo.getExtras().putCharSequence(TOOLTIP_TEXT_KEY, tooltipText);
4815         }
4816     }
4817 
4818     /**
4819      * If this node represents a visually distinct region of the screen that may update separately
4820      * from the rest of the window, it is considered a pane. Set the pane title to indicate that
4821      * the node is a pane, and to provide a title for it.
4822      * <p>This method has no effect below API 19</p>
4823      * <p>
4824      *   <strong>Note:</strong> Cannot be called from an
4825      *   {@link android.accessibilityservice.AccessibilityService}.
4826      *   This class is made immutable before being delivered to an AccessibilityService.
4827      * </p>
4828      * @param paneTitle The title of the window represented by this node.
4829      */
setPaneTitle(@ullable CharSequence paneTitle)4830     public void setPaneTitle(@Nullable CharSequence paneTitle) {
4831         if (Build.VERSION.SDK_INT >= 28) {
4832             mInfo.setPaneTitle(paneTitle);
4833         } else {
4834             mInfo.getExtras().putCharSequence(PANE_TITLE_KEY, paneTitle);
4835         }
4836     }
4837 
4838     /**
4839      * Get the title of the pane represented by this node.
4840      *
4841      * @return The title of the pane represented by this node, or {@code null} if this node does
4842      *         not represent a pane.
4843      */
getPaneTitle()4844     public @Nullable CharSequence getPaneTitle() {
4845         if (Build.VERSION.SDK_INT >= 28) {
4846             return mInfo.getPaneTitle();
4847         } else {
4848             return mInfo.getExtras().getCharSequence(PANE_TITLE_KEY);
4849         }
4850     }
4851 
4852     /**
4853      * Returns whether the node is explicitly marked as a focusable unit by a screen reader. Note
4854      * that {@code false} indicates that it is not explicitly marked, not that the node is not
4855      * a focusable unit. Screen readers should generally use other signals, such as
4856      * {@link #isFocusable()}, or the presence of text in a node, to determine what should receive
4857      * focus.
4858      *
4859      * @return {@code true} if the node is specifically marked as a focusable unit for screen
4860      *         readers, {@code false} otherwise.
4861      */
isScreenReaderFocusable()4862     public boolean isScreenReaderFocusable() {
4863         if (Build.VERSION.SDK_INT >= 28) {
4864             return mInfo.isScreenReaderFocusable();
4865         }
4866         return getBooleanProperty(BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE);
4867     }
4868 
4869     /**
4870      * Sets whether the node should be considered a focusable unit by a screen reader.
4871      * <p>This method has no effect below API 19</p>
4872      * <p>
4873      *   <strong>Note:</strong> Cannot be called from an
4874      *   {@link android.accessibilityservice.AccessibilityService}.
4875      *   This class is made immutable before being delivered to an AccessibilityService.
4876      * </p>
4877      *
4878      * @param screenReaderFocusable {@code true} if the node is a focusable unit for screen readers,
4879      *                              {@code false} otherwise.
4880      */
setScreenReaderFocusable(boolean screenReaderFocusable)4881     public void setScreenReaderFocusable(boolean screenReaderFocusable) {
4882         if (Build.VERSION.SDK_INT >= 28) {
4883             mInfo.setScreenReaderFocusable(screenReaderFocusable);
4884         } else {
4885             setBooleanProperty(BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE, screenReaderFocusable);
4886         }
4887     }
4888 
4889     /**
4890      * Returns whether the node's text represents a hint for the user to enter text. It should only
4891      * be {@code true} if the node has editable text.
4892      *
4893      * @return {@code true} if the text in the node represents a hint to the user, {@code false}
4894      * otherwise.
4895      */
isShowingHintText()4896     public boolean isShowingHintText() {
4897         if (Build.VERSION.SDK_INT >= 26) {
4898             return mInfo.isShowingHintText();
4899         }
4900         return getBooleanProperty(BOOLEAN_PROPERTY_IS_SHOWING_HINT);
4901     }
4902 
4903     /**
4904      * Sets whether the node's text represents a hint for the user to enter text. It should only
4905      * be {@code true} if the node has editable text.
4906      * <p>This method has no effect below API 19</p>
4907      * <p>
4908      *   <strong>Note:</strong> Cannot be called from an
4909      *   {@link android.accessibilityservice.AccessibilityService}.
4910      *   This class is made immutable before being delivered to an AccessibilityService.
4911      * </p>
4912      *
4913      * @param showingHintText {@code true} if the text in the node represents a hint to the user,
4914      * {@code false} otherwise.
4915      */
setShowingHintText(boolean showingHintText)4916     public void setShowingHintText(boolean showingHintText) {
4917         if (Build.VERSION.SDK_INT >= 26) {
4918             mInfo.setShowingHintText(showingHintText);
4919         } else {
4920             setBooleanProperty(BOOLEAN_PROPERTY_IS_SHOWING_HINT, showingHintText);
4921         }
4922     }
4923 
4924     /**
4925      * Returns whether node represents a heading.
4926      * <p><strong>Note:</strong> Returns {@code true} if either {@link #setHeading(boolean)}
4927      * marks this node as a heading or if the node has a {@link CollectionItemInfoCompat} that marks
4928      * it as such, to accommodate apps that use the now-deprecated API.</p>
4929      *
4930      * @return {@code true} if the node is a heading, {@code false} otherwise.
4931      */
4932     @SuppressWarnings("deprecation")
isHeading()4933     public boolean isHeading() {
4934         if (Build.VERSION.SDK_INT >= 28) {
4935             return mInfo.isHeading();
4936         }
4937         if (getBooleanProperty(BOOLEAN_PROPERTY_IS_HEADING)) return true;
4938         CollectionItemInfoCompat collectionItemInfo = getCollectionItemInfo();
4939         return (collectionItemInfo != null) && collectionItemInfo.isHeading();
4940     }
4941 
4942     /**
4943      * Sets whether the node represents a heading.
4944      * <p>This method has no effect below API 19</p>
4945      * <p>
4946      *   <strong>Note:</strong> Cannot be called from an
4947      *   {@link android.accessibilityservice.AccessibilityService}.
4948      *   This class is made immutable before being delivered to an AccessibilityService.
4949      * </p>
4950      *
4951      * @param isHeading {@code true} if the node is a heading, {@code false} otherwise.
4952      */
setHeading(boolean isHeading)4953     public void setHeading(boolean isHeading) {
4954         if (Build.VERSION.SDK_INT >= 28) {
4955             mInfo.setHeading(isHeading);
4956         } else {
4957             setBooleanProperty(BOOLEAN_PROPERTY_IS_HEADING, isHeading);
4958         }
4959     }
4960 
4961     /**
4962      * Returns whether node represents a text entry key that is part of a keyboard or keypad.
4963      *
4964      * @return {@code true} if the node is a text entry key, {@code false} otherwise.
4965      */
isTextEntryKey()4966     public boolean isTextEntryKey() {
4967         if (Build.VERSION.SDK_INT >= 29) {
4968             return mInfo.isTextEntryKey();
4969         }
4970         return getBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY);
4971     }
4972 
4973     /**
4974      * Sets whether the node represents a text entry key that is part of a keyboard or keypad.
4975      * <p>This method has no effect below API 19</p>
4976      * <p>
4977      *   <strong>Note:</strong> Cannot be called from an
4978      *   {@link android.accessibilityservice.AccessibilityService}.
4979      *   This class is made immutable before being delivered to an AccessibilityService.
4980      * </p>
4981      *
4982      * @param isTextEntryKey {@code true} if the node is a text entry key, {@code false} otherwise.
4983      */
setTextEntryKey(boolean isTextEntryKey)4984     public void setTextEntryKey(boolean isTextEntryKey) {
4985         if (Build.VERSION.SDK_INT >= 29) {
4986             mInfo.setTextEntryKey(isTextEntryKey);
4987         } else {
4988             setBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY, isTextEntryKey);
4989         }
4990     }
4991 
4992     /**
4993      * Gets whether the node has {@link #setRequestInitialAccessibilityFocus}.
4994      *
4995      * @return True if the node has requested initial accessibility focus.
4996      */
4997     @SuppressLint("KotlinPropertyAccess")
hasRequestInitialAccessibilityFocus()4998     public boolean hasRequestInitialAccessibilityFocus() {
4999         if (Build.VERSION.SDK_INT >= 34) {
5000             return Api34Impl.hasRequestInitialAccessibilityFocus(mInfo);
5001         } else {
5002             return getBooleanProperty(BOOLEAN_PROPERTY_HAS_REQUEST_INITIAL_ACCESSIBILITY_FOCUS);
5003         }
5004     }
5005 
5006     /**
5007      * Sets whether the node has requested initial accessibility focus.
5008      *
5009      * <p>
5010      * If the node {@link #hasRequestInitialAccessibilityFocus}, this node would be one of
5011      * candidates to be accessibility focused when the window appears.
5012      * </p>
5013      *
5014      * <p>
5015      *   <strong>Note:</strong> Cannot be called from an
5016      *   {@link android.accessibilityservice.AccessibilityService}.
5017      *   This class is made immutable before being delivered to an AccessibilityService.
5018      * </p>
5019      *
5020      * @param requestInitialAccessibilityFocus True if the node requests to receive initial
5021      *                                         accessibility focus.
5022      * @throws IllegalStateException If called from an AccessibilityService.
5023      */
5024     @SuppressLint("GetterSetterNames")
setRequestInitialAccessibilityFocus(boolean requestInitialAccessibilityFocus)5025     public void setRequestInitialAccessibilityFocus(boolean requestInitialAccessibilityFocus) {
5026         if (Build.VERSION.SDK_INT >= 34) {
5027             Api34Impl.setRequestInitialAccessibilityFocus(mInfo, requestInitialAccessibilityFocus);
5028         } else {
5029             setBooleanProperty(BOOLEAN_PROPERTY_HAS_REQUEST_INITIAL_ACCESSIBILITY_FOCUS,
5030                     requestInitialAccessibilityFocus);
5031         }
5032     }
5033 
5034     /**
5035      * Refreshes this info with the latest state of the view it represents.
5036      * <p>
5037      * <strong>Note:</strong> If this method returns false this info is obsolete
5038      * since it represents a view that is no longer in the view tree.
5039      * </p>
5040      * @return Whether the refresh succeeded.
5041      */
refresh()5042     public boolean refresh() {
5043         return mInfo.refresh();
5044     }
5045 
5046     /**
5047      * Gets the custom role description.
5048      * @return The role description.
5049      */
getRoleDescription()5050     public @Nullable CharSequence getRoleDescription() {
5051         return mInfo.getExtras().getCharSequence(ROLE_DESCRIPTION_KEY);
5052     }
5053 
5054     /**
5055      * Sets the custom role description.
5056      *
5057      * <p>
5058      *   The role description allows you to customize the name for the view's semantic
5059      *   role. For example, if you create a custom subclass of {@link android.view.View}
5060      *   to display a menu bar, you could assign it the role description of "menu bar".
5061      * </p>
5062      * <p>
5063      *   <strong>Warning:</strong> For consistency with other applications, you should
5064      *   not use the role description to force accessibility services to describe
5065      *   standard views (such as buttons or checkboxes) using specific wording. For
5066      *   example, you should not set a role description of "check box" or "tick box" for
5067      *   a standard {@link android.widget.CheckBox}. Instead let accessibility services
5068      *   decide what feedback to provide.
5069      * </p>
5070      * <p>
5071      *   <strong>Note:</strong> Cannot be called from an
5072      *   {@link android.accessibilityservice.AccessibilityService}.
5073      *   This class is made immutable before being delivered to an AccessibilityService.
5074      * </p>
5075      *
5076      * @param roleDescription The role description.
5077      */
setRoleDescription(@ullable CharSequence roleDescription)5078     public void setRoleDescription(@Nullable CharSequence roleDescription) {
5079         mInfo.getExtras().putCharSequence(ROLE_DESCRIPTION_KEY, roleDescription);
5080     }
5081 
5082     /**
5083      * Get the {@link TouchDelegateInfoCompat} for touch delegate behavior with the represented
5084      * view. It is possible for the same node to be pointed to by several regions. Use
5085      * {@link TouchDelegateInfoCompat#getRegionAt(int)} to get touch delegate target
5086      * {@link Region}, and {@link TouchDelegateInfoCompat#getTargetForRegion(Region)}
5087      * for {@link AccessibilityNodeInfoCompat} from the given region.
5088      * <p>
5089      * Compatibility:
5090      * <ul>
5091      *     <li>API &lt; 29: Always returns {@code null}</li>
5092      * </ul>
5093      *
5094      * @return {@link TouchDelegateInfoCompat} or {@code null} if there are no touch delegates
5095      * in this node.
5096      */
getTouchDelegateInfo()5097     public @Nullable TouchDelegateInfoCompat getTouchDelegateInfo() {
5098         if (Build.VERSION.SDK_INT >= 29) {
5099             TouchDelegateInfo delegateInfo = mInfo.getTouchDelegateInfo();
5100             if (delegateInfo != null) {
5101                 return new TouchDelegateInfoCompat(delegateInfo);
5102             }
5103         }
5104         return null;
5105     }
5106 
5107     /**
5108      * Set touch delegate info if the represented view has a {@link android.view.TouchDelegate}.
5109      * <p>
5110      *   <strong>Note:</strong> Cannot be called from an
5111      *   {@link android.accessibilityservice.AccessibilityService}.
5112      *   This class is made immutable before being delivered to an
5113      *   AccessibilityService.
5114      * </p>
5115      * <p>
5116      * Compatibility:
5117      * <ul>
5118      *     <li>API &lt; 29: No-op</li>
5119      * </ul>
5120      *
5121      * @param delegatedInfo {@link TouchDelegateInfoCompat}
5122      * @throws IllegalStateException If called from an AccessibilityService.
5123      */
setTouchDelegateInfo(@onNull TouchDelegateInfoCompat delegatedInfo)5124     public void setTouchDelegateInfo(@NonNull TouchDelegateInfoCompat delegatedInfo) {
5125         if (Build.VERSION.SDK_INT >= 29) {
5126             mInfo.setTouchDelegateInfo(delegatedInfo.mInfo);
5127         }
5128     }
5129 
5130     /**
5131      * Connects this node to the View's root so that operations on this node can query the entire
5132      * {@link AccessibilityNodeInfoCompat} tree and perform accessibility actions on nodes.
5133      *
5134      * <p>
5135      * Testing or debugging tools should create this {@link AccessibilityNodeInfoCompat} node using
5136      * {@link ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)}
5137      * or {@link AccessibilityNodeProviderCompat} and call this
5138      * method, then navigate and interact with the node tree by calling methods on the node.
5139      * Calling this method more than once on the same node is a no-op. After calling this method,
5140      * all nodes linked to this node (children, ancestors, etc.) are also queryable.
5141      * </p>
5142      *
5143      * <p>
5144      * Here "query" refers to the following node operations:
5145      * <ul>
5146      *      <li>check properties of this node (example: {@link #isScrollable()})</li>
5147      *      <li>find and query children (example: {@link #getChild(int)})</li>
5148      *      <li>find and query the parent (example: {@link #getParent()})</li>
5149      *      <li>find focus (examples: {@link #findFocus(int)}, {@link #focusSearch(int)})</li>
5150      *      <li>find and query other nodes (example:
5151      *      {@link #findAccessibilityNodeInfosByText(String)},
5152      *      {@link #findAccessibilityNodeInfosByViewId(String)})</li>
5153      *      <li>perform actions (example: {@link #performAction(int)})</li>
5154      * </ul>
5155      * </p>
5156      *
5157      * <p>
5158      * This is intended for short-lived inspections from testing or debugging tools in the app
5159      * process, as operations on this node tree will only succeed as long as the associated
5160      * view hierarchy remains attached to a window. {@link AccessibilityNodeInfoCompat} objects can
5161      * quickly become out of sync with their corresponding {@link View} objects; if you wish to
5162      * inspect a changed or different view hierarchy then create a new node from any view in that
5163      * hierarchy and call this method on that new node, instead of disabling & re-enabling the
5164      * connection on the previous node.
5165      * </p>
5166      * <p>
5167      * Compatibility:
5168      * <ul>
5169      *     <li>API &lt; 34: No-op</li>
5170      * </ul>
5171      *
5172      * @param view The view that generated this node, or any view in the same view-root hierarchy.
5173      * @param enabled Whether to enable (true) or disable (false) querying from the app process.
5174      * @throws IllegalStateException If called from an {@link AccessibilityService}, or if provided
5175      *                               a {@link View} that is not attached to a window.
5176      */
setQueryFromAppProcessEnabled(@onNull View view, boolean enabled)5177     public void setQueryFromAppProcessEnabled(@NonNull View view, boolean enabled) {
5178         if (Build.VERSION.SDK_INT >= 34) {
5179             Api34Impl.setQueryFromAppProcessEnabled(mInfo, view, enabled);
5180         }
5181     }
5182 
5183     /**
5184      * Returns the supplemental description of this {@link AccessibilityNodeInfoCompat}.
5185      * <p>
5186      * A supplemental description provides brief supplemental information for this node, such as
5187      * the purpose of the node when that purpose is not conveyed within its textual representation.
5188      * For example, if a dropdown select has a purpose of setting font family, the supplemental
5189      * description could be "font family". If this node has children, its supplemental description
5190      * serves as additional information and is not intended to replace any existing information in
5191      * the subtree. This is different from the {@link #getContentDescription()} in that this
5192      * description is purely supplemental while a content description may be used to replace a
5193      * description for a node or its subtree that an assistive technology would otherwise compute
5194      * based on other properties of the node and its descendants.
5195      *
5196      * @return The supplemental description.
5197      * @see #setSupplementalDescription(CharSequence)
5198      * @see #getContentDescription()
5199      */
5200     @Nullable
getSupplementalDescription()5201     public CharSequence getSupplementalDescription() {
5202         if (Build.VERSION.SDK_INT >= 36) {
5203             return Api36Impl.getSupplementalDescription(mInfo);
5204         } else {
5205             return mInfo.getExtras().getCharSequence(SUPPLEMENTAL_DESCRIPTION_KEY);
5206         }
5207     }
5208 
5209     /**
5210      * Sets the supplemental description of this {@link AccessibilityNodeInfoCompat}.
5211      * <p>
5212      * A supplemental description provides brief supplemental information for this node, such as
5213      * the purpose of the node when that purpose is not conveyed within its textual representation.
5214      * For example, if a dropdown select has a purpose of setting font family, the supplemental
5215      * description could be "font family". If this node has children, its supplemental description
5216      * serves as additional information and is not intended to replace any existing information in
5217      * the subtree. This is different from the {@link #setContentDescription(CharSequence)} in that
5218      * this description is purely supplemental while a content description may be used to replace a
5219      * description for a node or its subtree that an assistive technology would otherwise compute
5220      * based on other properties of the node and its descendants.
5221      *
5222      * <p>
5223      * <strong>Note:</strong> Cannot be called from an {@link
5224      * android.accessibilityservice.AccessibilityService}. This class is made immutable before being
5225      * delivered to an AccessibilityService.
5226      *
5227      * @param supplementalDescription The supplemental description.
5228      * @throws IllegalStateException If called from an AccessibilityService.
5229      * @see #getSupplementalDescription()
5230      * @see #setContentDescription(CharSequence)
5231      */
setSupplementalDescription(@ullable CharSequence supplementalDescription)5232     public void setSupplementalDescription(@Nullable CharSequence supplementalDescription) {
5233         if (Build.VERSION.SDK_INT >= 36) {
5234             Api36Impl.setSupplementalDescription(mInfo, supplementalDescription);
5235         } else {
5236             mInfo.getExtras()
5237                     .putCharSequence(SUPPLEMENTAL_DESCRIPTION_KEY, supplementalDescription);
5238         }
5239     }
5240 
5241     @Override
hashCode()5242     public int hashCode() {
5243         return (mInfo == null) ? 0 : mInfo.hashCode();
5244     }
5245 
5246     @Override
equals(Object obj)5247     public boolean equals(Object obj) {
5248         if (this == obj) {
5249             return true;
5250         }
5251         if (obj == null) {
5252             return false;
5253         }
5254         if (!(obj instanceof AccessibilityNodeInfoCompat)) {
5255             return false;
5256         }
5257         AccessibilityNodeInfoCompat other = (AccessibilityNodeInfoCompat) obj;
5258         if (mInfo == null) {
5259             if (other.mInfo != null) {
5260                 return false;
5261             }
5262         } else if (!mInfo.equals(other.mInfo)) {
5263             return false;
5264         }
5265         if (mVirtualDescendantId != other.mVirtualDescendantId) {
5266             return false;
5267         }
5268         if (mParentVirtualDescendantId != other.mParentVirtualDescendantId) {
5269             return false;
5270         }
5271         return true;
5272     }
5273 
5274     @SuppressWarnings("deprecation")
5275     @Override
toString()5276     public @NonNull String toString() {
5277         StringBuilder builder = new StringBuilder();
5278         builder.append(super.toString());
5279 
5280         Rect bounds = new Rect();
5281 
5282         getBoundsInParent(bounds);
5283         builder.append("; boundsInParent: " + bounds);
5284 
5285         getBoundsInScreen(bounds);
5286         builder.append("; boundsInScreen: " + bounds);
5287 
5288         getBoundsInWindow(bounds);
5289         builder.append("; boundsInWindow: " + bounds);
5290 
5291         builder.append("; packageName: ").append(getPackageName());
5292         builder.append("; className: ").append(getClassName());
5293         builder.append("; text: ").append(getText());
5294         builder.append("; error: ").append(getError());
5295         builder.append("; maxTextLength: ").append(getMaxTextLength());
5296         builder.append("; stateDescription: ").append(getStateDescription());
5297         builder.append("; contentDescription: ").append(getContentDescription());
5298         builder.append("; supplementalDescription: ").append(getSupplementalDescription());
5299         builder.append("; tooltipText: ").append(getTooltipText());
5300         builder.append("; viewIdResName: ").append(getViewIdResourceName());
5301         builder.append("; uniqueId: ").append(getUniqueId());
5302 
5303         builder.append("; checkable: ").append(isCheckable());
5304         builder.append("; checked: ").append(getCheckedString());
5305         builder.append("; fieldRequired: ").append(isFieldRequired());
5306         builder.append("; focusable: ").append(isFocusable());
5307         builder.append("; focused: ").append(isFocused());
5308         builder.append("; selected: ").append(isSelected());
5309         builder.append("; clickable: ").append(isClickable());
5310         builder.append("; longClickable: ").append(isLongClickable());
5311         builder.append("; contextClickable: ").append(isContextClickable());
5312         builder.append("; expandedState: ").append(
5313                 getExpandedStateSymbolicName(getExpandedState()));
5314         builder.append("; enabled: ").append(isEnabled());
5315         builder.append("; password: ").append(isPassword());
5316         builder.append("; scrollable: " + isScrollable());
5317         builder.append("; containerTitle: ").append(getContainerTitle());
5318         builder.append("; granularScrollingSupported: ").append(isGranularScrollingSupported());
5319         builder.append("; importantForAccessibility: ").append(isImportantForAccessibility());
5320         builder.append("; visible: ").append(isVisibleToUser());
5321         builder.append("; isTextSelectable: ").append(isTextSelectable());
5322         builder.append("; accessibilityDataSensitive: ").append(isAccessibilityDataSensitive());
5323 
5324         builder.append("; [");
5325         List<AccessibilityActionCompat> actions = getActionList();
5326         for (int i = 0; i < actions.size(); i++) {
5327             AccessibilityActionCompat action = actions.get(i);
5328             String actionName = getActionSymbolicName(action.getId());
5329             if (actionName.equals("ACTION_UNKNOWN") && action.getLabel() != null) {
5330                 actionName = action.getLabel().toString();
5331             }
5332             builder.append(actionName);
5333             if (i != actions.size() - 1) {
5334                 builder.append(", ");
5335             }
5336         }
5337         builder.append("]");
5338 
5339         return builder.toString();
5340     }
5341 
setBooleanProperty(int property, boolean value)5342     private void setBooleanProperty(int property, boolean value) {
5343         Bundle extras = getExtras();
5344         if (extras != null) {
5345             int booleanProperties = extras.getInt(BOOLEAN_PROPERTY_KEY, 0);
5346             booleanProperties &= ~property;
5347             booleanProperties |= value ? property : 0;
5348             extras.putInt(BOOLEAN_PROPERTY_KEY, booleanProperties);
5349         }
5350     }
5351 
getBooleanProperty(int property)5352     private boolean getBooleanProperty(int property) {
5353         Bundle extras = getExtras();
5354         if (extras == null) return false;
5355         return (extras.getInt(BOOLEAN_PROPERTY_KEY, 0) & property) == property;
5356     }
5357 
getCheckedString()5358     private String getCheckedString() {
5359         @CheckedState int checkedState = getChecked();
5360         if (checkedState == AccessibilityNodeInfo.CHECKED_STATE_TRUE) {
5361             return "TRUE";
5362         } else if (checkedState == AccessibilityNodeInfo.CHECKED_STATE_PARTIAL) {
5363             return "PARTIAL";
5364         } else {
5365             return "FALSE";
5366         }
5367     }
5368 
getActionSymbolicName(int action)5369     static String getActionSymbolicName(int action) {
5370         switch (action) {
5371             case ACTION_FOCUS:
5372                 return "ACTION_FOCUS";
5373             case ACTION_CLEAR_FOCUS:
5374                 return "ACTION_CLEAR_FOCUS";
5375             case ACTION_SELECT:
5376                 return "ACTION_SELECT";
5377             case ACTION_CLEAR_SELECTION:
5378                 return "ACTION_CLEAR_SELECTION";
5379             case ACTION_CLICK:
5380                 return "ACTION_CLICK";
5381             case ACTION_LONG_CLICK:
5382                 return "ACTION_LONG_CLICK";
5383             case ACTION_ACCESSIBILITY_FOCUS:
5384                 return "ACTION_ACCESSIBILITY_FOCUS";
5385             case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
5386                 return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
5387             case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
5388                 return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
5389             case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
5390                 return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
5391             case ACTION_NEXT_HTML_ELEMENT:
5392                 return "ACTION_NEXT_HTML_ELEMENT";
5393             case ACTION_PREVIOUS_HTML_ELEMENT:
5394                 return "ACTION_PREVIOUS_HTML_ELEMENT";
5395             case ACTION_SCROLL_FORWARD:
5396                 return "ACTION_SCROLL_FORWARD";
5397             case ACTION_SCROLL_BACKWARD:
5398                 return "ACTION_SCROLL_BACKWARD";
5399             case ACTION_CUT:
5400                 return "ACTION_CUT";
5401             case ACTION_COPY:
5402                 return "ACTION_COPY";
5403             case ACTION_PASTE:
5404                 return "ACTION_PASTE";
5405             case ACTION_SET_SELECTION:
5406                 return "ACTION_SET_SELECTION";
5407             case ACTION_EXPAND:
5408                 return "ACTION_EXPAND";
5409             case ACTION_COLLAPSE:
5410                 return "ACTION_COLLAPSE";
5411             case ACTION_SET_TEXT:
5412                 return "ACTION_SET_TEXT";
5413             case android.R.id.accessibilityActionScrollUp:
5414                 return "ACTION_SCROLL_UP";
5415             case android.R.id.accessibilityActionScrollLeft:
5416                 return "ACTION_SCROLL_LEFT";
5417             case android.R.id.accessibilityActionScrollDown:
5418                 return "ACTION_SCROLL_DOWN";
5419             case android.R.id.accessibilityActionScrollRight:
5420                 return "ACTION_SCROLL_RIGHT";
5421             case android.R.id.accessibilityActionPageDown:
5422                 return "ACTION_PAGE_DOWN";
5423             case android.R.id.accessibilityActionPageUp:
5424                 return "ACTION_PAGE_UP";
5425             case android.R.id.accessibilityActionPageLeft:
5426                 return "ACTION_PAGE_LEFT";
5427             case android.R.id.accessibilityActionPageRight:
5428                 return "ACTION_PAGE_RIGHT";
5429             case android.R.id.accessibilityActionShowOnScreen:
5430                 return "ACTION_SHOW_ON_SCREEN";
5431             case android.R.id.accessibilityActionScrollToPosition:
5432                 return "ACTION_SCROLL_TO_POSITION";
5433             case android.R.id.accessibilityActionContextClick:
5434                 return "ACTION_CONTEXT_CLICK";
5435             case android.R.id.accessibilityActionSetProgress:
5436                 return "ACTION_SET_PROGRESS";
5437             case android.R.id.accessibilityActionMoveWindow:
5438                 return "ACTION_MOVE_WINDOW";
5439             case android.R.id.accessibilityActionShowTooltip:
5440                 return "ACTION_SHOW_TOOLTIP";
5441             case android.R.id.accessibilityActionHideTooltip:
5442                 return "ACTION_HIDE_TOOLTIP";
5443             case android.R.id.accessibilityActionPressAndHold:
5444                 return "ACTION_PRESS_AND_HOLD";
5445             case android.R.id.accessibilityActionImeEnter:
5446                 return "ACTION_IME_ENTER";
5447             case android.R.id.accessibilityActionDragStart:
5448                 return "ACTION_DRAG_START";
5449             case android.R.id.accessibilityActionDragDrop:
5450                 return "ACTION_DRAG_DROP";
5451             case android.R.id.accessibilityActionDragCancel:
5452                 return "ACTION_DRAG_CANCEL";
5453             case android.R.id.accessibilityActionScrollInDirection:
5454                 return "ACTION_SCROLL_IN_DIRECTION";
5455             default:
5456                 return "ACTION_UNKNOWN";
5457         }
5458     }
5459 
getExpandedStateSymbolicName(@xpandedState int state)5460     static String getExpandedStateSymbolicName(@ExpandedState int state) {
5461         switch(state) {
5462             case AccessibilityNodeInfo.EXPANDED_STATE_UNDEFINED:
5463                 return "UNDEFINED";
5464             case AccessibilityNodeInfo.EXPANDED_STATE_COLLAPSED:
5465                 return "COLLAPSED";
5466             case AccessibilityNodeInfo.EXPANDED_STATE_PARTIAL:
5467                 return "PARTIAL";
5468             case AccessibilityNodeInfo.EXPANDED_STATE_FULL:
5469                 return "FULL";
5470             default:
5471                 return "UNKNOWN";
5472         }
5473     }
5474 
5475     @RequiresApi(30)
5476     private static class Api30Impl {
Api30Impl()5477         private Api30Impl() {
5478             // This class is non instantiable.
5479         }
5480 
setStateDescription(AccessibilityNodeInfo info, CharSequence stateDescription)5481         public static void setStateDescription(AccessibilityNodeInfo info,
5482                 CharSequence stateDescription) {
5483             info.setStateDescription(stateDescription);
5484         }
5485 
getStateDescription(AccessibilityNodeInfo info)5486         public static CharSequence getStateDescription(AccessibilityNodeInfo info) {
5487             return info.getStateDescription();
5488         }
5489 
createRangeInfo(int type, float min, float max, float current)5490         public static Object createRangeInfo(int type, float min, float max, float current) {
5491             return new AccessibilityNodeInfo.RangeInfo(type, min, max, current);
5492         }
5493     }
5494 
5495     @RequiresApi(33)
5496     private static class Api33Impl {
Api33Impl()5497         private Api33Impl() {
5498             // This class is non instantiable.
5499         }
5500 
getExtraRenderingInfo( AccessibilityNodeInfo info)5501         public static AccessibilityNodeInfo.ExtraRenderingInfo getExtraRenderingInfo(
5502                 AccessibilityNodeInfo info) {
5503             return info.getExtraRenderingInfo();
5504         }
5505 
isTextSelectable(AccessibilityNodeInfo info)5506         public static boolean isTextSelectable(AccessibilityNodeInfo info) {
5507             return info.isTextSelectable();
5508         }
5509 
setTextSelectable(AccessibilityNodeInfo info, boolean selectable)5510         public static void setTextSelectable(AccessibilityNodeInfo info, boolean selectable) {
5511             info.setTextSelectable(selectable);
5512         }
5513 
buildCollectionItemInfoCompat( boolean heading, int columnIndex, int rowIndex, int columnSpan, int rowSpan, boolean selected, String rowTitle, String columnTitle)5514         public static CollectionItemInfoCompat buildCollectionItemInfoCompat(
5515                 boolean heading, int columnIndex, int rowIndex, int columnSpan,
5516                 int rowSpan, boolean selected, String rowTitle, String columnTitle) {
5517             return new CollectionItemInfoCompat(
5518                     new AccessibilityNodeInfo.CollectionItemInfo.Builder()
5519                     .setHeading(heading).setColumnIndex(columnIndex)
5520                     .setRowIndex(rowIndex)
5521                     .setColumnSpan(columnSpan)
5522                     .setRowSpan(rowSpan)
5523                     .setSelected(selected)
5524                     .setRowTitle(rowTitle)
5525                     .setColumnTitle(columnTitle)
5526                     .build());
5527         }
5528 
getChild(AccessibilityNodeInfo info, int index, int prefetchingStrategy)5529         public static AccessibilityNodeInfoCompat getChild(AccessibilityNodeInfo info, int index,
5530                 int prefetchingStrategy) {
5531             return AccessibilityNodeInfoCompat.wrapNonNullInstance(info.getChild(index,
5532                     prefetchingStrategy));
5533         }
5534 
getParent(AccessibilityNodeInfo info, int prefetchingStrategy)5535         public static AccessibilityNodeInfoCompat getParent(AccessibilityNodeInfo info,
5536                 int prefetchingStrategy) {
5537             return AccessibilityNodeInfoCompat.wrapNonNullInstance(info.getParent(
5538                     prefetchingStrategy));
5539         }
5540 
getUniqueId(AccessibilityNodeInfo info)5541         public static String getUniqueId(AccessibilityNodeInfo info) {
5542             return info.getUniqueId();
5543         }
5544 
setUniqueId(AccessibilityNodeInfo info, String uniqueId)5545         public static void setUniqueId(AccessibilityNodeInfo info, String uniqueId) {
5546             info.setUniqueId(uniqueId);
5547         }
5548 
getCollectionItemRowTitle(Object info)5549         public static String getCollectionItemRowTitle(Object info) {
5550             return ((AccessibilityNodeInfo.CollectionItemInfo) info).getRowTitle();
5551 
5552         }
5553 
getCollectionItemColumnTitle(Object info)5554         public static String getCollectionItemColumnTitle(Object info) {
5555             return ((AccessibilityNodeInfo.CollectionItemInfo) info).getColumnTitle();
5556         }
5557     }
5558 
5559     @RequiresApi(34)
5560     private static class Api34Impl {
Api34Impl()5561         private Api34Impl() {
5562             // This class is non instantiable.
5563         }
5564 
isAccessibilityDataSensitive(AccessibilityNodeInfo info)5565         public static boolean isAccessibilityDataSensitive(AccessibilityNodeInfo info) {
5566             return info.isAccessibilityDataSensitive();
5567         }
5568 
setAccessibilityDataSensitive(AccessibilityNodeInfo info, boolean accessibilityDataSensitive)5569         public static void setAccessibilityDataSensitive(AccessibilityNodeInfo info,
5570                 boolean accessibilityDataSensitive) {
5571             info.setAccessibilityDataSensitive(accessibilityDataSensitive);
5572         }
5573 
getContainerTitle(AccessibilityNodeInfo info)5574         public static CharSequence getContainerTitle(AccessibilityNodeInfo info) {
5575             return info.getContainerTitle();
5576         }
5577 
setContainerTitle(AccessibilityNodeInfo info, CharSequence containerTitle)5578         public static void setContainerTitle(AccessibilityNodeInfo info,
5579                 CharSequence containerTitle) {
5580             info.setContainerTitle(containerTitle);
5581         }
5582 
getBoundsInWindow(AccessibilityNodeInfo info, Rect bounds)5583         public static void getBoundsInWindow(AccessibilityNodeInfo info, Rect bounds) {
5584             info.getBoundsInWindow(bounds);
5585         }
5586 
setBoundsInWindow(AccessibilityNodeInfo info, Rect bounds)5587         public static void setBoundsInWindow(AccessibilityNodeInfo info, Rect bounds) {
5588             info.setBoundsInWindow(bounds);
5589         }
5590 
hasRequestInitialAccessibilityFocus(AccessibilityNodeInfo info)5591         public static boolean hasRequestInitialAccessibilityFocus(AccessibilityNodeInfo info) {
5592             return info.hasRequestInitialAccessibilityFocus();
5593         }
5594 
setRequestInitialAccessibilityFocus(AccessibilityNodeInfo info, boolean requestInitialAccessibilityFocus)5595         public static void setRequestInitialAccessibilityFocus(AccessibilityNodeInfo info,
5596                 boolean requestInitialAccessibilityFocus) {
5597             info.setRequestInitialAccessibilityFocus(requestInitialAccessibilityFocus);
5598         }
5599 
getMinDurationBetweenContentChangeMillis(AccessibilityNodeInfo info)5600         public static long getMinDurationBetweenContentChangeMillis(AccessibilityNodeInfo info) {
5601             return info.getMinDurationBetweenContentChanges().toMillis();
5602         }
5603 
setMinDurationBetweenContentChangeMillis(AccessibilityNodeInfo info, long duration)5604         public static void setMinDurationBetweenContentChangeMillis(AccessibilityNodeInfo info,
5605                 long duration) {
5606             info.setMinDurationBetweenContentChanges(Duration.ofMillis(duration));
5607         }
5608 
setQueryFromAppProcessEnabled(AccessibilityNodeInfo info, View view, boolean enabled)5609         public static void setQueryFromAppProcessEnabled(AccessibilityNodeInfo info, View view,
5610                 boolean enabled) {
5611             info.setQueryFromAppProcessEnabled(view, enabled);
5612         }
5613 
getActionScrollInDirection()5614         public static AccessibilityNodeInfo.AccessibilityAction getActionScrollInDirection() {
5615             return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_IN_DIRECTION;
5616         }
5617     }
5618 
5619     @RequiresApi(35)
5620     private static class Api35Impl {
Api35Impl()5621         private Api35Impl() {
5622             // This class is non instantiable.
5623         }
5624 
getItemCount(Object info)5625         public static int getItemCount(Object info) {
5626             return ((AccessibilityNodeInfo.CollectionInfo) info).getItemCount();
5627         }
5628 
getImportantForAccessibilityItemCount(Object info)5629         public static int getImportantForAccessibilityItemCount(Object info) {
5630             return ((AccessibilityNodeInfo.CollectionInfo) info)
5631                     .getImportantForAccessibilityItemCount();
5632         }
5633 
buildCollectionInfoCompat(int rowCount, int columnCount, boolean hierarchical, int selectionMode, int itemCount, int importantForAccessibilityItemCount)5634         public static CollectionInfoCompat buildCollectionInfoCompat(int rowCount, int columnCount,
5635                 boolean hierarchical, int selectionMode, int itemCount,
5636                 int importantForAccessibilityItemCount) {
5637             return new CollectionInfoCompat(new AccessibilityNodeInfo.CollectionInfo.Builder()
5638                     .setRowCount(rowCount)
5639                     .setColumnCount(columnCount)
5640                     .setHierarchical(hierarchical)
5641                     .setSelectionMode(selectionMode)
5642                     .setItemCount(itemCount)
5643                     .setImportantForAccessibilityItemCount(importantForAccessibilityItemCount)
5644                     .build());
5645         }
5646     }
5647 
5648     @RequiresApi(36)
5649     private static class Api36Impl {
Api36Impl()5650         private Api36Impl() {
5651             // This class is non instantiable.
5652         }
5653 
5654         @ExpandedState
getExpandedState(AccessibilityNodeInfo info)5655         public static int getExpandedState(AccessibilityNodeInfo info) {
5656             return info.getExpandedState();
5657         }
5658 
setExpandedState(AccessibilityNodeInfo info, @ExpandedState int state)5659         public static void setExpandedState(AccessibilityNodeInfo info, @ExpandedState int state) {
5660             info.setExpandedState(state);
5661         }
5662 
isFieldRequired(AccessibilityNodeInfo info)5663         public static boolean isFieldRequired(AccessibilityNodeInfo info) {
5664             return info.isFieldRequired();
5665         }
5666 
setFieldRequired(AccessibilityNodeInfo info, boolean required)5667         public static void setFieldRequired(AccessibilityNodeInfo info, boolean required) {
5668             info.setFieldRequired(required);
5669         }
5670 
5671         @Nullable
getSupplementalDescription(AccessibilityNodeInfo info)5672         public static CharSequence getSupplementalDescription(AccessibilityNodeInfo info) {
5673             return info.getSupplementalDescription();
5674         }
5675 
setSupplementalDescription( AccessibilityNodeInfo info, @Nullable CharSequence supplementalDescription)5676         public static void setSupplementalDescription(
5677                 AccessibilityNodeInfo info, @Nullable CharSequence supplementalDescription) {
5678             info.setSupplementalDescription(supplementalDescription);
5679         }
5680 
5681         @CheckedState
getChecked(AccessibilityNodeInfo info)5682         private static int getChecked(AccessibilityNodeInfo info) {
5683             return info.getChecked();
5684         }
5685 
setChecked(AccessibilityNodeInfo info, @CheckedState int checked)5686         private static void setChecked(AccessibilityNodeInfo info, @CheckedState int checked) {
5687             info.setChecked(checked);
5688         }
5689     }
5690 }
5691