public class

AccessibilityNodeInfoCompat

extends java.lang.Object

 java.lang.Object

↳androidx.core.view.accessibility.AccessibilityNodeInfoCompat

Gradle dependencies

compile group: 'androidx.core', name: 'core', version: '1.15.0-alpha02'

  • groupId: androidx.core
  • artifactId: core
  • version: 1.15.0-alpha02

Artifact androidx.core:core:1.15.0-alpha02 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.core:core com.android.support:support-compat

Androidx class mapping:

androidx.core.view.accessibility.AccessibilityNodeInfoCompat android.support.v4.view.accessibility.AccessibilityNodeInfoCompat

Overview

Helper for accessing android.view.accessibility.AccessibilityNodeInfo in a backwards compatible fashion.

Summary

Fields
public static final intACTION_ACCESSIBILITY_FOCUS

Action that gives accessibility focus to the node.

public static final java.lang.StringACTION_ARGUMENT_COLUMN_INT

Argument for specifying the collection column to make visible on screen.

public static final java.lang.StringACTION_ARGUMENT_DIRECTION_INT

Argument to represent the direction when using AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_IN_DIRECTION.

public static final java.lang.StringACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN

Argument for whether when moving at granularity to extend the selection or to move it otherwise.

public static final java.lang.StringACTION_ARGUMENT_HTML_ELEMENT_STRING

Argument for which HTML element to get moving to the next/previous HTML element.

public static final java.lang.StringACTION_ARGUMENT_MOVE_WINDOW_X

Argument for specifying the x coordinate to which to move a window.

public static final java.lang.StringACTION_ARGUMENT_MOVE_WINDOW_Y

Argument for specifying the y coordinate to which to move a window.

public static final java.lang.StringACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT

Argument for which movement granularity to be used when traversing the node text.

public static final java.lang.StringACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT

Argument to represent the duration in milliseconds to press and hold a node.

public static final java.lang.StringACTION_ARGUMENT_PROGRESS_VALUE

Argument for specifying the progress value to set.

public static final java.lang.StringACTION_ARGUMENT_ROW_INT

Argument for specifying the collection row to make visible on screen.

public static final java.lang.StringACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT

Argument to represent the scroll amount as a percent of the visible area of a node, with 1.0F as the default.

public static final java.lang.StringACTION_ARGUMENT_SELECTION_END_INT

Argument for specifying the selection end.

public static final java.lang.StringACTION_ARGUMENT_SELECTION_START_INT

Argument for specifying the selection start.

public static final java.lang.StringACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE

Argument for specifying the text content to set

public static final intACTION_CLEAR_ACCESSIBILITY_FOCUS

Action that clears accessibility focus of the node.

public static final intACTION_CLEAR_FOCUS

Action that unfocuses the node.

public static final intACTION_CLEAR_SELECTION

Action that unselects the node.

public static final intACTION_CLICK

Action that clicks on the node info.

public static final intACTION_COLLAPSE

Action to collapse an expandable node.

public static final intACTION_COPY

Action to copy the current selection to the clipboard.

public static final intACTION_CUT

Action to cut the current selection and place it to the clipboard.

public static final intACTION_DISMISS

Action to dismiss a dismissible node.

public static final intACTION_EXPAND

Action to expand an expandable node.

public static final intACTION_FOCUS

Action that focuses the node.

public static final intACTION_LONG_CLICK

Action that long clicks on the node.

public static final intACTION_NEXT_AT_MOVEMENT_GRANULARITY

Action that requests to go to the next entity in this node's text at a given movement granularity.

public static final intACTION_NEXT_HTML_ELEMENT

Action to move to the next HTML element of a given type.

public static final intACTION_PASTE

Action to paste the current clipboard content.

public static final intACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY

Action that requests to go to the previous entity in this node's text at a given movement granularity.

public static final intACTION_PREVIOUS_HTML_ELEMENT

Action to move to the previous HTML element of a given type.

public static final intACTION_SCROLL_BACKWARD

Action to scroll the node content backward.

public static final intACTION_SCROLL_FORWARD

Action to scroll the node content forward.

public static final intACTION_SELECT

Action that selects the node.

public static final intACTION_SET_SELECTION

Action to set the selection.

public static final intACTION_SET_TEXT

Action that sets the text of the node.

public static final java.lang.StringEXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH

Integer argument specifying the end index of the requested text location data.

public static final intEXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH

The maximum allowed length of the requested text location data.

public static final java.lang.StringEXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX

Integer argument specifying the start index of the requested text location data.

public static final java.lang.StringEXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY

Key used to request and locate extra data for text character location.

public static final intFLAG_PREFETCH_ANCESTORS

Prefetching strategy that prefetches the ancestors of the requested node.

public static final intFLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST

Prefetching strategy that prefetches the descendants of the requested node breadth-first.

public static final intFLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST

Prefetching strategy that prefetches the descendants of the requested node depth-first.

public static final intFLAG_PREFETCH_DESCENDANTS_HYBRID

Prefetching strategy that prefetches the descendants in a hybrid depth first and breadth first approach.

public static final intFLAG_PREFETCH_SIBLINGS

Prefetching strategy that prefetches the siblings of the requested node.

public static final intFLAG_PREFETCH_UNINTERRUPTIBLE

Prefetching flag that specifies prefetching should not be interrupted by a request to retrieve a node or perform an action on a node.

public static final intFOCUS_ACCESSIBILITY

The accessibility focus.

public static final intFOCUS_INPUT

The input focus.

public static final intMAX_NUMBER_OF_PREFETCHED_NODES

Maximum batch size of prefetched nodes for a request.

public static final intMOVEMENT_GRANULARITY_CHARACTER

Movement granularity bit for traversing the text of a node by character.

public static final intMOVEMENT_GRANULARITY_LINE

Movement granularity bit for traversing the text of a node by line.

public static final intMOVEMENT_GRANULARITY_PAGE

Movement granularity bit for traversing the text of a node by page.

public static final intMOVEMENT_GRANULARITY_PARAGRAPH

Movement granularity bit for traversing the text of a node by paragraph.

public static final intMOVEMENT_GRANULARITY_WORD

Movement granularity bit for traversing the text of a node by word.

public intmParentVirtualDescendantId

androidx.customview.widget.ExploreByTouchHelper.HOST_ID = -1;

Constructors
publicAccessibilityNodeInfoCompat(java.lang.Object info)

Creates a new instance wrapping an android.view.accessibility.AccessibilityNodeInfo.

Methods
public voidaddAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat action)

Adds an action that can be performed on the node.

public voidaddAction(int action)

Adds an action that can be performed on the node.

public voidaddChild(View child)

Adds a child.

public voidaddChild(View root, int virtualDescendantId)

Adds a virtual child which is a descendant of the given root.

public voidaddSpansToExtras(java.lang.CharSequence text, View view)

public booleancanOpenPopup()

Gets if this node opens a popup or a dialog.

public booleanequals(java.lang.Object obj)

public java.util.List<AccessibilityNodeInfoCompat>findAccessibilityNodeInfosByText(java.lang.String text)

Finds android.view.accessibility.AccessibilityNodeInfos by text.

public java.util.List<AccessibilityNodeInfoCompat>findAccessibilityNodeInfosByViewId(java.lang.String viewId)

Finds AccessibilityNodeInfoCompats by the fully qualified view id's resource name where a fully qualified id is of the from "package:id/id_resource_name".

public AccessibilityNodeInfoCompatfindFocus(int focus)

Find the view that has the specified focus type.

public AccessibilityNodeInfoCompatfocusSearch(int direction)

Searches for the nearest view in the specified direction that can take input focus.

public java.util.List<AccessibilityNodeInfoCompat.AccessibilityActionCompat>getActionList()

Gets the actions that can be performed on the node.

public intgetActions()

Gets the actions that can be performed on the node.

public java.util.List<java.lang.String>getAvailableExtraData()

Get the extra data available for this node.

public voidgetBoundsInParent(Rect outBounds)

Gets the node bounds in the viewParent's coordinates.

public voidgetBoundsInScreen(Rect outBounds)

Gets the node bounds in screen coordinates.

public voidgetBoundsInWindow(Rect outBounds)

Gets the node bounds in window coordinates.

public AccessibilityNodeInfoCompatgetChild(int index)

Get the child at given index.

public AccessibilityNodeInfoCompatgetChild(int index, int prefetchingStrategy)

Get the child at given index.

public intgetChildCount()

Gets the number of children.

public java.lang.CharSequencegetClassName()

Gets the class this node comes from.

public static ClickableSpangetClickableSpans(java.lang.CharSequence text)

public AccessibilityNodeInfoCompat.CollectionInfoCompatgetCollectionInfo()

Gets the collection info if the node is a collection.

public AccessibilityNodeInfoCompat.CollectionItemInfoCompatgetCollectionItemInfo()

Gets the collection item info if the node is a collection item.

public java.lang.CharSequencegetContainerTitle()

Returns the container title.

public java.lang.CharSequencegetContentDescription()

Gets the content description of this node.

public intgetDrawingOrder()

Get the drawing order of the view corresponding it this node.

public java.lang.CharSequencegetError()

Gets the error text of this node.

public AccessibilityNodeInfo.ExtraRenderingInfogetExtraRenderingInfo()

Gets the if the node is meant to be refreshed with extra data to examine rendering related accessibility issues.

public BundlegetExtras()

Gets an optional bundle with extra data.

public java.lang.CharSequencegetHintText()

Gets the hint text of this node.

public java.lang.ObjectgetInfo()

public intgetInputType()

Gets the input type of the source as defined by .

public AccessibilityNodeInfoCompatgetLabeledBy()

Gets the node info which serves as the label of the view represented by this info for accessibility purposes.

public AccessibilityNodeInfoCompatgetLabelFor()

Gets the node info for which the view represented by this info serves as a label for accessibility purposes.

public intgetLiveRegion()

Gets the node's live region mode.

public intgetMaxTextLength()

Returns the maximum text length for this node.

public longgetMinDurationBetweenContentChangesMillis()

Gets the minimum time duration between two content change events.

public intgetMovementGranularities()

Gets the movement granularities for traversing the text of this node.

public java.lang.CharSequencegetPackageName()

Gets the package this node comes from.

public java.lang.CharSequencegetPaneTitle()

Get the title of the pane represented by this node.

public AccessibilityNodeInfoCompatgetParent()

Gets the parent.

public AccessibilityNodeInfoCompatgetParent(int prefetchingStrategy)

Gets the parent.

public AccessibilityNodeInfoCompat.RangeInfoCompatgetRangeInfo()

Gets the range info if this node is a range.

public java.lang.CharSequencegetRoleDescription()

Gets the custom role description.

public java.lang.CharSequencegetStateDescription()

Gets the state description of this node.

public java.lang.CharSequencegetText()

Gets the text of this node.

public intgetTextSelectionEnd()

Gets the text selection end.

public intgetTextSelectionStart()

Gets the text selection start.

public java.lang.CharSequencegetTooltipText()

Gets the tooltip text of this node.

public AccessibilityNodeInfoCompat.TouchDelegateInfoCompatgetTouchDelegateInfo()

Get the AccessibilityNodeInfoCompat.TouchDelegateInfoCompat for touch delegate behavior with the represented view.

public AccessibilityNodeInfoCompatgetTraversalAfter()

Gets the node after which this one is visited in accessibility traversal.

public AccessibilityNodeInfoCompatgetTraversalBefore()

Gets the node before which this one is visited during traversal.

public java.lang.StringgetUniqueId()

Gets the unique id of this node.

public java.lang.StringgetViewIdResourceName()

Gets the fully qualified resource name of the source view's id.

public AccessibilityWindowInfoCompatgetWindow()

Gets the window to which this node belongs.

public intgetWindowId()

Gets the id of the window from which the info comes from.

public inthashCode()

public booleanhasRequestInitialAccessibilityFocus()

Gets whether the node has AccessibilityNodeInfoCompat.setRequestInitialAccessibilityFocus(boolean).

public booleanisAccessibilityDataSensitive()

Gets if the node's accessibility data is considered sensitive.

public booleanisAccessibilityFocused()

Gets whether this node is accessibility focused.

public booleanisCheckable()

Gets whether this node is checkable.

public booleanisChecked()

Gets whether this node is checked.

public booleanisClickable()

Gets whether this node is clickable.

public booleanisContentInvalid()

Gets if the content of this node is invalid.

public booleanisContextClickable()

Gets whether this node is context clickable.

public booleanisDismissable()

Gets if the node can be dismissed.

public booleanisEditable()

Gets if the node is editable.

public booleanisEnabled()

Gets whether this node is enabled.

public booleanisFocusable()

Gets whether this node is focusable.

public booleanisFocused()

Gets whether this node is focused.

public booleanisGranularScrollingSupported()

Gets if the node supports granular scrolling.

public booleanisHeading()

Returns whether node represents a heading.

public booleanisImportantForAccessibility()

Returns whether the node originates from a view considered important for accessibility.

public booleanisLongClickable()

Gets whether this node is long clickable.

public booleanisMultiLine()

Gets if the node is a multi line editable text.

public booleanisPassword()

Gets whether this node is a password.

public booleanisScreenReaderFocusable()

Returns whether the node is explicitly marked as a focusable unit by a screen reader.

public booleanisScrollable()

Gets if the node is scrollable.

public booleanisSelected()

Gets whether this node is selected.

public booleanisShowingHintText()

Returns whether the node's text represents a hint for the user to enter text.

public booleanisTextEntryKey()

Returns whether node represents a text entry key that is part of a keyboard or keypad.

public booleanisTextSelectable()

Gets if the node has selectable text.

public booleanisVisibleToUser()

Gets whether this node is visible to the user.

public static AccessibilityNodeInfoCompatobtain()

Returns a cached instance if such is available otherwise a new one.

public static AccessibilityNodeInfoCompatobtain(View source)

Returns a cached instance if such is available otherwise a new one and sets the source.

public static AccessibilityNodeInfoCompatobtain(View root, int virtualDescendantId)

Returns a cached instance if such is available otherwise a new one and sets the source.

public booleanperformAction(int action)

Performs an action on the node.

public booleanperformAction(int action, Bundle arguments)

Performs an action on the node.

public voidrecycle()

Return an instance back to be reused.

public booleanrefresh()

Refreshes this info with the latest state of the view it represents.

public booleanremoveAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat action)

Removes an action that can be performed on the node.

public booleanremoveChild(View child)

Removes a child.

public booleanremoveChild(View root, int virtualDescendantId)

Removes a virtual child which is a descendant of the given root.

public voidsetAccessibilityDataSensitive(boolean accessibilityDataSensitive)

Sets whether this node's accessibility data is considered sensitive.

public voidsetAccessibilityFocused(boolean focused)

Sets whether this node is accessibility focused.

public voidsetAvailableExtraData(java.util.List<java.lang.String> extraDataKeys)

Set the extra data available for this node.

public voidsetBoundsInParent(Rect bounds)

Sets the node bounds in the viewParent's coordinates.

public voidsetBoundsInScreen(Rect bounds)

Sets the node bounds in screen coordinates.

public voidsetBoundsInWindow(Rect bounds)

Sets the node bounds in window coordinates.

public voidsetCanOpenPopup(boolean opensPopup)

Sets if this node opens a popup or a dialog.

public voidsetCheckable(boolean checkable)

Sets whether this node is checkable.

public voidsetChecked(boolean checked)

Sets whether this node is checked.

public voidsetClassName(java.lang.CharSequence className)

Sets the class this node comes from.

public voidsetClickable(boolean clickable)

Sets whether this node is clickable.

public voidsetCollectionInfo(java.lang.Object collectionInfo)

public voidsetCollectionItemInfo(java.lang.Object collectionItemInfo)

public voidsetContainerTitle(java.lang.CharSequence containerTitle)

Sets the container title for app-developer-defined container which can be any type of ViewGroup or layout.

public voidsetContentDescription(java.lang.CharSequence contentDescription)

Sets the content description of this node.

public voidsetContentInvalid(boolean contentInvalid)

Sets if the content of this node is invalid.

public voidsetContextClickable(boolean contextClickable)

Sets whether this node is context clickable.

public voidsetDismissable(boolean dismissable)

Sets if the node can be dismissed.

public voidsetDrawingOrder(int drawingOrderInParent)

Set the drawing order of the view corresponding it this node.

public voidsetEditable(boolean editable)

Sets whether this node is editable.

public voidsetEnabled(boolean enabled)

Sets whether this node is enabled.

public voidsetError(java.lang.CharSequence error)

Sets the error text of this node.

public voidsetFocusable(boolean focusable)

Sets whether this node is focusable.

public voidsetFocused(boolean focused)

Sets whether this node is focused.

public voidsetGranularScrollingSupported(boolean granularScrollingSupported)

Sets if the node supports granular scrolling.

public voidsetHeading(boolean isHeading)

Sets whether the node represents a heading.

public voidsetHintText(java.lang.CharSequence hintText)

Sets the hint text of this node.

public voidsetImportantForAccessibility(boolean important)

Sets whether the node is considered important for accessibility.

public voidsetInputType(int inputType)

Sets the input type of the source as defined by .

public voidsetLabeledBy(View label)

Sets the view which serves as the label of the view represented by this info for accessibility purposes.

public voidsetLabeledBy(View root, int virtualDescendantId)

Sets the view which serves as the label of the view represented by this info for accessibility purposes.

public voidsetLabelFor(View labeled)

Sets the view for which the view represented by this info serves as a label for accessibility purposes.

public voidsetLabelFor(View root, int virtualDescendantId)

Sets the view for which the view represented by this info serves as a label for accessibility purposes.

public voidsetLiveRegion(int mode)

Sets the node's live region mode.

public voidsetLongClickable(boolean longClickable)

Sets whether this node is long clickable.

public voidsetMaxTextLength(int max)

Sets the maximum text length, or -1 for no limit.

public voidsetMinDurationBetweenContentChangesMillis(long duration)

Sets the minimum time duration between two content change events, which is used in throttling content change events in accessibility services.

public voidsetMovementGranularities(int granularities)

Sets the movement granularities for traversing the text of this node.

public voidsetMultiLine(boolean multiLine)

Sets if the node is a multi line editable text.

public voidsetPackageName(java.lang.CharSequence packageName)

Sets the package this node comes from.

public voidsetPaneTitle(java.lang.CharSequence paneTitle)

If this node represents a visually distinct region of the screen that may update separately from the rest of the window, it is considered a pane.

public voidsetParent(View parent)

Sets the parent.

public voidsetParent(View root, int virtualDescendantId)

Sets the parent to be a virtual descendant of the given root.

public voidsetPassword(boolean password)

Sets whether this node is a password.

public voidsetQueryFromAppProcessEnabled(View view, boolean enabled)

Connects this node to the View's root so that operations on this node can query the entire AccessibilityNodeInfoCompat tree and perform accessibility actions on nodes.

public voidsetRangeInfo(AccessibilityNodeInfoCompat.RangeInfoCompat rangeInfo)

Sets the range info if this node is a range.

public voidsetRequestInitialAccessibilityFocus(boolean requestInitialAccessibilityFocus)

Sets whether the node has requested initial accessibility focus.

public voidsetRoleDescription(java.lang.CharSequence roleDescription)

Sets the custom role description.

public voidsetScreenReaderFocusable(boolean screenReaderFocusable)

Sets whether the node should be considered a focusable unit by a screen reader.

public voidsetScrollable(boolean scrollable)

Sets if the node is scrollable.

public voidsetSelected(boolean selected)

Sets whether this node is selected.

public voidsetShowingHintText(boolean showingHintText)

Sets whether the node's text represents a hint for the user to enter text.

public voidsetSource(View source)

Sets the source.

public voidsetSource(View root, int virtualDescendantId)

Sets the source to be a virtual descendant of the given root.

public voidsetStateDescription(java.lang.CharSequence stateDescription)

Sets the state description of this node.

public voidsetText(java.lang.CharSequence text)

Sets the text of this node.

public voidsetTextEntryKey(boolean isTextEntryKey)

Sets whether the node represents a text entry key that is part of a keyboard or keypad.

public voidsetTextSelectable(boolean selectableText)

Sets if the node has selectable text.

public voidsetTextSelection(int start, int end)

Sets the text selection start and end.

public voidsetTooltipText(java.lang.CharSequence tooltipText)

Sets the tooltip text of this node.

public voidsetTouchDelegateInfo(AccessibilityNodeInfoCompat.TouchDelegateInfoCompat delegatedInfo)

Set touch delegate info if the represented view has a .

public voidsetTraversalAfter(View view)

Sets the view whose node is visited after this one in accessibility traversal.

public voidsetTraversalAfter(View root, int virtualDescendantId)

Sets the node after which this one is visited in accessibility traversal.

public voidsetTraversalBefore(View view)

Sets the view before whose node this one should be visited during traversal.

public voidsetTraversalBefore(View root, int virtualDescendantId)

Sets the node before which this one is visited during traversal.

public voidsetUniqueId(java.lang.String uniqueId)

Sets the unique id of this node.

public voidsetViewIdResourceName(java.lang.String viewId)

Sets the fully qualified resource name of the source view's id.

public voidsetVisibleToUser(boolean visibleToUser)

Sets whether this node is visible to the user.

public java.lang.StringtoString()

public AccessibilityNodeInfounwrap()

public static AccessibilityNodeInfoCompatwrap(AccessibilityNodeInfo info)

Creates a new instance wrapping an android.view.accessibility.AccessibilityNodeInfo.

from java.lang.Objectclone, finalize, getClass, notify, notifyAll, wait, wait, wait

Fields

public int mParentVirtualDescendantId

androidx.customview.widget.ExploreByTouchHelper.HOST_ID = -1;

public static final int ACTION_FOCUS

Action that focuses the node.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_FOCUS

public static final int ACTION_CLEAR_FOCUS

Action that unfocuses the node.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLEAR_FOCUS

public static final int ACTION_SELECT

Action that selects the node.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SELECT

public static final int ACTION_CLEAR_SELECTION

Action that unselects the node.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLEAR_SELECTION

public static final int ACTION_CLICK

Action that clicks on the node info.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK

public static final int ACTION_LONG_CLICK

Action that long clicks on the node.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK

public static final int ACTION_ACCESSIBILITY_FOCUS

Action that gives accessibility focus to the node.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_ACCESSIBILITY_FOCUS

public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS

Action that clears accessibility focus of the node.

public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY

Action that requests to go to the next entity in this node's text at a given movement granularity. For example, move to the next character, word, etc.

Arguments: AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT<, AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
Example: Move to the previous character and do not extend selection.

Bundle arguments = new Bundle(); arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false); info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);

See also: AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, AccessibilityNodeInfoCompat.setMovementGranularities(int), AccessibilityNodeInfoCompat.getMovementGranularities(), AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER, AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_WORD, AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_LINE, AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PARAGRAPH, AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY

public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY

Action that requests to go to the previous entity in this node's text at a given movement granularity. For example, move to the next character, word, etc.

Arguments: AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT<, AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
Example: Move to the next character and do not extend selection.

Bundle arguments = new Bundle(); arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false); info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);

See also: AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, AccessibilityNodeInfoCompat.setMovementGranularities(int), AccessibilityNodeInfoCompat.getMovementGranularities(), AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER, AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_WORD, AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_LINE, AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PARAGRAPH, AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY

public static final int ACTION_NEXT_HTML_ELEMENT

Action to move to the next HTML element of a given type. For example, move to the BUTTON, INPUT, TABLE, etc.

Arguments: AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING
Example:

Bundle arguments = new Bundle(); arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_NEXT_HTML_ELEMENT

public static final int ACTION_PREVIOUS_HTML_ELEMENT

Action to move to the previous HTML element of a given type. For example, move to the BUTTON, INPUT, TABLE, etc.

Arguments: AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING
Example:

Bundle arguments = new Bundle(); arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_PREVIOUS_HTML_ELEMENT

public static final int ACTION_SCROLL_FORWARD

Action to scroll the node content forward.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_FORWARD

public static final int ACTION_SCROLL_BACKWARD

Action to scroll the node content backward.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_BACKWARD

public static final int ACTION_COPY

Action to copy the current selection to the clipboard.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_COPY

public static final int ACTION_PASTE

Action to paste the current clipboard content.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_PASTE

public static final int ACTION_CUT

Action to cut the current selection and place it to the clipboard.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CUT

public static final int ACTION_SET_SELECTION

Action to set the selection. Performing this action with no arguments clears the selection.

Arguments: AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT, AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT
Example:

Bundle arguments = new Bundle(); arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1); arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2); info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments);

See also: AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT, AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_SELECTION

public static final int ACTION_EXPAND

Action to expand an expandable node.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_EXPAND

public static final int ACTION_COLLAPSE

Action to collapse an expandable node.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_COLLAPSE

public static final int ACTION_DISMISS

Action to dismiss a dismissible node.

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_DISMISS

public static final int ACTION_SET_TEXT

Action that sets the text of the node. Performing the action without argument, using null or empty java.lang.CharSequence will clear the text. This action will also put the cursor at the end of text.

Arguments: AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE
Example:

Bundle arguments = new Bundle(); arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "android"); info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_TEXT

public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT

Argument for which movement granularity to be used when traversing the node text.

Type: int
Actions: AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, AccessibilityNodeInfoCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY

public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING

Argument for which HTML element to get moving to the next/previous HTML element.

Type: String
Actions: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_NEXT_HTML_ELEMENT, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_PREVIOUS_HTML_ELEMENT

public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN

Argument for whether when moving at granularity to extend the selection or to move it otherwise.

Type: boolean
Actions: AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, AccessibilityNodeInfoCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY

public static final java.lang.String ACTION_ARGUMENT_SELECTION_START_INT

Argument for specifying the selection start.

Type: int
Actions: AccessibilityNodeInfoCompat.ACTION_SET_SELECTION

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_SELECTION

public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT

Argument for specifying the selection end.

Type: int
Actions: AccessibilityNodeInfoCompat.ACTION_SET_SELECTION

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_SELECTION

public static final java.lang.String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE

Argument for specifying the text content to set

Type: CharSequence
Actions: AccessibilityNodeInfoCompat.ACTION_SET_TEXT

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_TEXT

public static final java.lang.String ACTION_ARGUMENT_ROW_INT

Argument for specifying the collection row to make visible on screen.

Type: int
Actions:

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_TO_POSITION

public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT

Argument for specifying the collection column to make visible on screen.

Type: int
Actions:

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_TO_POSITION

public static final java.lang.String ACTION_ARGUMENT_PROGRESS_VALUE

Argument for specifying the progress value to set.

Type: float
Actions:

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_PROGRESS

public static final java.lang.String ACTION_ARGUMENT_MOVE_WINDOW_X

Argument for specifying the x coordinate to which to move a window.

Type: int
Actions:

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_MOVE_WINDOW

public static final java.lang.String ACTION_ARGUMENT_MOVE_WINDOW_Y

Argument for specifying the y coordinate to which to move a window.

Type: int
Actions:

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_MOVE_WINDOW

public static final java.lang.String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT

Argument to represent the duration in milliseconds to press and hold a node.

Type: int
Actions:

See also: AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_PRESS_AND_HOLD

public static final java.lang.String ACTION_ARGUMENT_DIRECTION_INT

Argument to represent the direction when using AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_IN_DIRECTION.

The value of this argument can be one of:

  • View
  • View
  • View
  • View
  • View
  • View

public static final java.lang.String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT

Argument to represent the scroll amount as a percent of the visible area of a node, with 1.0F as the default. Values smaller than 1.0F represent a partial scroll of the node, and values larger than 1.0F represent a scroll that extends beyond the currently visible node Rect. Setting this to POSITIVE_INFINITY or to another "too large" value should scroll to the end of the node. Negative values should not be used with this argument.

This argument should be used with the following scroll actions:

Example: if a view representing a list of items implements AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_FORWARD to scroll forward by an entire screen (one "page"), then passing a value of .25F via this argument should scroll that view only by 1/4th of a screen. Passing a value of 1.50F via this argument should scroll the view by 1 1/2 screens or to end of the node if the node doesn't extend to 1 1/2 screens.

This argument should not be used with the following scroll actions, which don't cleanly conform to granular scroll semantics:

Views that support this argument should set AccessibilityNodeInfoCompat.setGranularScrollingSupported(boolean) to true. Clients should use AccessibilityNodeInfoCompat.isGranularScrollingSupported() to check if granular scrolling is supported.

public static final int FOCUS_INPUT

The input focus.

public static final int FOCUS_ACCESSIBILITY

The accessibility focus.

public static final int MOVEMENT_GRANULARITY_CHARACTER

Movement granularity bit for traversing the text of a node by character.

public static final int MOVEMENT_GRANULARITY_WORD

Movement granularity bit for traversing the text of a node by word.

public static final int MOVEMENT_GRANULARITY_LINE

Movement granularity bit for traversing the text of a node by line.

public static final int MOVEMENT_GRANULARITY_PARAGRAPH

Movement granularity bit for traversing the text of a node by paragraph.

public static final int MOVEMENT_GRANULARITY_PAGE

Movement granularity bit for traversing the text of a node by page.

public static final java.lang.String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY

Key used to request and locate extra data for text character location. This key requests that an array of s be added to the extras. This request is made with AccessibilityNodeInfoCompat. The arguments taken by this request are two integers: AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX and AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH. The starting index must be valid inside the CharSequence returned by AccessibilityNodeInfoCompat.getText(), and the length must be positive.

The data can be retrieved from the Bundle returned by AccessibilityNodeInfoCompat.getExtras() using this string as a key for . The will be null for characters that either do not exist or are off the screen. AccessibilityNodeInfoCompat

public static final java.lang.String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX

Integer argument specifying the start index of the requested text location data. Must be valid inside the CharSequence returned by AccessibilityNodeInfoCompat.getText().

See also: AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY

public static final java.lang.String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH

Integer argument specifying the end index of the requested text location data. Must be positive and no larger than AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH.

See also: AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY

public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH

The maximum allowed length of the requested text location data.

public static final int FLAG_PREFETCH_ANCESTORS

Prefetching strategy that prefetches the ancestors of the requested node.

Ancestors will be prefetched before siblings and descendants.

See also: AccessibilityNodeInfoCompat.getChild(int, int), AccessibilityNodeInfoCompat.getParent(int), AccessibilityWindowInfoCompat.getRoot(int)

public static final int FLAG_PREFETCH_SIBLINGS

Prefetching strategy that prefetches the siblings of the requested node.

To avoid disconnected trees, this flag will also prefetch the parent. Siblings will be prefetched before descendants.

See also: for where to use these flags.

public static final int FLAG_PREFETCH_DESCENDANTS_HYBRID

Prefetching strategy that prefetches the descendants in a hybrid depth first and breadth first approach.

The children of the root node is prefetched before recursing on the children. This must not be combined with AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST or AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST or this will trigger an IllegalArgumentException.

See also: for where to use these flags.

public static final int FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST

Prefetching strategy that prefetches the descendants of the requested node depth-first.

This must not be combined with AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_HYBRID or AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST or this will trigger an IllegalArgumentException.

See also: for where to use these flags.

public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST

Prefetching strategy that prefetches the descendants of the requested node breadth-first.

This must not be combined with AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_HYBRID or AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST or this will trigger an IllegalArgumentException.

See also: for where to use these flags.

public static final int FLAG_PREFETCH_UNINTERRUPTIBLE

Prefetching flag that specifies prefetching should not be interrupted by a request to retrieve a node or perform an action on a node.

See also: for where to use these flags.

public static final int MAX_NUMBER_OF_PREFETCHED_NODES

Maximum batch size of prefetched nodes for a request.

Constructors

public AccessibilityNodeInfoCompat(java.lang.Object info)

Deprecated: Use AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo) instead.

Creates a new instance wrapping an android.view.accessibility.AccessibilityNodeInfo.

Parameters:

info: The info.

Methods

public static AccessibilityNodeInfoCompat wrap(AccessibilityNodeInfo info)

Creates a new instance wrapping an android.view.accessibility.AccessibilityNodeInfo.

Parameters:

info: The info.

public AccessibilityNodeInfo unwrap()

Returns:

The unwrapped android.view.accessibility.AccessibilityNodeInfo.

public java.lang.Object getInfo()

Deprecated: Use AccessibilityNodeInfoCompat.unwrap() instead.

Returns:

The wrapped android.view.accessibility.AccessibilityNodeInfo.

public static AccessibilityNodeInfoCompat obtain(View source)

Returns a cached instance if such is available otherwise a new one and sets the source.

Returns:

An instance.

See also: AccessibilityNodeInfoCompat.setSource(View)

public static AccessibilityNodeInfoCompat obtain(View root, int virtualDescendantId)

Returns a cached instance if such is available otherwise a new one and sets the source.

Parameters:

root: The root of the virtual subtree.
virtualDescendantId: The id of the virtual descendant.

Returns:

An instance.

See also: AccessibilityNodeInfoCompat.setSource(View, int)

public static AccessibilityNodeInfoCompat obtain()

Returns a cached instance if such is available otherwise a new one.

Returns:

An instance.

public void setSource(View source)

Sets the source.

Parameters:

source: The info source.

public void setSource(View root, int virtualDescendantId)

Sets the source to be a virtual descendant of the given root. If virtualDescendantId is View the root is set as the source.

A virtual descendant is an imaginary View that is reported as a part of the view hierarchy for accessibility purposes. This enables custom views that draw complex content to report themselves as a tree of virtual views, thus conveying their logical structure.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

This method is not supported on devices running API level < 16 since the platform did not support virtual descendants of real views.

Parameters:

root: The root of the virtual subtree.
virtualDescendantId: The id of the virtual descendant.

public AccessibilityNodeInfoCompat findFocus(int focus)

Find the view that has the specified focus type. The search starts from the view represented by this node info.

Parameters:

focus: The focus to find. One of AccessibilityNodeInfoCompat.FOCUS_INPUT or AccessibilityNodeInfoCompat.FOCUS_ACCESSIBILITY.

Returns:

The node info of the focused view or null.

See also: AccessibilityNodeInfoCompat.FOCUS_INPUT, AccessibilityNodeInfoCompat.FOCUS_ACCESSIBILITY

public AccessibilityNodeInfoCompat focusSearch(int direction)

Searches for the nearest view in the specified direction that can take input focus.

Parameters:

direction: The direction. Can be one of: View, View, View, View, View, View.

Returns:

The node info for the view that can take accessibility focus.

public int getWindowId()

Gets the id of the window from which the info comes from.

Returns:

The window id.

public int getChildCount()

Gets the number of children.

Returns:

The child count.

public AccessibilityNodeInfoCompat getChild(int index)

Get the child at given index.

Parameters:

index: The child index.

Returns:

The child node.

public AccessibilityNodeInfoCompat getChild(int index, int prefetchingStrategy)

Get the child at given index.

Parameters:

index: The child index.
prefetchingStrategy: the prefetching strategy.

Returns:

The child node.

See also: for a description of prefetching.

public void addChild(View child)

Adds a child.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

child: The child.

public void addChild(View root, int virtualDescendantId)

Adds a virtual child which is a descendant of the given root. If virtualDescendantId is View the root is added as a child.

A virtual descendant is an imaginary View that is reported as a part of the view hierarchy for accessibility purposes. This enables custom views that draw complex content to report them selves as a tree of virtual views, thus conveying their logical structure.

Parameters:

root: The root of the virtual subtree.
virtualDescendantId: The id of the virtual child.

public boolean removeChild(View child)

Removes a child. If the child was not previously added to the node, calling this method has no effect.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

child: The child.

Returns:

true if the child was present

public boolean removeChild(View root, int virtualDescendantId)

Removes a virtual child which is a descendant of the given root. If the child was not previously added to the node, calling this method has no effect.

Parameters:

root: The root of the virtual subtree.
virtualDescendantId: The id of the virtual child.

Returns:

true if the child was present

See also: AccessibilityNodeInfoCompat.addChild(View, int)

public int getActions()

Deprecated: Use AccessibilityNodeInfoCompat.getActionList() instead.

Gets the actions that can be performed on the node.

Returns:

The bit mask of with actions.

See also: android.view.accessibility.AccessibilityNodeInfo, android.view.accessibility.AccessibilityNodeInfo, android.view.accessibility.AccessibilityNodeInfo, android.view.accessibility.AccessibilityNodeInfo

public void addAction(int action)

Adds an action that can be performed on the node.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

action: The action.

Adds an action that can be performed on the node.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

action: The action.

public boolean removeAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat action)

Removes an action that can be performed on the node. If the action was not already added to the node, calling this method has no effect.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

action: The action to be removed.

Returns:

The action removed from the list of actions.

public boolean performAction(int action)

Performs an action on the node.

Note: An action can be performed only if the request is made from an .

Parameters:

action: The action to perform.

Returns:

True if the action was performed.

public boolean performAction(int action, Bundle arguments)

Performs an action on the node.

Note: An action can be performed only if the request is made from an .

Parameters:

action: The action to perform.
arguments: A bundle with additional arguments.

Returns:

True if the action was performed.

public void setMovementGranularities(int granularities)

Sets the movement granularities for traversing the text of this node.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

granularities: The bit mask with granularities.

public int getMovementGranularities()

Gets the movement granularities for traversing the text of this node.

Returns:

The bit mask with granularities.

public java.util.List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String text)

Finds android.view.accessibility.AccessibilityNodeInfos by text. The match is case insensitive containment. The search is relative to this info i.e. this info is the root of the traversed tree.

Parameters:

text: The searched text.

Returns:

A list of node info.

public AccessibilityNodeInfoCompat getParent()

Gets the parent.

Returns:

The parent.

public AccessibilityNodeInfoCompat getParent(int prefetchingStrategy)

Gets the parent.

Use prefetchingStrategy to determine the types of nodes prefetched from the app if the requested node is not in the cache and must be retrieved by the app. The default strategy for AccessibilityNodeInfoCompat.getParent() is a combination of ancestor and sibling strategies. The app will prefetch until all nodes fulfilling the strategies are fetched, another node request is sent, or the maximum prefetch batch size of AccessibilityNodeInfoCompat.MAX_NUMBER_OF_PREFETCHED_NODES nodes is reached. To prevent interruption by another request and to force prefetching of the max batch size, use AccessibilityNodeInfoCompat.FLAG_PREFETCH_UNINTERRUPTIBLE.

Parameters:

prefetchingStrategy: the prefetching strategy.

Returns:

The parent.

See also: AccessibilityNodeInfoCompat.FLAG_PREFETCH_ANCESTORS, AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST, AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST, AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_HYBRID, AccessibilityNodeInfoCompat.FLAG_PREFETCH_SIBLINGS, AccessibilityNodeInfoCompat.FLAG_PREFETCH_UNINTERRUPTIBLE

public void setParent(View parent)

Sets the parent.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

parent: The parent.

public void setParent(View root, int virtualDescendantId)

Sets the parent to be a virtual descendant of the given root. If virtualDescendantId equals to View the root is set as the parent.

A virtual descendant is an imaginary View that is reported as a part of the view hierarchy for accessibility purposes. This enables custom views that draw complex content to report them selves as a tree of virtual views, thus conveying their logical structure.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

This method is not supported on devices running API level < 16 since the platform did not support virtual descendants of real views.

Parameters:

root: The root of the virtual subtree.
virtualDescendantId: The id of the virtual descendant.

public void getBoundsInParent(Rect outBounds)

Deprecated: Use AccessibilityNodeInfoCompat.getBoundsInScreen(Rect) instead.

Gets the node bounds in the viewParent's coordinates. AccessibilityNodeInfoCompat.getParent() does not represent the source's viewParent. Instead it represents the result of View, which returns the closest ancestor where View is true. So this method is not reliable.

Parameters:

outBounds: The output node bounds.

public void setBoundsInParent(Rect bounds)

Deprecated: Accessibility services should not care about these bounds.

Sets the node bounds in the viewParent's coordinates. AccessibilityNodeInfoCompat.getParent() does not represent the source's viewParent. Instead it represents the result of View, which returns the closest ancestor where View is true. So this method is not reliable.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

bounds: The node bounds.

public void getBoundsInScreen(Rect outBounds)

Gets the node bounds in screen coordinates.

Parameters:

outBounds: The output node bounds.

public void setBoundsInScreen(Rect bounds)

Sets the node bounds in screen coordinates.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

bounds: The node bounds.

public void getBoundsInWindow(Rect outBounds)

Gets the node bounds in window coordinates.

When magnification is enabled, the bounds in window are scaled up by magnification scale and the positions are also adjusted according to the offset of magnification viewport. For example, it returns Rect(-180, -180, 0, 0) for original bounds Rect(10, 10, 100, 100), when the magnification scale is 2 and offsets for X and Y are both 200.

Compatibility:

  • API < 19: No-op

Parameters:

outBounds: The output node bounds.

public void setBoundsInWindow(Rect bounds)

Sets the node bounds in window coordinates.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Compatibility:

  • API < 19: No-op

Parameters:

bounds: The node bounds.

public boolean isCheckable()

Gets whether this node is checkable.

Returns:

True if the node is checkable.

public void setCheckable(boolean checkable)

Sets whether this node is checkable.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

checkable: True if the node is checkable.

public boolean isChecked()

Gets whether this node is checked.

Returns:

True if the node is checked.

public void setChecked(boolean checked)

Sets whether this node is checked.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

checked: True if the node is checked.

public boolean isFocusable()

Gets whether this node is focusable.

Returns:

True if the node is focusable.

public void setFocusable(boolean focusable)

Sets whether this node is focusable.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

focusable: True if the node is focusable.

public boolean isFocused()

Gets whether this node is focused.

Returns:

True if the node is focused.

public void setFocused(boolean focused)

Sets whether this node is focused.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

focused: True if the node is focused.

public boolean isVisibleToUser()

Gets whether this node is visible to the user.

Returns:

Whether the node is visible to the user.

public void setVisibleToUser(boolean visibleToUser)

Sets whether this node is visible to the user.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

visibleToUser: Whether the node is visible to the user.

public boolean isAccessibilityFocused()

Gets whether this node is accessibility focused.

Returns:

True if the node is accessibility focused.

public void setAccessibilityFocused(boolean focused)

Sets whether this node is accessibility focused.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

focused: True if the node is accessibility focused.

public boolean isSelected()

Gets whether this node is selected.

Returns:

True if the node is selected.

public void setSelected(boolean selected)

Sets whether this node is selected.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

selected: True if the node is selected.

public boolean isClickable()

Gets whether this node is clickable.

Returns:

True if the node is clickable.

public void setClickable(boolean clickable)

Sets whether this node is clickable.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

clickable: True if the node is clickable.

public boolean isLongClickable()

Gets whether this node is long clickable.

Returns:

True if the node is long clickable.

public void setLongClickable(boolean longClickable)

Sets whether this node is long clickable.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

longClickable: True if the node is long clickable.

public boolean isEnabled()

Gets whether this node is enabled.

Returns:

True if the node is enabled.

public void setEnabled(boolean enabled)

Sets whether this node is enabled.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

enabled: True if the node is enabled.

public boolean isPassword()

Gets whether this node is a password.

Returns:

True if the node is a password.

public void setPassword(boolean password)

Sets whether this node is a password.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

password: True if the node is a password.

public boolean isScrollable()

Gets if the node is scrollable.

Returns:

True if the node is scrollable, false otherwise.

public void setScrollable(boolean scrollable)

Sets if the node is scrollable.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

scrollable: True if the node is scrollable, false otherwise.

public boolean isGranularScrollingSupported()

Gets if the node supports granular scrolling.

Compatibility:

  • Api < 19: Returns false.

Returns:

True if all scroll actions that could support AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT have done so, false otherwise.

public void setGranularScrollingSupported(boolean granularScrollingSupported)

Sets if the node supports granular scrolling. This should be set to true if all scroll actions which could support AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT have done so.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Compatibility:

  • Api < 19: No-op.

Parameters:

granularScrollingSupported: True if the node supports granular scrolling, false otherwise.

public boolean isTextSelectable()

Gets if the node has selectable text.

Services should use AccessibilityNodeInfoCompat.ACTION_SET_SELECTION for selection. Editable text nodes must also be selectable. But not all UIs will populate this field, so services should consider 'isTextSelectable | isEditable' to ensure they don't miss nodes with selectable text.

Compatibility:

  • Api < 19: Returns false.

Returns:

True if the node has selectable text.

See also: AccessibilityNodeInfoCompat.isEditable()

public void setTextSelectable(boolean selectableText)

Sets if the node has selectable text.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Compatibility:

  • Api < 19: Does not operate.

Parameters:

selectableText: True if the node has selectable text, false otherwise.

public long getMinDurationBetweenContentChangesMillis()

Gets the minimum time duration between two content change events.

public void setMinDurationBetweenContentChangesMillis(long duration)

Sets the minimum time duration between two content change events, which is used in throttling content change events in accessibility services.

Example: An app can set MinDurationBetweenContentChanges as 1 min for a view which sends content change events to accessibility services one event per second. Accessibility service will throttle those content change events and only handle one event per minute for that view.

Parameters:

duration: the minimum duration between content change events.

See also: for all content change types.

public boolean isImportantForAccessibility()

Returns whether the node originates from a view considered important for accessibility.

Returns:

true if the node originates from a view considered important for accessibility, false otherwise

See also: View

public void setImportantForAccessibility(boolean important)

Sets whether the node is considered important for accessibility.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

important: true if the node is considered important for accessibility, false otherwise

public boolean isAccessibilityDataSensitive()

Gets if the node's accessibility data is considered sensitive.

Returns:

True if the node's data is considered sensitive, false otherwise.

See also: View

public void setAccessibilityDataSensitive(boolean accessibilityDataSensitive)

Sets whether this node's accessibility data is considered sensitive.

For SDK 34 and higher: when set to true the framework will hide this node from accessibility services with the property set to false.

Otherwise, for SDK 19 and higher: the framework cannot hide this node but this property may be read by accessibility services to provide modified behavior for sensitive nodes.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

accessibilityDataSensitive: True if the node's accessibility data is considered sensitive.

public java.lang.CharSequence getPackageName()

Gets the package this node comes from.

Returns:

The package name.

public void setPackageName(java.lang.CharSequence packageName)

Sets the package this node comes from.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

packageName: The package name.

public java.lang.CharSequence getClassName()

Gets the class this node comes from.

Returns:

The class name.

public void setClassName(java.lang.CharSequence className)

Sets the class this node comes from.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

className: The class name.

public java.lang.CharSequence getText()

Gets the text of this node.

Returns:

The text.

public void setText(java.lang.CharSequence text)

Sets the text of this node.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

text: The text.

public void addSpansToExtras(java.lang.CharSequence text, View view)

public static ClickableSpan getClickableSpans(java.lang.CharSequence text)

public java.lang.CharSequence getContentDescription()

Gets the content description of this node.

Returns:

The content description.

public java.lang.CharSequence getStateDescription()

Gets the state description of this node.

Returns:

the state description or null if android version smaller than 19.

public void setContentDescription(java.lang.CharSequence contentDescription)

Sets the content description of this node.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

contentDescription: The content description.

public void setStateDescription(java.lang.CharSequence stateDescription)

Sets the state description of this node.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

stateDescription: the state description of this node.

public java.lang.String getUniqueId()

Gets the unique id of this node.

Returns:

the unique id or null if android version smaller than 19.

public void setUniqueId(java.lang.String uniqueId)

Sets the unique id of this node.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

uniqueId: the unique id of this node.

public void setContainerTitle(java.lang.CharSequence containerTitle)

Sets the container title for app-developer-defined container which can be any type of ViewGroup or layout. Container title will be used to group together related controls, similar to HTML fieldset. Or container title may identify a large piece of the UI that is visibly grouped together, such as a toolbar or a card, etc.

Container title helps to assist in navigation across containers and other groups. For example, a screen reader may use this to determine where to put accessibility focus.

Container title is different from pane titleAccessibilityNodeInfoCompat.setPaneTitle(CharSequence) which indicates that the node represents a window or activity.

Example: An app can set container titles on several non-modal menus, containing TextViews or ImageButtons that have content descriptions, text, etc. Screen readers can quickly switch accessibility focus among menus instead of child views. Other accessibility-services can easily find the menu.

Compatibility:

  • API < 19: No-op

Parameters:

containerTitle: The container title that is associated with a ViewGroup/Layout on the screen.

public java.lang.CharSequence getContainerTitle()

Returns the container title.

Compatibility:

  • API < 19: Returns null

See also: for details.

public void recycle()

Deprecated: Accessibility Object recycling is no longer necessary or functional.

Return an instance back to be reused.

Note: You must not touch the object after calling this function.

public void setViewIdResourceName(java.lang.String viewId)

Sets the fully qualified resource name of the source view's id.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

viewId: The id resource name.

public java.lang.String getViewIdResourceName()

Gets the fully qualified resource name of the source view's id.

Note: The primary usage of this API is for UI test automation and in order to report the source view id of an AccessibilityNodeInfoCompat the client has to set the AccessibilityServiceInfoCompat.FLAG_REPORT_VIEW_IDS flag when configuring their .

Returns:

The id resource name.

public int getLiveRegion()

Gets the node's live region mode.

A live region is a node that contains information that is important for the user and when it changes the user should be notified. For example, in a login screen with a TextView that displays an "incorrect password" notification, that view should be marked as a live region with mode ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE.

It is the responsibility of the accessibility service to monitor AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED events indicating changes to live region nodes and their children.

Returns:

The live region mode, or ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE if the view is not a live region.

See also: ViewCompat.getAccessibilityLiveRegion(View)

public void setLiveRegion(int mode)

Sets the node's live region mode.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

mode: The live region mode, or ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE if the view is not a live region.

See also: ViewCompat.setAccessibilityLiveRegion(View, int)

public int getDrawingOrder()

Get the drawing order of the view corresponding it this node.

Drawing order is determined only within the node's parent, so this index is only relative to its siblings.

In some cases, the drawing order is essentially simultaneous, so it is possible for two siblings to return the same value. It is also possible that values will be skipped.

Returns:

The drawing position of the view corresponding to this node relative to its siblings.

public void setDrawingOrder(int drawingOrderInParent)

Set the drawing order of the view corresponding it this node.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

drawingOrderInParent:

Gets the collection info if the node is a collection. A collection child is always a collection item.

Returns:

The collection info.

public void setCollectionInfo(java.lang.Object collectionInfo)

public void setCollectionItemInfo(java.lang.Object collectionItemInfo)

Gets the collection item info if the node is a collection item. A collection item is always a child of a collection.

Returns:

The collection item info.

Gets the range info if this node is a range.

Returns:

The range.

public void setRangeInfo(AccessibilityNodeInfoCompat.RangeInfoCompat rangeInfo)

Sets the range info if this node is a range.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

rangeInfo: The range info.

public AccessibilityNodeInfo.ExtraRenderingInfo getExtraRenderingInfo()

Gets the if the node is meant to be refreshed with extra data to examine rendering related accessibility issues.

Returns:

The .

public java.util.List<AccessibilityNodeInfoCompat.AccessibilityActionCompat> getActionList()

Gets the actions that can be performed on the node.

Returns:

A list of AccessibilityActions.

Compatibility:

  • API < 21: Always returns null

public void setContentInvalid(boolean contentInvalid)

Sets if the content of this node is invalid. For example, a date is not well-formed.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

contentInvalid: If the node content is invalid.

public boolean isContentInvalid()

Gets if the content of this node is invalid. For example, a date is not well-formed.

Returns:

If the node content is invalid.

public boolean isContextClickable()

Gets whether this node is context clickable.

Returns:

True if the node is context clickable.

public void setContextClickable(boolean contextClickable)

Sets whether this node is context clickable.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

contextClickable: True if the node is context clickable.

public java.lang.CharSequence getHintText()

Gets the hint text of this node. Only applies to nodes where text can be entered.

Returns:

The hint text.

public void setHintText(java.lang.CharSequence hintText)

Sets the hint text of this node. Only applies to nodes where text can be entered.

This method has no effect below API 19

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

hintText: The hint text for this mode.

public void setError(java.lang.CharSequence error)

Sets the error text of this node.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

error: The error text.

public java.lang.CharSequence getError()

Gets the error text of this node.

Returns:

The error text.

public void setLabelFor(View labeled)

Sets the view for which the view represented by this info serves as a label for accessibility purposes.

Parameters:

labeled: The view for which this info serves as a label.

public void setLabelFor(View root, int virtualDescendantId)

Sets the view for which the view represented by this info serves as a label for accessibility purposes. If virtualDescendantId is View the root is set as the labeled.

A virtual descendant is an imaginary View that is reported as a part of the view hierarchy for accessibility purposes. This enables custom views that draw complex content to report themselves as a tree of virtual views, thus conveying their logical structure.

Parameters:

root: The root whose virtual descendant serves as a label.
virtualDescendantId: The id of the virtual descendant.

public AccessibilityNodeInfoCompat getLabelFor()

Gets the node info for which the view represented by this info serves as a label for accessibility purposes.

Returns:

The labeled info.

public void setLabeledBy(View label)

Sets the view which serves as the label of the view represented by this info for accessibility purposes.

Parameters:

label: The view that labels this node's source.

public void setLabeledBy(View root, int virtualDescendantId)

Sets the view which serves as the label of the view represented by this info for accessibility purposes. If virtualDescendantId is View the root is set as the label.

A virtual descendant is an imaginary View that is reported as a part of the view hierarchy for accessibility purposes. This enables custom views that draw complex content to report themselves as a tree of virtual views, thus conveying their logical structure.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

root: The root whose virtual descendant labels this node's source.
virtualDescendantId: The id of the virtual descendant.

public AccessibilityNodeInfoCompat getLabeledBy()

Gets the node info which serves as the label of the view represented by this info for accessibility purposes.

Returns:

The label.

public boolean canOpenPopup()

Gets if this node opens a popup or a dialog.

Returns:

If the the node opens a popup.

public void setCanOpenPopup(boolean opensPopup)

Sets if this node opens a popup or a dialog.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

opensPopup: If the the node opens a popup.

public java.util.List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(java.lang.String viewId)

Finds AccessibilityNodeInfoCompats by the fully qualified view id's resource name where a fully qualified id is of the from "package:id/id_resource_name". For example, if the target application's package is "foo.bar" and the id resource name is "baz", the fully qualified resource id is "foo.bar:id/baz".

Note: The primary usage of this API is for UI test automation and in order to report the fully qualified view id if an AccessibilityNodeInfoCompat the client has to set the flag when configuring their .

Parameters:

viewId: The fully qualified resource name of the view id to find.

Returns:

A list of node info.

public Bundle getExtras()

Gets an optional bundle with extra data. The bundle is lazily created and never null.

Note: It is recommended to use the package name of your application as a prefix for the keys to avoid collisions which may confuse an accessibility service if the same key has different meaning when emitted from different applications.

Returns:

The bundle.

public int getInputType()

Gets the input type of the source as defined by .

Returns:

The input type.

public void setInputType(int inputType)

Sets the input type of the source as defined by .

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

inputType: The input type.

public java.util.List<java.lang.String> getAvailableExtraData()

Get the extra data available for this node.

Some data that is useful for some accessibility services is expensive to compute, and would place undue overhead on apps to compute all the time. That data can be requested with AccessibilityNodeInfoCompat.

Returns:

An unmodifiable list of keys corresponding to extra data that can be requested.

See also: AccessibilityNodeInfoCompat, AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY

public void setAvailableExtraData(java.util.List<java.lang.String> extraDataKeys)

Set the extra data available for this node.

Note: When a View passes in a non-empty list, it promises that it will populate the node's extras with corresponding pieces of information in View.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

extraDataKeys: A list of types of extra data that are available.

See also: AccessibilityNodeInfoCompat.getAvailableExtraData()

public void setMaxTextLength(int max)

Sets the maximum text length, or -1 for no limit.

Typically used to indicate that an editable text field has a limit on the number of characters entered.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

max: The maximum text length.

See also: AccessibilityNodeInfoCompat.getMaxTextLength()

public int getMaxTextLength()

Returns the maximum text length for this node.

Returns:

The maximum text length, or -1 for no limit.

See also: AccessibilityNodeInfoCompat.setMaxTextLength(int)

public void setTextSelection(int start, int end)

Sets the text selection start and end.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

start: The text selection start.
end: The text selection end.

public int getTextSelectionStart()

Gets the text selection start.

Returns:

The text selection start if there is selection or -1.

public int getTextSelectionEnd()

Gets the text selection end.

Returns:

The text selection end if there is selection or -1.

public AccessibilityNodeInfoCompat getTraversalBefore()

Gets the node before which this one is visited during traversal. A screen-reader must visit the content of this node before the content of the one it precedes.

Returns:

The succeeding node if such or null.

See also: AccessibilityNodeInfoCompat, AccessibilityNodeInfoCompat

public void setTraversalBefore(View view)

Sets the view before whose node this one should be visited during traversal. A screen-reader must visit the content of this node before the content of the one it precedes.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

view: The view providing the preceding node.

See also: AccessibilityNodeInfoCompat.getTraversalBefore()

public void setTraversalBefore(View root, int virtualDescendantId)

Sets the node before which this one is visited during traversal. A screen-reader must visit the content of this node before the content of the one it precedes. The successor is a virtual descendant of the given root. If virtualDescendantId equals to View the root is set as the successor.

A virtual descendant is an imaginary View that is reported as a part of the view hierarchy for accessibility purposes. This enables custom views that draw complex content to report them selves as a tree of virtual views, thus conveying their logical structure.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

root: The root of the virtual subtree.
virtualDescendantId: The id of the virtual descendant.

public AccessibilityNodeInfoCompat getTraversalAfter()

Gets the node after which this one is visited in accessibility traversal. A screen-reader must visit the content of the other node before the content of this one.

Returns:

The succeeding node if such or null.

See also: AccessibilityNodeInfoCompat, AccessibilityNodeInfoCompat

public void setTraversalAfter(View view)

Sets the view whose node is visited after this one in accessibility traversal. A screen-reader must visit the content of the other node before the content of this one.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

view: The previous view.

See also: AccessibilityNodeInfoCompat.getTraversalAfter()

public void setTraversalAfter(View root, int virtualDescendantId)

Sets the node after which this one is visited in accessibility traversal. A screen-reader must visit the content of the other node before the content of this one. If virtualDescendantId equals to View the root is set as the predecessor.

A virtual descendant is an imaginary View that is reported as a part of the view hierarchy for accessibility purposes. This enables custom views that draw complex content to report them selves as a tree of virtual views, thus conveying their logical structure.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

root: The root of the virtual subtree.
virtualDescendantId: The id of the virtual descendant.

Gets the window to which this node belongs.

Returns:

The window.

See also:

public boolean isDismissable()

Gets if the node can be dismissed.

Returns:

If the node can be dismissed.

public void setDismissable(boolean dismissable)

Sets if the node can be dismissed.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

dismissable: If the node can be dismissed.

public boolean isEditable()

Gets if the node is editable.

Returns:

True if the node is editable, false otherwise.

public void setEditable(boolean editable)

Sets whether this node is editable.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

editable: True if the node is editable.

public boolean isMultiLine()

Gets if the node is a multi line editable text.

Returns:

True if the node is multi line.

public void setMultiLine(boolean multiLine)

Sets if the node is a multi line editable text.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

multiLine: True if the node is multi line.

public java.lang.CharSequence getTooltipText()

Gets the tooltip text of this node.

Returns:

The tooltip text.

public void setTooltipText(java.lang.CharSequence tooltipText)

Sets the tooltip text of this node.

This method has no effect below API 19

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

tooltipText: The tooltip text.

public void setPaneTitle(java.lang.CharSequence paneTitle)

If this node represents a visually distinct region of the screen that may update separately from the rest of the window, it is considered a pane. Set the pane title to indicate that the node is a pane, and to provide a title for it.

This method has no effect below API 19

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

paneTitle: The title of the window represented by this node.

public java.lang.CharSequence getPaneTitle()

Get the title of the pane represented by this node.

Returns:

The title of the pane represented by this node, or null if this node does not represent a pane.

public boolean isScreenReaderFocusable()

Returns whether the node is explicitly marked as a focusable unit by a screen reader. Note that false indicates that it is not explicitly marked, not that the node is not a focusable unit. Screen readers should generally use other signals, such as AccessibilityNodeInfoCompat.isFocusable(), or the presence of text in a node, to determine what should receive focus.

Returns:

true if the node is specifically marked as a focusable unit for screen readers, false otherwise.

public void setScreenReaderFocusable(boolean screenReaderFocusable)

Sets whether the node should be considered a focusable unit by a screen reader.

This method has no effect below API 19

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

screenReaderFocusable: true if the node is a focusable unit for screen readers, false otherwise.

public boolean isShowingHintText()

Returns whether the node's text represents a hint for the user to enter text. It should only be true if the node has editable text.

Returns:

true if the text in the node represents a hint to the user, false otherwise.

public void setShowingHintText(boolean showingHintText)

Sets whether the node's text represents a hint for the user to enter text. It should only be true if the node has editable text.

This method has no effect below API 19

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

showingHintText: true if the text in the node represents a hint to the user, false otherwise.

public boolean isHeading()

Returns whether node represents a heading.

Note: Returns true if either AccessibilityNodeInfoCompat.setHeading(boolean) marks this node as a heading or if the node has a AccessibilityNodeInfoCompat.CollectionItemInfoCompat that marks it as such, to accommodate apps that use the now-deprecated API.

Returns:

true if the node is a heading, false otherwise.

public void setHeading(boolean isHeading)

Sets whether the node represents a heading.

This method has no effect below API 19

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

isHeading: true if the node is a heading, false otherwise.

public boolean isTextEntryKey()

Returns whether node represents a text entry key that is part of a keyboard or keypad.

Returns:

true if the node is a text entry key, false otherwise.

public void setTextEntryKey(boolean isTextEntryKey)

Sets whether the node represents a text entry key that is part of a keyboard or keypad.

This method has no effect below API 19

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

isTextEntryKey: true if the node is a text entry key, false otherwise.

public boolean hasRequestInitialAccessibilityFocus()

Gets whether the node has AccessibilityNodeInfoCompat.setRequestInitialAccessibilityFocus(boolean).

Returns:

True if the node has requested initial accessibility focus.

public void setRequestInitialAccessibilityFocus(boolean requestInitialAccessibilityFocus)

Sets whether the node has requested initial accessibility focus.

If the node AccessibilityNodeInfoCompat.hasRequestInitialAccessibilityFocus(), this node would be one of candidates to be accessibility focused when the window appears.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

requestInitialAccessibilityFocus: True if the node requests to receive initial accessibility focus.

public boolean refresh()

Refreshes this info with the latest state of the view it represents.

Note: If this method returns false this info is obsolete since it represents a view that is no longer in the view tree.

Returns:

Whether the refresh succeeded.

public java.lang.CharSequence getRoleDescription()

Gets the custom role description.

Returns:

The role description.

public void setRoleDescription(java.lang.CharSequence roleDescription)

Sets the custom role description.

The role description allows you to customize the name for the view's semantic role. For example, if you create a custom subclass of android.view.View to display a menu bar, you could assign it the role description of "menu bar".

Warning: For consistency with other applications, you should not use the role description to force accessibility services to describe standard views (such as buttons or checkboxes) using specific wording. For example, you should not set a role description of "check box" or "tick box" for a standard . Instead let accessibility services decide what feedback to provide.

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Parameters:

roleDescription: The role description.

Get the AccessibilityNodeInfoCompat.TouchDelegateInfoCompat for touch delegate behavior with the represented view. It is possible for the same node to be pointed to by several regions. Use AccessibilityNodeInfoCompat.TouchDelegateInfoCompat.getRegionAt(int) to get touch delegate target , and AccessibilityNodeInfoCompat.TouchDelegateInfoCompat.getTargetForRegion(Region) for AccessibilityNodeInfoCompat from the given region.

Compatibility:

  • API < 29: Always returns null

Returns:

AccessibilityNodeInfoCompat.TouchDelegateInfoCompat or null if there are no touch delegates in this node.

public void setTouchDelegateInfo(AccessibilityNodeInfoCompat.TouchDelegateInfoCompat delegatedInfo)

Set touch delegate info if the represented view has a .

Note: Cannot be called from an . This class is made immutable before being delivered to an AccessibilityService.

Compatibility:

  • API < 29: No-op

Parameters:

delegatedInfo: AccessibilityNodeInfoCompat.TouchDelegateInfoCompat

public void setQueryFromAppProcessEnabled(View view, boolean enabled)

Connects this node to the View's root so that operations on this node can query the entire AccessibilityNodeInfoCompat tree and perform accessibility actions on nodes.

Testing or debugging tools should create this AccessibilityNodeInfoCompat node using ViewCompat.onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat) or AccessibilityNodeProviderCompat and call this method, then navigate and interact with the node tree by calling methods on the node. Calling this method more than once on the same node is a no-op. After calling this method, all nodes linked to this node (children, ancestors, etc.) are also queryable.

Here "query" refers to the following node operations:

This is intended for short-lived inspections from testing or debugging tools in the app process, as operations on this node tree will only succeed as long as the associated view hierarchy remains attached to a window. AccessibilityNodeInfoCompat objects can quickly become out of sync with their corresponding View objects; if you wish to inspect a changed or different view hierarchy then create a new node from any view in that hierarchy and call this method on that new node, instead of disabling & re-enabling the connection on the previous node.

Compatibility:

  • API < 34: No-op

Parameters:

view: The view that generated this node, or any view in the same view-root hierarchy.
enabled: Whether to enable (true) or disable (false) querying from the app process.

public int hashCode()

public boolean equals(java.lang.Object obj)

public java.lang.String toString()

Source

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.core.view.accessibility;

import static android.view.View.NO_ID;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;

import static java.util.Collections.emptyList;

import android.accessibilityservice.AccessibilityService;
import android.annotation.SuppressLint;
import android.content.ClipData;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ClickableSpan;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo;

import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.R;
import androidx.core.accessibilityservice.AccessibilityServiceInfoCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.MoveAtGranularityArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.MoveHtmlArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.MoveWindowArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.ScrollToPositionArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.SetProgressArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.SetSelectionArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.SetTextArguments;

import java.lang.ref.WeakReference;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Helper for accessing {@link android.view.accessibility.AccessibilityNodeInfo} in a backwards
 * compatible fashion.
 */
public class AccessibilityNodeInfoCompat {

    /**
     * A class defining an action that can be performed on an {@link AccessibilityNodeInfo}.
     * Each action has a unique id and a label.
     * <p>
     * There are three categories of actions:
     * <ul>
     * <li><strong>Standard actions</strong> - These are actions that are reported and
     * handled by the standard UI widgets in the platform. Each standard action is associated with
     * a resource id, e.g. {@link android.R.id#accessibilityActionScrollUp}. Note that actions were
     * formerly associated with static constants defined in this class, e.g.
     * {@link #ACTION_FOCUS}. These actions will have {@code null} labels.
     * </li>
     * <li><strong>Custom actions action</strong> - These are actions that are reported
     * and handled by custom widgets. i.e. ones that are not part of the UI toolkit. For
     * example, an application may define a custom action for clearing the user history.
     * </li>
     * <li><strong>Overriden standard actions</strong> - These are actions that override
     * standard actions to customize them. For example, an app may add a label to the
     * standard {@link #ACTION_CLICK} action to indicate to the user that this action clears
     * browsing history.
     * </ul>
     * </p>
     * <p class="note">
     * <strong>Note:</strong> Views which support these actions should invoke
     * {@link ViewCompat#setImportantForAccessibility(View, int)} with
     * {@link ViewCompat#IMPORTANT_FOR_ACCESSIBILITY_YES} to ensure an
     * {@link android.accessibilityservice.AccessibilityService} can discover the set of supported
     * actions.
     * </p>
     */
    public static class AccessibilityActionCompat {

        private static final String TAG = "A11yActionCompat";

        /**
         * Action that gives input focus to the node.
         * <p>The focus request sends an event of {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
         * if successful. In the View system, this is handled by {@link View#requestFocus}.
         *
         * <p>The node that is focused should return {@code true} for
         * {@link AccessibilityNodeInfoCompat#isFocused()}.
         *
         * @see #ACTION_ACCESSIBILITY_FOCUS for the difference between system focus and
         * accessibility focus.
         */
        public static final AccessibilityActionCompat ACTION_FOCUS =
                new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_FOCUS, null);

        /**
         * Action that clears input focus of the node.
         * <p>The node that is cleared should return {@code false} for
         * {@link AccessibilityNodeInfoCompat#isFocused()}.
         */
        public static final AccessibilityActionCompat ACTION_CLEAR_FOCUS =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS, null);

        /**
         *  Action that selects the node.
         */
        public static final AccessibilityActionCompat ACTION_SELECT =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_SELECT, null);

        /**
         * Action that deselects the node.
         */
        public static final AccessibilityActionCompat ACTION_CLEAR_SELECTION =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION, null);

        /**
         * Action that clicks on the node info.
         *
         * <p>The UI element that implements this should send a
         * {@link AccessibilityEvent#TYPE_VIEW_CLICKED} event. In the View system,
         * the default handling of this action when performed by a service is to call
         * {@link View#performClick()}, and setting a
         * {@link View#setOnClickListener(View.OnClickListener)} automatically adds this action.
         *
         * <p>{@link #isClickable()} should return true if this action is available.
         */
        public static final AccessibilityActionCompat ACTION_CLICK =
                new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_CLICK, null);

        /**
         * Action that long clicks on the node.
         *
         * <p>The UI element that implements this should send a
         * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED} event. In the View system,
         * the default handling of this action when performed by a service is to call
         * {@link View#performLongClick()}, and setting a
         * {@link View#setOnLongClickListener(View.OnLongClickListener)} automatically adds this
         * action.
         *
         * <p>{@link #isLongClickable()} should return true if this action is available.
         */
        public static final AccessibilityActionCompat ACTION_LONG_CLICK =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_LONG_CLICK, null);

        /**
         * Action that gives accessibility focus to the node.
         * <p>The UI element that implements this should send a
         * {@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED} event
         * if successful. The node that is focused should return {@code true} for
         * {@link AccessibilityNodeInfoCompat#isAccessibilityFocused()}.
         *
         * <p>This is intended to be used by screen readers to assist with user navigation. Apps
         * changing focus can confuse screen readers, so the resulting behavior can vary by device
         * and screen reader version.
         * <p>This is distinct from {@link #ACTION_FOCUS}, which refers to system focus. System
         * focus is typically used to convey targets for keyboard navigation.
         */
        public static final AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null);

        /**
         * Action that clears accessibility focus of the node.
         * <p>The UI element that implements this should send a
         * {@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED} event if successful. The
         * node that is cleared should return {@code false} for
         * {@link AccessibilityNodeInfoCompat#isAccessibilityFocused()}.
         */
        public static final AccessibilityActionCompat ACTION_CLEAR_ACCESSIBILITY_FOCUS =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);

        /**
         * Action that requests to go to the next entity in this node's text
         * at a given movement granularity. For example, move to the next character,
         * word, etc.
         * <p>
         * <strong>Arguments:</strong>
         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
         * <strong>Example:</strong> Move to the previous character and do not extend selection.
         * <code><pre><p>
         *   Bundle arguments = new Bundle();
         *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
         *           AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER);
         *   arguments.putBoolean(
         *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
         *   info.performAction(
         *           AccessibilityActionCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY.getId(),
         *           arguments);
         * </code></pre></p>
         * </p>
         *
         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
         *
         * @see AccessibilityNodeInfoCompat#setMovementGranularities(int)
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
         * @see AccessibilityNodeInfoCompat#getMovementGranularities()
         *  AccessibilityNodeInfoCompat.getMovementGranularities()
         *
         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_CHARACTER
         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER
         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_WORD
         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_WORD
         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_LINE
         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_LINE
         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PARAGRAPH
         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PARAGRAPH
         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PAGE
         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE
         */
        public static final AccessibilityActionCompat ACTION_NEXT_AT_MOVEMENT_GRANULARITY =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, null,
                        MoveAtGranularityArguments.class);

        /**
         * Action that requests to go to the previous entity in this node's text
         * at a given movement granularity. For example, move to the next character,
         * word, etc.
         * <p>
         * <strong>Arguments:</strong>
         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
         * <strong>Example:</strong> Move to the next character and do not extend selection.
         * <code><pre><p>
         *   Bundle arguments = new Bundle();
         *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
         *           AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER);
         *   arguments.putBoolean(
         *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
         *   info.performAction(
         *           AccessibilityActionCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY.getId(),
         *           arguments);
         * </code></pre></p>
         * </p>
         *
         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
         *
         * @see AccessibilityNodeInfoCompat#setMovementGranularities(int)
         *   AccessibilityNodeInfoCompat.setMovementGranularities(int)
         * @see AccessibilityNodeInfoCompat#getMovementGranularities()
         *  AccessibilityNodeInfoCompat.getMovementGranularities()
         *
         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_CHARACTER
         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER
         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_WORD
         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_WORD
         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_LINE
         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_LINE
         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PARAGRAPH
         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PARAGRAPH
         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PAGE
         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE
         */
        public static final AccessibilityActionCompat ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, null,
                        MoveAtGranularityArguments.class);

        /**
         * Action to move to the next HTML element of a given type. For example, move
         * to the BUTTON, INPUT, TABLE, etc.
         * <p>
         * <strong>Arguments:</strong>
         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_HTML_ELEMENT_STRING
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
         * <strong>Example:</strong>
         * <code><pre><p>
         *   Bundle arguments = new Bundle();
         *   arguments.putString(
         *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
         *   info.performAction(
         *           AccessibilityActionCompat.ACTION_NEXT_HTML_ELEMENT.getId(), arguments);
         * </code></pre></p>
         * </p>
         */
        public static final AccessibilityActionCompat ACTION_NEXT_HTML_ELEMENT =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_NEXT_HTML_ELEMENT, null,
                        MoveHtmlArguments.class);

        /**
         * Action to move to the previous HTML element of a given type. For example, move
         * to the BUTTON, INPUT, TABLE, etc.
         * <p>
         * <strong>Arguments:</strong>
         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_HTML_ELEMENT_STRING
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
         * <strong>Example:</strong>
         * <code><pre><p>
         *   Bundle arguments = new Bundle();
         *   arguments.putString(
         *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
         *   info.performAction(
         *           AccessibilityActionCompat.ACTION_PREVIOUS_HTML_ELEMENT.getId(), arguments);
         * </code></pre></p>
         * </p>
         */
        public static final AccessibilityActionCompat ACTION_PREVIOUS_HTML_ELEMENT =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_PREVIOUS_HTML_ELEMENT, null,
                        MoveHtmlArguments.class);

        /**
         * Action to scroll the node content forward.
         */
        public static final AccessibilityActionCompat ACTION_SCROLL_FORWARD =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, null);

        /**
         * Action to scroll the node content backward.
         */
        public static final AccessibilityActionCompat ACTION_SCROLL_BACKWARD =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, null);

        /**
         * Action to copy the current selection to the clipboard.
         */
        public static final AccessibilityActionCompat ACTION_COPY =
                new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_COPY, null);

        /**
         * Action to paste the current clipboard content.
         */
        public static final AccessibilityActionCompat ACTION_PASTE =
                new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_PASTE, null);

        /**
         * Action to cut the current selection and place it to the clipboard.
         */
        public static final AccessibilityActionCompat ACTION_CUT =
                new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_CUT, null);

        /**
         * Action to set the selection. Performing this action with no arguments
         * clears the selection.
         * <p>
         * <strong>Arguments:</strong>
         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_START_INT
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT},
         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_END_INT
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT}<br>
         * <strong>Example:</strong>
         * <code><pre><p>
         *   Bundle arguments = new Bundle();
         *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT, 1);
         *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT, 2);
         *   info.performAction(AccessibilityActionCompat.ACTION_SET_SELECTION.getId(), arguments);
         * </code></pre></p>
         * </p>
         *
         * <p> If this is a text selection, the UI element that implements this should send a
         * {@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED} event if its selection is
         * updated. This element should also return {@code true} for
         * {@link AccessibilityNodeInfoCompat#isTextSelectable()}.
         *
         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_START_INT
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT
         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_END_INT
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT
         */
        public static final AccessibilityActionCompat ACTION_SET_SELECTION =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_SET_SELECTION, null,
                        SetSelectionArguments.class);

        /**
         * Action to expand an expandable node.
         */
        public static final AccessibilityActionCompat ACTION_EXPAND =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_EXPAND, null);

        /**
         * Action to collapse an expandable node.
         */
        public static final AccessibilityActionCompat ACTION_COLLAPSE =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_COLLAPSE, null);

        /**
         * Action to dismiss a dismissable node.
         */
        public static final AccessibilityActionCompat ACTION_DISMISS =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_DISMISS, null);

        /**
         * Action that sets the text of the node. Performing the action without argument,
         * using <code> null</code> or empty {@link CharSequence} will clear the text. This
         * action will also put the cursor at the end of text.
         * <p>
         * <strong>Arguments:</strong>
         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE
         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
         * <strong>Example:</strong>
         * <code><pre><p>
         *   Bundle arguments = new Bundle();
         *   arguments.putCharSequence(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
         *       "android");
         *  info.performAction(AccessibilityActionCompat.ACTION_SET_TEXT.getId(), arguments);
         * </code></pre></p>
         * <p>The UI element that implements this should send a
         * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED} event if its text is updated.
         * This element should also return {@code true} for
         * {@link AccessibilityNodeInfoCompat#isEditable()}.
         */
        public static final AccessibilityActionCompat ACTION_SET_TEXT =
                new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_SET_TEXT, null,
                        SetTextArguments.class);

        /**
         * Action that requests the node make its bounding rectangle visible
         * on the screen, scrolling if necessary just enough.
         *
         * @see View#requestRectangleOnScreen(Rect)
         */
        public static final AccessibilityActionCompat ACTION_SHOW_ON_SCREEN =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_ON_SCREEN : null,
                        android.R.id.accessibilityActionShowOnScreen, null, null, null);

        /**
         * Action that scrolls the node to make the specified collection
         * position visible on screen.
         * <p>
         * <strong>Arguments:</strong>
         * <ul>
         *     <li>{@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_ROW_INT}</li>
         *     <li>{@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_COLUMN_INT}</li>
         * <ul>
         *
         * @see AccessibilityNodeInfoCompat#getCollectionInfo()
         */
        public static final AccessibilityActionCompat ACTION_SCROLL_TO_POSITION =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_TO_POSITION
                        : null, android.R.id.accessibilityActionScrollToPosition, null, null,
                        ScrollToPositionArguments.class);

        /**
         * Action to scroll the node content up.
         */
        public static final AccessibilityActionCompat ACTION_SCROLL_UP =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP : null,
                        android.R.id.accessibilityActionScrollUp, null, null, null);
        /**
         * Action to scroll the node content left.
         */
        public static final AccessibilityActionCompat ACTION_SCROLL_LEFT =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_LEFT : null,
                        android.R.id.accessibilityActionScrollLeft, null, null, null);

        /**
         * Action to scroll the node content down.
         */
        public static final AccessibilityActionCompat ACTION_SCROLL_DOWN =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN : null,
                        android.R.id.accessibilityActionScrollDown, null, null, null);

        /**
         * Action to scroll the node content right.
         */
        public static final AccessibilityActionCompat ACTION_SCROLL_RIGHT =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_RIGHT : null,
                        android.R.id.accessibilityActionScrollRight, null, null, null);

        /**
         * Action to move to the page above.
         */
        @NonNull
        public static final AccessibilityActionCompat ACTION_PAGE_UP =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 29
                        ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_UP : null,
                        android.R.id.accessibilityActionPageUp, null, null, null);

        /**
         * Action to move to the page below.
         */
        @NonNull
        public static final AccessibilityActionCompat ACTION_PAGE_DOWN =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 29
                        ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_DOWN : null,
                        android.R.id.accessibilityActionPageDown, null, null, null);

        /**
         * Action to move to the page left.
         */
        @NonNull
        public static final AccessibilityActionCompat ACTION_PAGE_LEFT =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 29
                        ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT : null,
                        android.R.id.accessibilityActionPageLeft, null, null, null);

        /**
         * Action to move to the page right.
         */
        @NonNull
        public static final AccessibilityActionCompat ACTION_PAGE_RIGHT =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 29
                        ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT : null,
                        android.R.id.accessibilityActionPageRight, null, null, null);

        /**
         * Action that context clicks the node.
         *
         * <p>The UI element that implements this should send a
         * {@link AccessibilityEvent#TYPE_VIEW_CONTEXT_CLICKED} event. In the View system,
         * the default handling of this action when performed by a service is to call
         * {@link View#performContextClick()}, and setting a
         * {@link View#setOnContextClickListener(View.OnContextClickListener)} automatically adds
         * this action.
         *
         * <p>A context click usually occurs from a mouse pointer right-click or a stylus button
         * press.
         *
         * <p>{@link #isContextClickable()} should return true if this action is available.
         */
        public static final AccessibilityActionCompat ACTION_CONTEXT_CLICK =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_CONTEXT_CLICK : null,
                        android.R.id.accessibilityActionContextClick, null, null, null);

        /**
         * Action that sets progress between {@link  RangeInfoCompat#getMin() RangeInfo.getMin()} and
         * {@link  RangeInfoCompat#getMax() RangeInfo.getMax()}. It should use the same value type as
         * {@link RangeInfoCompat#getType() RangeInfo.getType()}
         * <p>
         * <strong>Arguments:</strong>
         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_PROGRESS_VALUE}
         *
         * @see RangeInfoCompat
         */
        public static final AccessibilityActionCompat ACTION_SET_PROGRESS =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 24
                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS : null,
                        android.R.id.accessibilityActionSetProgress, null, null,
                        SetProgressArguments.class);

        /**
         * Action to move a window to a new location.
         * <p>
         * <strong>Arguments:</strong>
         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVE_WINDOW_X}
         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVE_WINDOW_Y}
         */
        public static final AccessibilityActionCompat ACTION_MOVE_WINDOW =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 26
                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_MOVE_WINDOW : null,
                        android.R.id.accessibilityActionMoveWindow, null, null,
                        MoveWindowArguments.class);

        /**
         * Action to show a tooltip.
         */
        public static final AccessibilityActionCompat ACTION_SHOW_TOOLTIP =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 28
                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_TOOLTIP : null,
                        android.R.id.accessibilityActionShowTooltip, null, null, null);

        /**
         * Action to hide a tooltip. A node should expose this action only for views that are
         * currently showing a tooltip.
         */
        public static final AccessibilityActionCompat ACTION_HIDE_TOOLTIP =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 28
                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_HIDE_TOOLTIP : null,
                        android.R.id.accessibilityActionHideTooltip, null, null, null);

        /**
         * Action that presses and holds a node.
         * <p>
         * This action is for nodes that have distinct behavior that depends on how long a press is
         * held. Nodes having a single action for long press should use {@link #ACTION_LONG_CLICK}
         *  instead of this action, and nodes should not expose both actions.
         * <p>
         * When calling {@code performAction(ACTION_PRESS_AND_HOLD, bundle}, use
         * {@link #ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT} to specify how long the
         * node is pressed. The first time an accessibility service performs ACTION_PRES_AND_HOLD
         * on a node, it must specify 0 as ACTION_ARGUMENT_PRESS_AND_HOLD, so the application is
         * notified that the held state has started. To ensure reasonable behavior, the values
         * must be increased incrementally and may not exceed 10,000. UIs requested
         * to hold for times outside of this range should ignore the action.
         * <p>
         * The total time the element is held could be specified by an accessibility user up-front,
         * or may depend on what happens on the UI as the user continues to request the hold.
         * <p>
         *   <strong>Note:</strong> The time between dispatching the action and it arriving in the
         *     UI process is not guaranteed. It is possible on a busy system for the time to expire
         *     unexpectedly. For the case of holding down a key for a repeating action, a delayed
         *     arrival should be benign. Please do not use this sort of action in cases where such
         *     delays will lead to unexpected UI behavior.
         * <p>
         */
        @NonNull public static final AccessibilityActionCompat ACTION_PRESS_AND_HOLD =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 30
                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_PRESS_AND_HOLD : null,
                        android.R.id.accessibilityActionPressAndHold, null, null, null);

        /**
         * Action to send an ime actionId which is from
         * {@link android.view.inputmethod.EditorInfo#actionId}. This ime actionId sets by
         * {@link android.widget.TextView#setImeActionLabel(CharSequence, int)}, or it would be
         * {@link android.view.inputmethod.EditorInfo#IME_ACTION_UNSPECIFIED} if no specific
         * actionId has set. A node should expose this action only for views that are currently
         * with input focus and editable.
         */
        @NonNull public static final AccessibilityActionCompat ACTION_IME_ENTER =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 30
                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_IME_ENTER : null,
                        android.R.id.accessibilityActionImeEnter, null, null, null);

        /**
         * Action to start a drag.
         * <p>
         * This action initiates a drag & drop within the system. The source's dragged content is
         * prepared before the drag begins. In View, this action should prepare the arguments to
         * {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)}} and then
         * call the method. The equivalent should be performed for other UI toolkits.
         * </p>
         *
         * @see AccessibilityEventCompat#CONTENT_CHANGE_TYPE_DRAG_STARTED
         */
        @NonNull
        public static final AccessibilityActionCompat ACTION_DRAG_START =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 32
                        ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_DRAG_START : null,
                        android.R.id.accessibilityActionDragStart, null, null, null);

        /**
         * Action to trigger a drop of the content being dragged.
         * <p>
         * This action is added to potential drop targets if the source started a drag with
         * {@link #ACTION_DRAG_START}. In View, these targets are Views that accepted
         * {@link android.view.DragEvent#ACTION_DRAG_STARTED} and have an
         * {@link View.OnDragListener}.
         * </p>
         *
         * @see AccessibilityEventCompat#CONTENT_CHANGE_TYPE_DRAG_DROPPED
         */
        @NonNull
        public static final AccessibilityActionCompat ACTION_DRAG_DROP =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 32
                        ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_DRAG_DROP : null,
                        android.R.id.accessibilityActionDragDrop, null, null, null);

        /**
         * Action to cancel a drag.
         * <p>
         * This action is added to the source that started a drag with {@link #ACTION_DRAG_START}.
         * </p>
         *
         * @see AccessibilityEventCompat#CONTENT_CHANGE_TYPE_DRAG_CANCELLED
         */
        @NonNull
        public static final AccessibilityActionCompat ACTION_DRAG_CANCEL =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 32
                        ?  AccessibilityNodeInfo.AccessibilityAction.ACTION_DRAG_CANCEL : null,
                        android.R.id.accessibilityActionDragCancel, null, null, null);

        /**
         * Action to show suggestions for editable text.
         */
        @NonNull
        public static final AccessibilityActionCompat ACTION_SHOW_TEXT_SUGGESTIONS =
                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 33
                        ?   AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_TEXT_SUGGESTIONS
                        :   null, android.R.id.accessibilityActionShowTextSuggestions, null,
                        null, null);

        /**
         * Action that brings fully on screen the next node in the specified direction.
         *
         * <p>
         *     This should include wrapping around to the next/previous row, column, etc. in a
         *     collection if one is available. If there is no node in that direction, the action
         *     should fail and return false.
         * </p>
         * <p>
         *     This action should be used instead of
         *     {@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION} when a widget does not
         *     have clear row and column semantics or if a directional search is needed to find a
         *     node in a complex ViewGroup where individual nodes may span multiple rows or
         *     columns. The implementing widget must send a
         *     {@link AccessibilityEventCompat#TYPE_VIEW_TARGETED_BY_SCROLL} accessibility event
         *     with the scroll target as the source.  An accessibility service can listen for this
         *     event, inspect its source, and use the result when determining where to place
         *     accessibility focus.
         * <p>
         *     <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_DIRECTION_INT}. This is a
         *     required argument.<br>
         * </p>
         */
        @NonNull
        @OptIn(markerClass = androidx.core.os.BuildCompat.PrereleaseSdkCheck.class)
        public static final AccessibilityActionCompat ACTION_SCROLL_IN_DIRECTION =
                new AccessibilityActionCompat(
                        Build.VERSION.SDK_INT >= 34 ? Api34Impl.getActionScrollInDirection() : null,
                        android.R.id.accessibilityActionScrollInDirection, null, null, null);

        final Object mAction;
        private final int mId;
        private final Class<? extends CommandArguments> mViewCommandArgumentClass;

        /**
         */
        @RestrictTo(LIBRARY_GROUP_PREFIX)
        protected final AccessibilityViewCommand mCommand;

        /**
         * Creates a new instance.
         *
         * @param actionId The action id.
         * @param label The action label.
         */
        public AccessibilityActionCompat(int actionId, CharSequence label) {
            this(null, actionId, label, null, null);
        }

        /**
         * Creates a new instance.
         *
         * @param actionId The action id.
         * @param label The action label.
         * @param command The command performed when the service requests the action
         */
        @RestrictTo(LIBRARY_GROUP_PREFIX)
        public AccessibilityActionCompat(int actionId, CharSequence label,
                AccessibilityViewCommand command) {
            this(null, actionId, label, command, null);
        }

        AccessibilityActionCompat(Object action) {
            this(action, 0, null, null, null);
        }

        private AccessibilityActionCompat(int actionId, CharSequence label,
                Class<? extends CommandArguments> viewCommandArgumentClass) {
            this(null, actionId, label, null, viewCommandArgumentClass);
        }

        AccessibilityActionCompat(Object action, int id, CharSequence label,
                AccessibilityViewCommand command,
                Class<? extends CommandArguments> viewCommandArgumentClass) {
            mId = id;
            mCommand = command;
            if (Build.VERSION.SDK_INT >= 21 && action == null) {
                mAction = new AccessibilityNodeInfo.AccessibilityAction(id, label);
            } else {
                mAction = action;
            }
            mViewCommandArgumentClass = viewCommandArgumentClass;
        }

        /**
         * Gets the id for this action.
         *
         * @return The action id.
         */
        public int getId() {
            if (Build.VERSION.SDK_INT >= 21) {
                return ((AccessibilityNodeInfo.AccessibilityAction) mAction).getId();
            } else {
                return 0;
            }
        }

        /**
         * Gets the label for this action. Its purpose is to describe the
         * action to user.
         *
         * @return The label.
         */
        public CharSequence getLabel() {
            if (Build.VERSION.SDK_INT >= 21) {
                return ((AccessibilityNodeInfo.AccessibilityAction) mAction).getLabel();
            } else {
                return null;
            }
        }

        /**
         * Performs the action.
         * @return If the action was handled.
         * @param view View to act upon.
         * @param arguments Optional action arguments.
         */
        @RestrictTo(LIBRARY_GROUP_PREFIX)
        public boolean perform(View view, Bundle arguments) {
            if (mCommand != null) {
                CommandArguments viewCommandArgument = null;
                if (mViewCommandArgumentClass != null) {
                    try {
                        viewCommandArgument =
                                mViewCommandArgumentClass.getDeclaredConstructor().newInstance();
                        viewCommandArgument.setBundle(arguments);
                    } catch (Exception e) {
                        final String className = mViewCommandArgumentClass == null
                                ? "null" : mViewCommandArgumentClass.getName();
                        Log.e(TAG, "Failed to execute command with argument class "
                                + "ViewCommandArgument: " + className, e);
                    }
                }
                return mCommand.perform(view, viewCommandArgument);
            }
            return false;
        }

        /**
         */
        @RestrictTo(LIBRARY_GROUP_PREFIX)
        public AccessibilityActionCompat createReplacementAction(CharSequence label,
                AccessibilityViewCommand command) {
            return new AccessibilityActionCompat(null, mId, label, command,
                    mViewCommandArgumentClass);
        }

        @Override
        public int hashCode() {
            return mAction != null ? mAction.hashCode() : 0;
        }

        @Override
        public boolean equals(@Nullable Object obj) {
            if (obj == null) {
                return false;
            }
            if (!(obj instanceof AccessibilityNodeInfoCompat.AccessibilityActionCompat)) {
                return false;
            }
            AccessibilityNodeInfoCompat.AccessibilityActionCompat other =
                    (AccessibilityNodeInfoCompat.AccessibilityActionCompat) obj;
            if (mAction == null) {
                if (other.mAction != null) {
                    return false;
                }
            } else if (!mAction.equals(other.mAction)) {
                return false;
            }
            return true;
        }

        @NonNull
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("AccessibilityActionCompat: ");
            // Mirror AccessibilityNodeInfoCompat.toString's action string.
            String actionName = getActionSymbolicName(mId);
            if (actionName.equals("ACTION_UNKNOWN") && getLabel() != null) {
                actionName = getLabel().toString();
            }
            builder.append(actionName);
            return builder.toString();
        }
    }

    /**
     * Class with information if a node is a collection.
     * <p>
     * A collection of items has rows and columns and may be marked as hierarchical.
     *
     * <p>
     * For example, a list where the items are placed in a vertical layout is a collection with one
     * column and as many rows as the list items. This collection has 3 rows and 1 column and should
     * not be marked as hierarchical since items do not exist at different levels/ranks and there
     * are no nested collections.
     * <ul>
     *     <li>Item 1</li>
     *     <li>Item 2</li>
     *     <li>Item 3</li>
     * </ul>
     *
     * <p>
     * A table is a collection with several rows and several columns. This collection has 2 rows and
     * 3 columns and is not marked as hierarchical:
     *<table>
     *   <tr>
     *     <td>Item 1</td>
     *     <td>Item 2</td>
     *     <td>Item 3</td>
     *   </tr>
     *   <tr>
     *     <td>Item 4</td>
     *     <td>Item 5</td>
     *     <td>Item 6</td>
     *   </tr>
     * </table>
     *
     * <p>
     * Nested collections could be marked as hierarchical. To add outer and inner collections to the
     * same hierarchy, mark them both as hierarchical.
     *
     * <p> For example, if you have a collection with two lists - this collection has an outer
     * list with 3 rows and 1 column and an inner list within "Item 2" with 2 rows and 1 -
     * you can mark both the outer list and the inner list as hierarchical to make them part of
     * the same hierarchy. If a collection does not have any ancestor or descendant hierarchical
     * collections, it does not need to be marked as hierarchical.
     *  <ul>
     *      <li>Item 1</li>
     *      <li> Item 2
     *          <ul>
     *              <li>Item 2A</li>
     *              <li>Item 2B</li>
     *          </ul>
     *      </li>
     *      <li>Item 3</li>
     *  </ul>
     *
     * <p>
     * To be a valid list, a collection has 1 row and any number of columns or 1 column and any
     * number of rows.
     * </p>
     */
    public static class CollectionInfoCompat {
        /** Selection mode where items are not selectable. */
        public static final int SELECTION_MODE_NONE = 0;

        /** Selection mode where a single item may be selected. */
        public static final int SELECTION_MODE_SINGLE = 1;

        /** Selection mode where multiple items may be selected. */
        public static final int SELECTION_MODE_MULTIPLE = 2;

        final Object mInfo;

        /**
         * Returns a cached instance if such is available otherwise a new one.
         *
         * @param rowCount The number of rows.
         * @param columnCount The number of columns.
         * @param hierarchical Whether the collection is hierarchical.
         * @param selectionMode The collection's selection mode, one of:
         *            <ul>
         *            <li>{@link #SELECTION_MODE_NONE}
         *            <li>{@link #SELECTION_MODE_SINGLE}
         *            <li>{@link #SELECTION_MODE_MULTIPLE}
         *            </ul>
         *
         * @return An instance.
         */
        public static CollectionInfoCompat obtain(int rowCount, int columnCount,
                boolean hierarchical, int selectionMode) {
            if (Build.VERSION.SDK_INT >= 21) {
                return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain(
                        rowCount, columnCount, hierarchical, selectionMode));
            } else {
                return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain(
                        rowCount, columnCount, hierarchical));
            }
        }

        /**
         * Returns a cached instance if such is available otherwise a new one.
         *
         * @param rowCount The number of rows, or -1 if count is unknown.
         * @param columnCount The number of columns , or -1 if count is unknown.
         * @param hierarchical Whether the collection is hierarchical.
         *
         * @return An instance.
         */
        public static CollectionInfoCompat obtain(int rowCount, int columnCount,
                boolean hierarchical) {
            return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain(
                    rowCount, columnCount, hierarchical));
        }

        CollectionInfoCompat(Object info) {
            mInfo = info;
        }

        /**
         * Gets the number of columns.
         *
         * @return The column count, or -1 if count is unknown.
         */
        public int getColumnCount() {
            return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getColumnCount();
        }

        /**
         * Gets the number of rows.
         *
         * @return The row count, or -1 if count is unknown.
         */
        public int getRowCount() {
            return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getRowCount();
        }

        /**
         * Gets if the collection is a hierarchically ordered.
         *
         * @return Whether the collection is hierarchical.
         */
        public boolean isHierarchical() {
            return ((AccessibilityNodeInfo.CollectionInfo) mInfo).isHierarchical();
        }

        /**
         * Gets the collection's selection mode.
         *
         * @return The collection's selection mode, one of:
         *         <ul>
         *         <li>{@link #SELECTION_MODE_NONE}
         *         <li>{@link #SELECTION_MODE_SINGLE}
         *         <li>{@link #SELECTION_MODE_MULTIPLE}
         *         </ul>
         */
        public int getSelectionMode() {
            if (Build.VERSION.SDK_INT >= 21) {
                return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getSelectionMode();
            } else {
                return 0;
            }
        }
    }

    /**
     * Class with information if a node is a collection item.
     * <p>
     * A collection item is contained in a collection, it starts at
     * a given row and column in the collection, and spans one or
     * more rows and columns. For example, a header of two related
     * table columns starts at the first row and the first column,
     * spans one row and two columns.
     * </p>
     */
    public static class CollectionItemInfoCompat {

        final Object mInfo;

        /**
         * Returns a cached instance if such is available otherwise a new one.
         *
         * @param rowIndex The row index at which the item is located.
         * @param rowSpan The number of rows the item spans.
         * @param columnIndex The column index at which the item is located.
         * @param columnSpan The number of columns the item spans.
         * @param heading Whether the item is a heading. This should be set to false and the newer
         *                {@link AccessibilityNodeInfoCompat#setHeading(boolean)} used to identify
         *                headings.
         * @param selected Whether the item is selected.
         * @return An instance.
         */
        public static CollectionItemInfoCompat obtain(int rowIndex, int rowSpan,
                int columnIndex, int columnSpan, boolean heading, boolean selected) {
            if (Build.VERSION.SDK_INT >= 21) {
                return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain(
                        rowIndex, rowSpan, columnIndex, columnSpan, heading, selected));
            } else {
                return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain(
                        rowIndex, rowSpan, columnIndex, columnSpan, heading));
            }
        }

        /**
         * Returns a cached instance if such is available otherwise a new one.
         *
         * @param rowIndex The row index at which the item is located.
         * @param rowSpan The number of rows the item spans.
         * @param columnIndex The column index at which the item is located.
         * @param columnSpan The number of columns the item spans.
         * @param heading Whether the item is a heading. This should be set to false and the newer
         *                {@link AccessibilityNodeInfoCompat#setHeading(boolean)} used to identify
         *                headings.
         * @return An instance.
         */
        public static CollectionItemInfoCompat obtain(int rowIndex, int rowSpan,
                int columnIndex, int columnSpan, boolean heading) {
            return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain(
                    rowIndex, rowSpan, columnIndex, columnSpan, heading));
        }

        CollectionItemInfoCompat(Object info) {
            mInfo = info;
        }

        /**
         * Gets the column index at which the item is located.
         *
         * @return The column index.
         */
        public int getColumnIndex() {
            return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getColumnIndex();
        }

        /**
         * Gets the number of columns the item spans.
         *
         * @return The column span.
         */
        public int getColumnSpan() {
            return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getColumnSpan();
        }

        /**
         * Gets the row index at which the item is located.
         *
         * @return The row index.
         */
        public int getRowIndex() {
            return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getRowIndex();
        }

        /**
         * Gets the number of rows the item spans.
         *
         * @return The row span.
         */
        public int getRowSpan() {
            return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getRowSpan();
        }

        /**
         * Gets if the collection item is a heading. For example, section
         * heading, table header, etc.
         *
         * @return If the item is a heading.
         * @deprecated Use {@link AccessibilityNodeInfoCompat#isHeading()}
         */
        @SuppressWarnings("deprecation")
        @Deprecated
        public boolean isHeading() {
            return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).isHeading();
        }

        /**
         * Gets if the collection item is selected.
         *
         * @return If the item is selected.
         */
        public boolean isSelected() {
            if (Build.VERSION.SDK_INT >= 21) {
                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).isSelected();
            } else {
                return false;
            }
        }

        /**
         * Gets the row title at which the item is located.
         *
         * @return The row title.
         */
        @Nullable
        public String getRowTitle() {
            if (Build.VERSION.SDK_INT >= 33) {
                return Api33Impl.getCollectionItemRowTitle(mInfo);
            } else {
                return null;
            }
        }

        /**
         * Gets the column title at which the item is located.
         *
         * @return The column title.
         */
        @Nullable
        public String getColumnTitle() {
            if (Build.VERSION.SDK_INT >= 33) {
                return Api33Impl.getCollectionItemColumnTitle(mInfo);
            } else {
                return null;
            }
        }

        /**
         * Builder for creating {@link CollectionItemInfoCompat} objects.
         */
        public static final class Builder {
            private boolean mHeading;
            private int mColumnIndex;
            private int mRowIndex;
            private int mColumnSpan;
            private int mRowSpan;
            private boolean mSelected;
            private String mRowTitle;
            private String mColumnTitle;

            /**
             * Creates a new Builder.
             */
            public Builder() {
            }

            /**
             * Sets the collection item is a heading.
             *
             * @param heading The heading state
             * @return This builder
             */
            @NonNull
            public Builder setHeading(boolean heading) {
                mHeading = heading;
                return this;
            }

            /**
             * Sets the column index at which the item is located.
             *
             * @param columnIndex The column index
             * @return This builder
             */
            @NonNull
            public Builder setColumnIndex(int columnIndex) {
                mColumnIndex = columnIndex;
                return this;
            }

            /**
             * Sets the row index at which the item is located.
             *
             * @param rowIndex The row index
             * @return This builder
             */
            @NonNull
            public Builder setRowIndex(int rowIndex) {
                mRowIndex = rowIndex;
                return this;
            }

            /**
             * Sets the number of columns the item spans.
             *
             * @param columnSpan The number of columns spans
             * @return This builder
             */
            @NonNull
            public Builder setColumnSpan(int columnSpan) {
                mColumnSpan = columnSpan;
                return this;
            }

            /**
             * Sets the number of rows the item spans.
             *
             * @param rowSpan The number of rows spans
             * @return This builder
             */
            @NonNull
            public Builder setRowSpan(int rowSpan) {
                mRowSpan = rowSpan;
                return this;
            }

            /**
             * Sets the collection item is selected.
             *
             * @param selected The number of rows spans
             * @return This builder
             */
            @NonNull
            public Builder setSelected(boolean selected) {
                mSelected = selected;
                return this;
            }

            /**
             * Sets the row title at which the item is located.
             *
             * @param rowTitle The row title
             * @return This builder
             */
            @NonNull
            public Builder setRowTitle(@Nullable String rowTitle) {
                mRowTitle = rowTitle;
                return this;
            }

            /**
             * Sets the column title at which the item is located.
             *
             * @param columnTitle The column title
             * @return This builder
             */
            @NonNull
            public Builder setColumnTitle(@Nullable String columnTitle) {
                mColumnTitle = columnTitle;
                return this;
            }

            /**
             * Builds and returns a {@link AccessibilityNodeInfo.CollectionItemInfo}.
             */
            @NonNull
            public CollectionItemInfoCompat build() {
                if (Build.VERSION.SDK_INT >= 33) {
                    return Api33Impl.buildCollectionItemInfoCompat(mHeading, mColumnIndex,
                            mRowIndex, mColumnSpan, mRowSpan, mSelected, mRowTitle, mColumnTitle);
                } else if (Build.VERSION.SDK_INT >= 21) {
                    return Api21Impl.createCollectionItemInfo(mRowIndex, mRowSpan, mColumnIndex,
                            mColumnSpan, mHeading, mSelected);
                } else {
                    return new CollectionItemInfoCompat(
                            AccessibilityNodeInfo.CollectionItemInfo.obtain(mRowIndex, mRowSpan,
                                    mColumnIndex,
                                    mColumnSpan, mHeading));
                }
            }
        }
    }

    /**
     * Class with information if a node is a range.
     */
    public static class RangeInfoCompat {
        /** Range type: integer. */
        public static final int RANGE_TYPE_INT = 0;
        /** Range type: float. */
        public static final int RANGE_TYPE_FLOAT = 1;
        /** Range type: percent with values from zero to one.*/
        public static final int RANGE_TYPE_PERCENT = 2;

        /**
         * Obtains a cached instance if such is available otherwise a new one.
         *
         * @param type The type of the range.
         * @param min The min value.
         * @param max The max value.
         * @param current The current value.
         * @return The instance
         */
        public static RangeInfoCompat obtain(int type, float min, float max, float current) {
            return new RangeInfoCompat(
                    AccessibilityNodeInfo.RangeInfo.obtain(type, min, max, current));
        }

        final Object mInfo;

        RangeInfoCompat(Object info) {
            mInfo = info;
        }

        /**
         * Creates a new range.
         *
         * @param type The type of the range.
         * @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no
         *            minimum.
         * @param max The maximum value. Use {@code Float.POSITIVE_INFINITY} if the range has no
         *            maximum.
         * @param current The current value.
         */
        public RangeInfoCompat(int type, float min, float max, float current) {
            if (Build.VERSION.SDK_INT >= 30) {
                mInfo = Api30Impl.createRangeInfo(type, min, max, current);
            } else {
                mInfo = AccessibilityNodeInfo.RangeInfo.obtain(type, min, max, current);
            }
        }

        /**
         * Gets the current value.
         *
         * @return The current value.
         */
        public float getCurrent() {
            return ((AccessibilityNodeInfo.RangeInfo) mInfo).getCurrent();
        }

        /**
         * Gets the max value.
         *
         * @return The max value.
         */
        public float getMax() {
            return ((AccessibilityNodeInfo.RangeInfo) mInfo).getMax();
        }

        /**
         * Gets the min value.
         *
         * @return The min value.
         */
        public float getMin() {
            return ((AccessibilityNodeInfo.RangeInfo) mInfo).getMin();
        }

        /**
         * Gets the range type.
         *
         * @return The range type.
         *
         * @see #RANGE_TYPE_INT
         * @see #RANGE_TYPE_FLOAT
         * @see #RANGE_TYPE_PERCENT
         */
        public int getType() {
            return ((AccessibilityNodeInfo.RangeInfo) mInfo).getType();
        }
    }

    /**
     * Class with information of touch delegated views and regions.
     */
    public static final class TouchDelegateInfoCompat {
        final TouchDelegateInfo mInfo;

        /**
         * Create a new instance of {@link TouchDelegateInfoCompat}.
         *
         * @param targetMap A map from regions (in view coordinates) to delegated views.
         */
        public TouchDelegateInfoCompat(@NonNull Map<Region, View> targetMap) {
            if (Build.VERSION.SDK_INT >= 29) {
                mInfo = new TouchDelegateInfo(targetMap);
            } else {
                mInfo = null;
            }
        }

        TouchDelegateInfoCompat(@NonNull TouchDelegateInfo info) {
            mInfo = info;
        }

        /**
         * Returns the number of touch delegate target region.
         * <p>
         * Compatibility:
         * <ul>
         *     <li>API &lt; 29: Always returns {@code 0}</li>
         * </ul>
         *
         * @return Number of touch delegate target region.
         */
        public @IntRange(from = 0) int getRegionCount() {
            if (Build.VERSION.SDK_INT >= 29) {
                return mInfo.getRegionCount();
            }
            return 0;
        }

        /**
         * Return the {@link Region} at the given index.
         * <p>
         * Compatibility:
         * <ul>
         *     <li>API &lt; 29: Always returns {@code null}</li>
         * </ul>
         *
         * @param index The desired index, must be between 0 and {@link #getRegionCount()}-1.
         * @return Returns the {@link Region} stored at the given index.
         */
        @Nullable
        public Region getRegionAt(@IntRange(from = 0) int index) {
            if (Build.VERSION.SDK_INT >= 29) {
                return mInfo.getRegionAt(index);
            }
            return null;
        }

        /**
         * Return the target {@link AccessibilityNodeInfoCompat} for the given {@link Region}.
         * <p>
         *   <strong>Note:</strong> This api can only be called from
         *   {@link android.accessibilityservice.AccessibilityService}.
         * </p>
         * <p>
         * Compatibility:
         * <ul>
         *     <li>API &lt; 29: Always returns {@code null}</li>
         * </ul>
         *
         * @param region The region retrieved from {@link #getRegionAt(int)}.
         * @return The target node associates with the given region.
         */
        @Nullable
        public AccessibilityNodeInfoCompat getTargetForRegion(@NonNull Region region) {
            if (Build.VERSION.SDK_INT >= 29) {
                AccessibilityNodeInfo info = mInfo.getTargetForRegion(region);
                if (info != null) {
                    return AccessibilityNodeInfoCompat.wrap(info);
                }
            }
            return null;
        }
    }

    private static final String ROLE_DESCRIPTION_KEY =
            "AccessibilityNodeInfo.roleDescription";

    private static final String PANE_TITLE_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat.PANE_TITLE_KEY";

    private static final String TOOLTIP_TEXT_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat.TOOLTIP_TEXT_KEY";

    private static final String HINT_TEXT_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat.HINT_TEXT_KEY";

    private static final String BOOLEAN_PROPERTY_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat.BOOLEAN_PROPERTY_KEY";

    private static final String SPANS_ID_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat.SPANS_ID_KEY";

    private static final String SPANS_START_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat.SPANS_START_KEY";

    private static final String SPANS_END_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat.SPANS_END_KEY";

    private static final String SPANS_FLAGS_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat.SPANS_FLAGS_KEY";

    private static final String SPANS_ACTION_ID_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat.SPANS_ACTION_ID_KEY";

    private static final String STATE_DESCRIPTION_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat.STATE_DESCRIPTION_KEY";

    private static final String UNIQUE_ID_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat.UNIQUE_ID_KEY";

    private static final String CONTAINER_TITLE_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat.CONTAINER_TITLE_KEY";

    private static final String BOUNDS_IN_WINDOW_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat.BOUNDS_IN_WINDOW_KEY";

    private static final String MIN_DURATION_BETWEEN_CONTENT_CHANGES_KEY =
            "androidx.view.accessibility.AccessibilityNodeInfoCompat."
                    + "MIN_DURATION_BETWEEN_CONTENT_CHANGES_KEY";

    // These don't line up with the internal framework constants, since they are independent
    // and we might as well get all 32 bits of utility here.
    private static final int BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE = 0x00000001;
    private static final int BOOLEAN_PROPERTY_IS_HEADING = 0x00000002;
    private static final int BOOLEAN_PROPERTY_IS_SHOWING_HINT = 0x00000004;
    private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x00000008;

    private static final int BOOLEAN_PROPERTY_HAS_REQUEST_INITIAL_ACCESSIBILITY_FOCUS = 1 << 5;
    private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_DATA_SENSITIVE = 1 << 6;
    private static final int BOOLEAN_PROPERTY_TEXT_SELECTABLE = 1 << 23;
    private static final int BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING = 1 << 26;

    private final AccessibilityNodeInfo mInfo;

    /**
     *  androidx.customview.widget.ExploreByTouchHelper.HOST_ID = -1;
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public int mParentVirtualDescendantId = NO_ID;

    private int mVirtualDescendantId = NO_ID;

    // Actions introduced in IceCreamSandwich

    /**
     * Action that focuses the node.
     * @see AccessibilityActionCompat#ACTION_FOCUS
     */
    public static final int ACTION_FOCUS = 0x00000001;

    /**
     * Action that unfocuses the node.
     * @see AccessibilityActionCompat#ACTION_CLEAR_FOCUS
     */
    public static final int ACTION_CLEAR_FOCUS = 0x00000002;

    /**
     * Action that selects the node.
     * @see AccessibilityActionCompat#ACTION_SELECT
     */
    public static final int ACTION_SELECT = 0x00000004;

    /**
     * Action that unselects the node.
     * @see AccessibilityActionCompat#ACTION_CLEAR_SELECTION
     */
    public static final int ACTION_CLEAR_SELECTION = 0x00000008;

    /**
     * Action that clicks on the node info.
     * @see AccessibilityActionCompat#ACTION_CLICK
     */
    public static final int ACTION_CLICK = 0x00000010;

    /**
     * Action that long clicks on the node.
     * @see AccessibilityActionCompat#ACTION_LONG_CLICK
     */
    public static final int ACTION_LONG_CLICK = 0x00000020;

    // Actions introduced in JellyBean

    /**
     * Action that gives accessibility focus to the node.
     * @see AccessibilityActionCompat#ACTION_ACCESSIBILITY_FOCUS
     */
    public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040;

    /**
     * Action that clears accessibility focus of the node.
     */
    public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080;

    /**
     * Action that requests to go to the next entity in this node's text
     * at a given movement granularity. For example, move to the next character,
     * word, etc.
     * <p>
     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
     * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
     * <strong>Example:</strong> Move to the previous character and do not extend selection.
     * <code><pre><p>
     *   Bundle arguments = new Bundle();
     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
     *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
     *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
     *           false);
     *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
     * </code></pre></p>
     * </p>
     *
     * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
     * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
     *
     * @see #setMovementGranularities(int)
     * @see #getMovementGranularities()
     *
     * @see #MOVEMENT_GRANULARITY_CHARACTER
     * @see #MOVEMENT_GRANULARITY_WORD
     * @see #MOVEMENT_GRANULARITY_LINE
     * @see #MOVEMENT_GRANULARITY_PARAGRAPH
     * @see #MOVEMENT_GRANULARITY_PAGE
     * @see AccessibilityActionCompat#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
     */
    public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100;

    /**
     * Action that requests to go to the previous entity in this node's text
     * at a given movement granularity. For example, move to the next character,
     * word, etc.
     * <p>
     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
     * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
     * <strong>Example:</strong> Move to the next character and do not extend selection.
     * <code><pre><p>
     *   Bundle arguments = new Bundle();
     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
     *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
     *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
     *           false);
     *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
     *           arguments);
     * </code></pre></p>
     * </p>
     *
     * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
     * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
     *
     * @see #setMovementGranularities(int)
     * @see #getMovementGranularities()
     *
     * @see #MOVEMENT_GRANULARITY_CHARACTER
     * @see #MOVEMENT_GRANULARITY_WORD
     * @see #MOVEMENT_GRANULARITY_LINE
     * @see #MOVEMENT_GRANULARITY_PARAGRAPH
     * @see #MOVEMENT_GRANULARITY_PAGE
     * @see AccessibilityActionCompat#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
     */
    public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200;

    /**
     * Action to move to the next HTML element of a given type. For example, move
     * to the BUTTON, INPUT, TABLE, etc.
     * <p>
     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
     * <strong>Example:</strong>
     * <code><pre><p>
     *   Bundle arguments = new Bundle();
     *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
     *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
     * </code></pre></p>
     * </p>
     * @see AccessibilityActionCompat#ACTION_NEXT_HTML_ELEMENT
     */
    public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;

    /**
     * Action to move to the previous HTML element of a given type. For example, move
     * to the BUTTON, INPUT, TABLE, etc.
     * <p>
     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
     * <strong>Example:</strong>
     * <code><pre><p>
     *   Bundle arguments = new Bundle();
     *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
     *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
     * </code></pre></p>
     * </p>
     *
     * @see AccessibilityActionCompat#ACTION_PREVIOUS_HTML_ELEMENT
     */
    public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;

    /**
     * Action to scroll the node content forward.
     * @see AccessibilityActionCompat#ACTION_SCROLL_FORWARD
     */
    public static final int ACTION_SCROLL_FORWARD = 0x00001000;

    /**
     * Action to scroll the node content backward.
     * @see AccessibilityActionCompat#ACTION_SCROLL_BACKWARD
     */
    public static final int ACTION_SCROLL_BACKWARD = 0x00002000;

    // Actions introduced in JellyBeanMr2

    /**
     * Action to copy the current selection to the clipboard.
     * @see AccessibilityActionCompat#ACTION_COPY
     */
    public static final int ACTION_COPY = 0x00004000;

    /**
     * Action to paste the current clipboard content.
     * @see AccessibilityActionCompat#ACTION_PASTE
     */
    public static final int ACTION_PASTE = 0x00008000;

    /**
     * Action to cut the current selection and place it to the clipboard.
     * @see AccessibilityActionCompat#ACTION_CUT
     */
    public static final int ACTION_CUT = 0x00010000;

    /**
     * Action to set the selection. Performing this action with no arguments
     * clears the selection.
     * <p>
     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SELECTION_START_INT},
     * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br>
     * <strong>Example:</strong>
     * <code><pre><p>
     *   Bundle arguments = new Bundle();
     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1);
     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2);
     *   info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments);
     * </code></pre></p>
     * </p>
     *
     * @see #ACTION_ARGUMENT_SELECTION_START_INT
     * @see #ACTION_ARGUMENT_SELECTION_END_INT
     * @see AccessibilityActionCompat#ACTION_SET_SELECTION
     */
    public static final int ACTION_SET_SELECTION = 0x00020000;

    /**
     * Action to expand an expandable node.
     * @see AccessibilityActionCompat#ACTION_EXPAND
     */
    public static final int ACTION_EXPAND = 0x00040000;

    /**
     * Action to collapse an expandable node.
     * @see AccessibilityActionCompat#ACTION_COLLAPSE
     */
    public static final int ACTION_COLLAPSE = 0x00080000;

    /**
     * Action to dismiss a dismissible node.
     * @see AccessibilityActionCompat#ACTION_DISMISS
     */
    public static final int ACTION_DISMISS = 0x00100000;

    /**
     * Action that sets the text of the node. Performing the action without argument, using <code>
     * null</code> or empty {@link CharSequence} will clear the text. This action will also put the
     * cursor at the end of text.
     * <p>
     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
     * <strong>Example:</strong>
     * <code><pre><p>
     *   Bundle arguments = new Bundle();
     *   arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
     *       "android");
     *   info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
     * </code></pre></p>
     * @see AccessibilityActionCompat#ACTION_SET_TEXT
     */
    public static final int ACTION_SET_TEXT = 0x00200000;

    // Action arguments

    /**
     * Argument for which movement granularity to be used when traversing the node text.
     * <p>
     * <strong>Type:</strong> int<br>
     * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
     * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
     * </p>
     */
    public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
        "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";

    /**
     * Argument for which HTML element to get moving to the next/previous HTML element.
     * <p>
     * <strong>Type:</strong> String<br>
     * <strong>Actions:</strong> {@link AccessibilityActionCompat#ACTION_NEXT_HTML_ELEMENT},
     *         {@link AccessibilityActionCompat#ACTION_PREVIOUS_HTML_ELEMENT}
     * </p>
     */
    public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
        "ACTION_ARGUMENT_HTML_ELEMENT_STRING";

    /**
     * Argument for whether when moving at granularity to extend the selection
     * or to move it otherwise.
     * <p>
     * <strong>Type:</strong> boolean<br>
     * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
     * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
     * </p>
     *
     * @see AccessibilityActionCompat#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
     * @see AccessibilityActionCompat#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
     */
    public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN =
            "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";

    /**
     * Argument for specifying the selection start.
     * <p>
     * <strong>Type:</strong> int<br>
     * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
     * </p>
     *
     * @see AccessibilityActionCompat#ACTION_SET_SELECTION
     */
    public static final String ACTION_ARGUMENT_SELECTION_START_INT =
            "ACTION_ARGUMENT_SELECTION_START_INT";

    /**
     * Argument for specifying the selection end.
     * <p>
     * <strong>Type:</strong> int<br>
     * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
     * </p>
     *
     * @see AccessibilityActionCompat#ACTION_SET_SELECTION
     */
    public static final String ACTION_ARGUMENT_SELECTION_END_INT =
            "ACTION_ARGUMENT_SELECTION_END_INT";

    /**
     * Argument for specifying the text content to set
     * <p>
     * <strong>Type:</strong> CharSequence<br>
     * <strong>Actions:</strong> {@link #ACTION_SET_TEXT}
     * </p>
     *
     * @see AccessibilityActionCompat#ACTION_SET_TEXT
     */
    public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE =
            "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";

    /**
     * Argument for specifying the collection row to make visible on screen.
     * <p>
     * <strong>Type:</strong> int<br>
     * <strong>Actions:</strong>
     * <ul>
     *     <li>{@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION}</li>
     * </ul>
     *
     * @see AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION
     */
    public static final String ACTION_ARGUMENT_ROW_INT =
            "android.view.accessibility.action.ARGUMENT_ROW_INT";

    /**
     * Argument for specifying the collection column to make visible on screen.
     * <p>
     * <strong>Type:</strong> int<br>
     * <strong>Actions:</strong>
     * <ul>
     *     <li>{@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION}</li>
     * </ul>
     *
     * @see AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION
     */
    public static final String ACTION_ARGUMENT_COLUMN_INT =
            "android.view.accessibility.action.ARGUMENT_COLUMN_INT";

    /**
     * Argument for specifying the progress value to set.
     * <p>
     * <strong>Type:</strong> float<br>
     * <strong>Actions:</strong>
     * <ul>
     *     <li>{@link AccessibilityActionCompat#ACTION_SET_PROGRESS}</li>
     * </ul>
     *
     * @see AccessibilityActionCompat#ACTION_SET_PROGRESS
     */
    public static final String ACTION_ARGUMENT_PROGRESS_VALUE =
            "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";

    /**
     * Argument for specifying the x coordinate to which to move a window.
     * <p>
     * <strong>Type:</strong> int<br>
     * <strong>Actions:</strong>
     * <ul>
     *     <li>{@link AccessibilityActionCompat#ACTION_MOVE_WINDOW}</li>
     * </ul>
     *
     * @see AccessibilityActionCompat#ACTION_MOVE_WINDOW
     */
    public static final String ACTION_ARGUMENT_MOVE_WINDOW_X =
            "ACTION_ARGUMENT_MOVE_WINDOW_X";

    /**
     * Argument for specifying the y coordinate to which to move a window.
     * <p>
     * <strong>Type:</strong> int<br>
     * <strong>Actions:</strong>
     * <ul>
     *     <li>{@link AccessibilityActionCompat#ACTION_MOVE_WINDOW}</li>
     * </ul>
     *
     * @see AccessibilityActionCompat#ACTION_MOVE_WINDOW
     */
    public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y =
            "ACTION_ARGUMENT_MOVE_WINDOW_Y";

    /**
     * Argument to represent the duration in milliseconds to press and hold a node.
     * <p>
     * <strong>Type:</strong> int<br>
     * <strong>Actions:</strong>
     * <ul>
     *     <li>{@link AccessibilityActionCompat#ACTION_PRESS_AND_HOLD}</li>
     * </ul>
     *
     * @see AccessibilityActionCompat#ACTION_PRESS_AND_HOLD
     */
    @SuppressLint("ActionValue")
    public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT =
            "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";

    /**
     * <p>Argument to represent the direction when using
     * {@link AccessibilityActionCompat#ACTION_SCROLL_IN_DIRECTION}.</p>
     *
     * <p>
     *     The value of this argument can be one of:
     *     <ul>
     *         <li>{@link View#FOCUS_DOWN}</li>
     *         <li>{@link View#FOCUS_UP}</li>
     *         <li>{@link View#FOCUS_LEFT}</li>
     *         <li>{@link View#FOCUS_RIGHT}</li>
     *         <li>{@link View#FOCUS_FORWARD}</li>
     *         <li>{@link View#FOCUS_BACKWARD}</li>
     *     </ul>
     * </p>
     */
    public static final String ACTION_ARGUMENT_DIRECTION_INT =
            "androidx.core.view.accessibility.action.ARGUMENT_DIRECTION_INT";

    /**
     * <p>Argument to represent the scroll amount as a percent of the visible area of a node, with
     * 1.0F as the default. Values smaller than 1.0F represent a partial scroll of the node, and
     * values larger than 1.0F represent a scroll that extends beyond the currently visible node
     * Rect. Setting this to {@link Float#POSITIVE_INFINITY} or to another "too large" value should
     * scroll to the end of the node. Negative values should not be used with this argument.
     * </p>
     *
     * <p>
     *     This argument should be used with the following scroll actions:
     *     <ul>
     *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_FORWARD}</li>
     *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_BACKWARD}</li>
     *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_UP}</li>
     *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_DOWN}</li>
     *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_LEFT}</li>
     *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_RIGHT}</li>
     *     </ul>
     * </p>
     * <p>
     *     Example: if a view representing a list of items implements
     *     {@link AccessibilityActionCompat#ACTION_SCROLL_FORWARD} to scroll forward by an entire
     *     screen
     *     (one "page"), then passing a value of .25F via this argument should scroll that view
     *     only by 1/4th of a screen. Passing a value of 1.50F via this argument should scroll the
     *     view by 1 1/2 screens or to end of the node if the node doesn't extend to 1 1/2 screens.
     * </p>
     *
     * <p>
     *     This argument should not be used with the following scroll actions, which don't cleanly
     *     conform to granular scroll semantics:
     *     <ul>
     *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_IN_DIRECTION}</li>
     *         <li>{@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION}</li>
     *     </ul>
     * </p>
     *
     * <p>
     *     Views that support this argument should set
     *     {@link #setGranularScrollingSupported(boolean)} to true. Clients should use
     *     {@link #isGranularScrollingSupported()} to check if granular scrolling is supported.
     * </p>
     */
    public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT =
            "androidx.core.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";

    // Focus types

    /**
     * The input focus.
     */
    public static final int FOCUS_INPUT = 1;

    /**
     * The accessibility focus.
     */
    public static final int FOCUS_ACCESSIBILITY = 2;

    // Movement granularities

    /**
     * Movement granularity bit for traversing the text of a node by character.
     */
    public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001;

    /**
     * Movement granularity bit for traversing the text of a node by word.
     */
    public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002;

    /**
     * Movement granularity bit for traversing the text of a node by line.
     */
    public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004;

    /**
     * Movement granularity bit for traversing the text of a node by paragraph.
     */
    public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008;

    /**
     * Movement granularity bit for traversing the text of a node by page.
     */
    public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010;

    /**
     * Key used to request and locate extra data for text character location. This key requests that
     * an array of {@link android.graphics.RectF}s be added to the extras. This request is made with
     * {@link #refreshWithExtraData(String, Bundle)}. The arguments taken by this request are two
     * integers: {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX} and
     * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH}. The starting index must be valid
     * inside the CharSequence returned by {@link #getText()}, and the length must be positive.
     * <p>
     * The data can be retrieved from the {@code Bundle} returned by {@link #getExtras()} using this
     * string as a key for {@link Bundle#getParcelableArray(String)}. The
     * {@link android.graphics.RectF} will be null for characters that either do not exist or are
     * off the screen.
     *
     * {@see #refreshWithExtraData(String, Bundle)}
     */
    @SuppressWarnings("ActionValue")
    public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY =
            "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";

    /**
     * Integer argument specifying the start index of the requested text location data. Must be
     * valid inside the CharSequence returned by {@link #getText()}.
     *
     * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
     */
    @SuppressWarnings("ActionValue")
    public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX =
            "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";

    /**
     * Integer argument specifying the end index of the requested text location data. Must be
     * positive and no larger than {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH}.
     *
     * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
     */
    @SuppressWarnings("ActionValue")
    public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH =
            "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";

    /**
     * The maximum allowed length of the requested text location data.
     */
    public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000;

    /**
     * Prefetching strategy that prefetches the ancestors of the requested node.
     * <p> Ancestors will be prefetched before siblings and descendants.
     *
     * @see #getChild(int, int)
     * @see #getParent(int)
     * @see AccessibilityWindowInfoCompat#getRoot(int)
     * @see AccessibilityService#getRootInActiveWindow(int)
     * @see AccessibilityEvent#getSource(int)
     */
    public static final int FLAG_PREFETCH_ANCESTORS = 0x00000001;

    /**
     * Prefetching strategy that prefetches the siblings of the requested node.
     * <p> To avoid disconnected trees, this flag will also prefetch the parent. Siblings will be
     * prefetched before descendants.
     *
     * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
     */
    public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002;

    /**
     * Prefetching strategy that prefetches the descendants in a hybrid depth first and breadth
     * first approach.
     * <p> The children of the root node is prefetched before recursing on the children. This
     * must not be combined with {@link #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST} or
     * {@link #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST} or this will trigger an
     * IllegalArgumentException.
     *
     * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
     */
    public static final int FLAG_PREFETCH_DESCENDANTS_HYBRID = 0x00000004;

    /**
     * Prefetching strategy that prefetches the descendants of the requested node depth-first.
     * <p> This must not be combined with {@link #FLAG_PREFETCH_DESCENDANTS_HYBRID} or
     * {@link #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST} or this will trigger an
     * IllegalArgumentException.
     *
     * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
     */
    public static final int FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST = 0x00000008;

    /**
     * Prefetching strategy that prefetches the descendants of the requested node breadth-first.
     * <p> This must not be combined with {@link #FLAG_PREFETCH_DESCENDANTS_HYBRID} or
     * {@link #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST} or this will trigger an
     * IllegalArgumentException.
     *
     * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
     */
    public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST = 0x00000010;

    /**
     * Prefetching flag that specifies prefetching should not be interrupted by a request to
     * retrieve a node or perform an action on a node.
     *
     * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
     */
    public static final int FLAG_PREFETCH_UNINTERRUPTIBLE = 0x00000020;

    /**
     * Maximum batch size of prefetched nodes for a request.
     */
    @SuppressLint("MinMaxConstant")
    public static final int MAX_NUMBER_OF_PREFETCHED_NODES = 50;

    private static int sClickableSpanId = 0;

    /**
     * Creates a wrapper for info implementation.
     *
     * @param object The info to wrap.
     * @return A wrapper for if the object is not null, null otherwise.
     */
    @SuppressWarnings("deprecation")
    static AccessibilityNodeInfoCompat wrapNonNullInstance(Object object) {
        if (object != null) {
            return new AccessibilityNodeInfoCompat(object);
        }
        return null;
    }

    /**
     * Creates a new instance wrapping an
     * {@link android.view.accessibility.AccessibilityNodeInfo}.
     *
     * @param info The info.
     *
     * @deprecated Use {@link #wrap(AccessibilityNodeInfo)} instead.
     */
    @Deprecated
    public AccessibilityNodeInfoCompat(Object info) {
        mInfo = (AccessibilityNodeInfo) info;
    }

    private AccessibilityNodeInfoCompat(AccessibilityNodeInfo info) {
        mInfo = info;
    }

    /**
     * Creates a new instance wrapping an
     * {@link android.view.accessibility.AccessibilityNodeInfo}.
     *
     * @param info The info.
     */
    public static AccessibilityNodeInfoCompat wrap(@NonNull AccessibilityNodeInfo info) {
        return new AccessibilityNodeInfoCompat(info);
    }

    /**
     * @return The unwrapped {@link android.view.accessibility.AccessibilityNodeInfo}.
     */
    public AccessibilityNodeInfo unwrap() {
        return mInfo;
    }

    /**
     * @return The wrapped {@link android.view.accessibility.AccessibilityNodeInfo}.
     *
     * @deprecated Use {@link #unwrap()} instead.
     */
    @Deprecated
    public Object getInfo() {
        return mInfo;
    }

    /**
     * Returns a cached instance if such is available otherwise a new one and
     * sets the source.
     *
     * @return An instance.
     * @see #setSource(View)
     */
    public static AccessibilityNodeInfoCompat obtain(View source) {
        return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain(source));
    }

    /**
     * Returns a cached instance if such is available otherwise a new one
     * and sets the source.
     *
     * @param root The root of the virtual subtree.
     * @param virtualDescendantId The id of the virtual descendant.
     * @return An instance.
     *
     * @see #setSource(View, int)
     */
    public static AccessibilityNodeInfoCompat obtain(View root, int virtualDescendantId) {
        return AccessibilityNodeInfoCompat.wrapNonNullInstance(
                AccessibilityNodeInfo.obtain(root, virtualDescendantId));
    }

    /**
     * Returns a cached instance if such is available otherwise a new one.
     *
     * @return An instance.
     */
    public static AccessibilityNodeInfoCompat obtain() {
        return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain());
    }

    /**
     * Returns a cached instance if such is available or a new one is create.
     * The returned instance is initialized from the given <code>info</code>.
     *
     * @param info The other info.
     * @return An instance.
     */
    public static AccessibilityNodeInfoCompat obtain(AccessibilityNodeInfoCompat info) {
        return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain(info.mInfo));
    }

    /**
     * Sets the source.
     *
     * @param source The info source.
     */
    public void setSource(View source) {
        mVirtualDescendantId = NO_ID;

        mInfo.setSource(source);
    }

    /**
     * Sets the source to be a virtual descendant of the given <code>root</code>.
     * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
     * is set as the source.
     * <p>
     * A virtual descendant is an imaginary View that is reported as a part of the view
     * hierarchy for accessibility purposes. This enables custom views that draw complex
     * content to report themselves as a tree of virtual views, thus conveying their
     * logical structure.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}.
     * This class is made immutable before being delivered to an AccessibilityService.
     * <p>
     * This method is not supported on devices running API level < 16 since the platform did
     * not support virtual descendants of real views.
     *
     * @param root The root of the virtual subtree.
     * @param virtualDescendantId The id of the virtual descendant.
     */
    public void setSource(View root, int virtualDescendantId) {
        // Store the ID anyway, since we may need it for equality checks.
        mVirtualDescendantId = virtualDescendantId;

        mInfo.setSource(root, virtualDescendantId);
    }

    /**
     * Find the view that has the specified focus type. The search starts from
     * the view represented by this node info.
     *
     * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
     *         {@link #FOCUS_ACCESSIBILITY}.
     * @return The node info of the focused view or null.
     *
     * @see #FOCUS_INPUT
     * @see #FOCUS_ACCESSIBILITY
     */
    public AccessibilityNodeInfoCompat findFocus(int focus) {
        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.findFocus(focus));
    }

    /**
     * Searches for the nearest view in the specified direction that can take
     * input focus.
     *
     * @param direction The direction. Can be one of:
     *     {@link View#FOCUS_DOWN},
     *     {@link View#FOCUS_UP},
     *     {@link View#FOCUS_LEFT},
     *     {@link View#FOCUS_RIGHT},
     *     {@link View#FOCUS_FORWARD},
     *     {@link View#FOCUS_BACKWARD}.
     *
     * @return The node info for the view that can take accessibility focus.
     */
    public AccessibilityNodeInfoCompat focusSearch(int direction) {
        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.focusSearch(direction));
    }

    /**
     * Gets the id of the window from which the info comes from.
     *
     * @return The window id.
     */
    public int getWindowId() {
        return mInfo.getWindowId();
    }

    /**
     * Gets the number of children.
     *
     * @return The child count.
     */
    public int getChildCount() {
        return mInfo.getChildCount();
    }

    /**
     * Get the child at given index.
     *
     * @param index The child index.
     * @return The child node.
     * @throws IllegalStateException If called outside of an
     *             AccessibilityService.
     */
    public AccessibilityNodeInfoCompat getChild(int index) {
        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getChild(index));
    }

    /**
     * Get the child at given index.
     *
     * @param index The child index.
     * @param prefetchingStrategy the prefetching strategy.
     * @return The child node.
     *
     * @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
     *                               calling {@link #setQueryFromAppProcessEnabled}.
     *
     * @see AccessibilityNodeInfoCompat#getParent(int) for a description of prefetching.
     */
    @Nullable
    public AccessibilityNodeInfoCompat getChild(int index, int prefetchingStrategy) {
        if (Build.VERSION.SDK_INT >= 33) {
            return Api33Impl.getChild(mInfo, index, prefetchingStrategy);
        }
        return getChild(index);
    }

    /**
     * Adds a child.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param child The child.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void addChild(View child) {
        mInfo.addChild(child);
    }

    /**
     * Adds a virtual child which is a descendant of the given <code>root</code>.
     * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
     * is added as a child.
     * <p>
     * A virtual descendant is an imaginary View that is reported as a part of the view
     * hierarchy for accessibility purposes. This enables custom views that draw complex
     * content to report them selves as a tree of virtual views, thus conveying their
     * logical structure.
     * </p>
     *
     * @param root The root of the virtual subtree.
     * @param virtualDescendantId The id of the virtual child.
     */
    public void addChild(View root, int virtualDescendantId) {
        mInfo.addChild(root, virtualDescendantId);
    }

    /**
     * Removes a child. If the child was not previously added to the node,
     * calling this method has no effect.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}.
     * This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param child The child.
     * @return true if the child was present
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public boolean removeChild(View child) {
        if (Build.VERSION.SDK_INT >= 21) {
            return mInfo.removeChild(child);
        } else {
            return false;
        }
    }

    /**
     * Removes a virtual child which is a descendant of the given
     * <code>root</code>. If the child was not previously added to the node,
     * calling this method has no effect.
     *
     * @param root The root of the virtual subtree.
     * @param virtualDescendantId The id of the virtual child.
     * @return true if the child was present
     * @see #addChild(View, int)
     */
    public boolean removeChild(View root, int virtualDescendantId) {
        if (Build.VERSION.SDK_INT >= 21) {
            return mInfo.removeChild(root, virtualDescendantId);
        } else {
            return false;
        }
    }

    /**
     * Gets the actions that can be performed on the node.
     *
     * @return The bit mask of with actions.
     * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_FOCUS
     * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
     * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_SELECT
     * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
     *
     * @deprecated Use {@link #getActionList()} instead.
     */
    @Deprecated
    public int getActions() {
        return mInfo.getActions();
    }

    /**
     * Adds an action that can be performed on the node.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param action The action.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void addAction(int action) {
        mInfo.addAction(action);
    }

    private List<Integer> extrasIntList(String key) {
        ArrayList<Integer> list = mInfo.getExtras()
                .getIntegerArrayList(key);
        if (list == null) {
            list = new ArrayList<Integer>();
            mInfo.getExtras().putIntegerArrayList(key, list);
        }
        return list;
    }

    /**
     * Adds an action that can be performed on the node.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param action The action.
     * @throws IllegalStateException If called from an AccessibilityService.
     * <p>
     * Compatibility:
     * <ul>
     *     <li>API &lt; 21: No-op</li>
     * </ul>
     */
    public void addAction(AccessibilityActionCompat action) {
        if (Build.VERSION.SDK_INT >= 21) {
            mInfo.addAction((AccessibilityNodeInfo.AccessibilityAction) action.mAction);
        }
    }

    /**
     * Removes an action that can be performed on the node. If the action was
     * not already added to the node, calling this method has no effect.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param action The action to be removed.
     * @return The action removed from the list of actions.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     * <p>
     * Compatibility:
     * <ul>
     *     <li>API &lt; 21: Always returns {@code false}</li>
     * </ul>
     */
    public boolean removeAction(AccessibilityActionCompat action) {
        if (Build.VERSION.SDK_INT >= 21) {
            return mInfo.removeAction((AccessibilityNodeInfo.AccessibilityAction) action.mAction);
        } else {
            return false;
        }
    }

    /**
     * Performs an action on the node.
     * <p>
     * <strong>Note:</strong> An action can be performed only if the request is
     * made from an {@link android.accessibilityservice.AccessibilityService}.
     * </p>
     *
     * @param action The action to perform.
     * @return True if the action was performed.
     * @throws IllegalStateException If called outside of an
     *             AccessibilityService.
     */
    public boolean performAction(int action) {
        return mInfo.performAction(action);
    }

    /**
     * Performs an action on the node.
     * <p>
     *   <strong>Note:</strong> An action can be performed only if the request is made
     *   from an {@link android.accessibilityservice.AccessibilityService}.
     * </p>
     *
     * @param action The action to perform.
     * @param arguments A bundle with additional arguments.
     * @return True if the action was performed.
     *
     * @throws IllegalStateException If called outside of an AccessibilityService.
     */
    public boolean performAction(int action, Bundle arguments) {
        return mInfo.performAction(action, arguments);
    }

    /**
     * Sets the movement granularities for traversing the text of this node.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param granularities The bit mask with granularities.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setMovementGranularities(int granularities) {
        mInfo.setMovementGranularities(granularities);
    }

    /**
     * Gets the movement granularities for traversing the text of this node.
     *
     * @return The bit mask with granularities.
     */
    public int getMovementGranularities() {
        return mInfo.getMovementGranularities();
    }

    /**
     * Finds {@link android.view.accessibility.AccessibilityNodeInfo}s by text. The match
     * is case insensitive containment. The search is relative to this info i.e. this
     * info is the root of the traversed tree.
     *
     * @param text The searched text.
     * @return A list of node info.
     */
    public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(String text) {
        List<AccessibilityNodeInfoCompat> result = new ArrayList<AccessibilityNodeInfoCompat>();
        List<AccessibilityNodeInfo> infos = mInfo.findAccessibilityNodeInfosByText(text);
        final int infoCount = infos.size();
        for (int i = 0; i < infoCount; i++) {
            AccessibilityNodeInfo info = infos.get(i);
            result.add(AccessibilityNodeInfoCompat.wrap(info));
        }
        return result;
    }

    /**
     * Gets the parent.
     *
     * @return The parent.
     */
    public AccessibilityNodeInfoCompat getParent() {
        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getParent());
    }

    /**
     * Gets the parent.
     *
     * <p>
     * Use {@code prefetchingStrategy} to determine the types of
     * nodes prefetched from the app if the requested node is not in the cache and must be retrieved
     * by the app. The default strategy for {@link #getParent()} is a combination of ancestor and
     * sibling strategies. The app will prefetch until all nodes fulfilling the strategies are
     * fetched, another node request is sent, or the maximum prefetch batch size of
     * {@link #MAX_NUMBER_OF_PREFETCHED_NODES} nodes is reached. To prevent interruption by another
     * request and to force prefetching of the max batch size, use
     * {@link AccessibilityNodeInfoCompat#FLAG_PREFETCH_UNINTERRUPTIBLE}.
     * </p>
     *
     * @param prefetchingStrategy the prefetching strategy.
     * @return The parent.
     *
     * @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
     *                               calling {@link #setQueryFromAppProcessEnabled}.
     *
     * @see #FLAG_PREFETCH_ANCESTORS
     * @see #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST
     * @see #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST
     * @see #FLAG_PREFETCH_DESCENDANTS_HYBRID
     * @see #FLAG_PREFETCH_SIBLINGS
     * @see #FLAG_PREFETCH_UNINTERRUPTIBLE
     */
    @Nullable
    public AccessibilityNodeInfoCompat getParent(int prefetchingStrategy) {
        if (Build.VERSION.SDK_INT >= 33) {
            return Api33Impl.getParent(mInfo, prefetchingStrategy);
        }
        return getParent();
    }

    /**
     * Sets the parent.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param parent The parent.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setParent(View parent) {
        mParentVirtualDescendantId = NO_ID;

        mInfo.setParent(parent);
    }

    /**
     * Sets the parent to be a virtual descendant of the given <code>root</code>.
     * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
     * is set as the parent.
     * <p>
     * A virtual descendant is an imaginary View that is reported as a part of the view
     * hierarchy for accessibility purposes. This enables custom views that draw complex
     * content to report them selves as a tree of virtual views, thus conveying their
     * logical structure.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}.
     * This class is made immutable before being delivered to an AccessibilityService.
     * <p>
     * This method is not supported on devices running API level < 16 since the platform did
     * not support virtual descendants of real views.
     *
     * @param root The root of the virtual subtree.
     * @param virtualDescendantId The id of the virtual descendant.
     */
    public void setParent(View root, int virtualDescendantId) {
        // Store the ID anyway, since we may need it for equality checks.
        mParentVirtualDescendantId = virtualDescendantId;

        mInfo.setParent(root, virtualDescendantId);
    }

    /**
     * Gets the node bounds in the viewParent's coordinates.
     * {@link #getParent()} does not represent the source's viewParent.
     * Instead it represents the result of {@link View#getParentForAccessibility()},
     * which returns the closest ancestor where {@link View#isImportantForAccessibility()} is true.
     * So this method is not reliable.
     *
     * @param outBounds The output node bounds.
     *
     * @deprecated Use {@link #getBoundsInScreen(Rect)} instead.
     */
    @Deprecated
    public void getBoundsInParent(Rect outBounds) {
        mInfo.getBoundsInParent(outBounds);
    }

    /**
     * Sets the node bounds in the viewParent's coordinates.
     * {@link #getParent()} does not represent the source's viewParent.
     * Instead it represents the result of {@link View#getParentForAccessibility()},
     * which returns the closest ancestor where {@link View#isImportantForAccessibility()} is true.
     * So this method is not reliable.
     *
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param bounds The node bounds.
     * @throws IllegalStateException If called from an AccessibilityService.
     *
     * @deprecated Accessibility services should not care about these bounds.
     */
    @Deprecated
    public void setBoundsInParent(Rect bounds) {
        mInfo.setBoundsInParent(bounds);
    }

    /**
     * Gets the node bounds in screen coordinates.
     *
     * @param outBounds The output node bounds.
     */
    public void getBoundsInScreen(Rect outBounds) {
        mInfo.getBoundsInScreen(outBounds);
    }

    /**
     * Sets the node bounds in screen coordinates.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param bounds The node bounds.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setBoundsInScreen(Rect bounds) {
        mInfo.setBoundsInScreen(bounds);
    }

    /**
     * Gets the node bounds in window coordinates.
     * <p>
     * When magnification is enabled, the bounds in window are scaled up by magnification scale
     * and the positions are also adjusted according to the offset of magnification viewport.
     * For example, it returns Rect(-180, -180, 0, 0) for original bounds Rect(10, 10, 100, 100),
     * when the magnification scale is 2 and offsets for X and Y are both 200.
     * <p/>
     * <p>
     * Compatibility:
     * <ul>
     *     <li>API &lt; 19: No-op</li>
     * </ul>
     * @param outBounds The output node bounds.
     */
    public void getBoundsInWindow(@NonNull  Rect outBounds) {
        if (Build.VERSION.SDK_INT >= 34) {
            Api34Impl.getBoundsInWindow(mInfo, outBounds);
        } else {
            Rect extraBounds = mInfo.getExtras().getParcelable(BOUNDS_IN_WINDOW_KEY);
            if (extraBounds != null) {
                outBounds.set(extraBounds.left, extraBounds.top, extraBounds.right,
                        extraBounds.bottom);
            }
        }
    }

    /**
     * Sets the node bounds in window coordinates.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     * <p>
     * Compatibility:
     * <ul>
     *     <li>API &lt; 19: No-op</li>
     * </ul>
     * @param bounds The node bounds.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setBoundsInWindow(@NonNull Rect bounds) {
        if (Build.VERSION.SDK_INT >= 34) {
            Api34Impl.setBoundsInWindow(mInfo, bounds);
        } else {
            mInfo.getExtras().putParcelable(BOUNDS_IN_WINDOW_KEY, bounds);
        }
    }

    /**
     * Gets whether this node is checkable.
     *
     * @return True if the node is checkable.
     */
    public boolean isCheckable() {
        return mInfo.isCheckable();
    }

    /**
     * Sets whether this node is checkable.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param checkable True if the node is checkable.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setCheckable(boolean checkable) {
        mInfo.setCheckable(checkable);
    }

    /**
     * Gets whether this node is checked.
     *
     * @return True if the node is checked.
     */
    public boolean isChecked() {
        return mInfo.isChecked();
    }

    /**
     * Sets whether this node is checked.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param checked True if the node is checked.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setChecked(boolean checked) {
        mInfo.setChecked(checked);
    }

    /**
     * Gets whether this node is focusable.
     *
     * @return True if the node is focusable.
     */
    public boolean isFocusable() {
        return mInfo.isFocusable();
    }

    /**
     * Sets whether this node is focusable.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param focusable True if the node is focusable.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setFocusable(boolean focusable) {
        mInfo.setFocusable(focusable);
    }

    /**
     * Gets whether this node is focused.
     *
     * @return True if the node is focused.
     */
    public boolean isFocused() {
        return mInfo.isFocused();
    }

    /**
     * Sets whether this node is focused.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param focused True if the node is focused.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setFocused(boolean focused) {
        mInfo.setFocused(focused);
    }

    /**
     * Gets whether this node is visible to the user.
     *
     * @return Whether the node is visible to the user.
     */
    public boolean isVisibleToUser() {
        return mInfo.isVisibleToUser();
    }

    /**
     * Sets whether this node is visible to the user.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param visibleToUser Whether the node is visible to the user.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setVisibleToUser(boolean visibleToUser) {
        mInfo.setVisibleToUser(visibleToUser);
    }

    /**
     * Gets whether this node is accessibility focused.
     *
     * @return True if the node is accessibility focused.
     */
    public boolean isAccessibilityFocused() {
        return mInfo.isAccessibilityFocused();
    }

    /**
     * Sets whether this node is accessibility focused.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param focused True if the node is accessibility focused.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setAccessibilityFocused(boolean focused) {
        mInfo.setAccessibilityFocused(focused);
    }

    /**
     * Gets whether this node is selected.
     *
     * @return True if the node is selected.
     */
    public boolean isSelected() {
        return mInfo.isSelected();
    }

    /**
     * Sets whether this node is selected.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param selected True if the node is selected.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setSelected(boolean selected) {
        mInfo.setSelected(selected);
    }

    /**
     * Gets whether this node is clickable.
     *
     * @return True if the node is clickable.
     */
    public boolean isClickable() {
        return mInfo.isClickable();
    }

    /**
     * Sets whether this node is clickable.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param clickable True if the node is clickable.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setClickable(boolean clickable) {
        mInfo.setClickable(clickable);
    }

    /**
     * Gets whether this node is long clickable.
     *
     * @return True if the node is long clickable.
     */
    public boolean isLongClickable() {
        return mInfo.isLongClickable();
    }

    /**
     * Sets whether this node is long clickable.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param longClickable True if the node is long clickable.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setLongClickable(boolean longClickable) {
        mInfo.setLongClickable(longClickable);
    }

    /**
     * Gets whether this node is enabled.
     *
     * @return True if the node is enabled.
     */
    public boolean isEnabled() {
        return mInfo.isEnabled();
    }

    /**
     * Sets whether this node is enabled.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param enabled True if the node is enabled.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setEnabled(boolean enabled) {
        mInfo.setEnabled(enabled);
    }

    /**
     * Gets whether this node is a password.
     *
     * @return True if the node is a password.
     */
    public boolean isPassword() {
        return mInfo.isPassword();
    }

    /**
     * Sets whether this node is a password.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param password True if the node is a password.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setPassword(boolean password) {
        mInfo.setPassword(password);
    }

    /**
     * Gets if the node is scrollable.
     *
     * @return True if the node is scrollable, false otherwise.
     */
    public boolean isScrollable() {
        return mInfo.isScrollable();
    }

    /**
     * Sets if the node is scrollable.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param scrollable True if the node is scrollable, false otherwise.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setScrollable(boolean scrollable) {
        mInfo.setScrollable(scrollable);
    }


    /**
     * Gets if the node supports granular scrolling.
     * <p>
     * Compatibility:
     * <ul>
     *     <li>Api &lt; 19: Returns false.</li>
     * </ul>
     * @return True if all scroll actions that could support
     * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT} have done so, false otherwise.
     */
    public boolean isGranularScrollingSupported() {
        return getBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING);
    }

    /**
     * Sets if the node supports granular scrolling. This should be set to true if all scroll
     * actions which could support {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT} have done so.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     * <p>
     * Compatibility:
     * <ul>
     *     <li>Api &lt; 19: No-op.</li>
     * </ul>
     * @param granularScrollingSupported True if the node supports granular scrolling, false
     *                                  otherwise.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setGranularScrollingSupported(boolean granularScrollingSupported) {
        setBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING,
                granularScrollingSupported);
    }

    /**
     * Gets if the node has selectable text.
     *
     * <p>
     *     Services should use {@link #ACTION_SET_SELECTION} for selection. Editable text nodes must
     *     also be selectable. But not all UIs will populate this field, so services should consider
     *     'isTextSelectable | isEditable' to ensure they don't miss nodes with selectable text.
     * </p>
     * <p>
     * Compatibility:
     * <ul>
     *     <li>Api &lt; 19: Returns false.</li>
     * </ul>
     *
     * @see #isEditable
     * @return True if the node has selectable text.
     */
    public boolean isTextSelectable() {
        if (Build.VERSION.SDK_INT >= 33) {
            return Api33Impl.isTextSelectable(mInfo);
        } else {
            return getBooleanProperty(BOOLEAN_PROPERTY_TEXT_SELECTABLE);
        }
    }

    /**
     * Sets if the node has selectable text.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     * <p>
     * Compatibility:
     * <ul>
     *     <li>Api &lt; 19: Does not operate.</li>
     * </ul>
     * </p>
     *
     * @param selectableText True if the node has selectable text, false otherwise.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setTextSelectable(boolean selectableText) {
        if (Build.VERSION.SDK_INT >= 33) {
            Api33Impl.setTextSelectable(mInfo, selectableText);
        } else {
            setBooleanProperty(BOOLEAN_PROPERTY_TEXT_SELECTABLE, selectableText);
        }
    }

    /**
     * Gets the minimum time duration between two content change events.
     */
    public long getMinDurationBetweenContentChangesMillis() {
        if (Build.VERSION.SDK_INT >= 34) {
            return Api34Impl.getMinDurationBetweenContentChangeMillis(mInfo);
        } else {
            return mInfo.getExtras().getLong(MIN_DURATION_BETWEEN_CONTENT_CHANGES_KEY);
        }
    }

    /**
     * Sets the minimum time duration between two content change events, which is used in throttling
     * content change events in accessibility services.
     *
     * <p>
     * Example: An app can set MinDurationBetweenContentChanges as 1 min for a view which sends
     * content change events to accessibility services one event per second.
     * Accessibility service will throttle those content change events and only handle one event
     * per minute for that view.
     * </p>
     *
     * @see AccessibilityEventCompat#getContentChangeTypes for all content change types.
     * @param duration the minimum duration between content change events.
     */
    public void setMinDurationBetweenContentChangesMillis(long duration) {
        if (Build.VERSION.SDK_INT >= 34) {
            Api34Impl.setMinDurationBetweenContentChangeMillis(mInfo, duration);
        } else {
            mInfo.getExtras().putLong(MIN_DURATION_BETWEEN_CONTENT_CHANGES_KEY, duration);
        }
    }

    /**
     * Returns whether the node originates from a view considered important for accessibility.
     *
     * @return {@code true} if the node originates from a view considered important for
     *         accessibility, {@code false} otherwise
     *
     * @see View#isImportantForAccessibility()
     */
    public boolean isImportantForAccessibility() {
        if (Build.VERSION.SDK_INT >= 24) {
            return mInfo.isImportantForAccessibility();
        } else {
            return true;
        }
    }

    /**
     * Sets whether the node is considered important for accessibility.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param important {@code true} if the node is considered important for accessibility,
     *                  {@code false} otherwise
     */
    public void setImportantForAccessibility(boolean important) {
        if (Build.VERSION.SDK_INT >= 24) {
            mInfo.setImportantForAccessibility(important);
        }
    }

    /**
     * Gets if the node's accessibility data is considered sensitive.
     *
     * @return True if the node's data is considered sensitive, false otherwise.
     * @see View#isAccessibilityDataSensitive()
     */
    public boolean isAccessibilityDataSensitive() {
        if (Build.VERSION.SDK_INT >= 34) {
            return Api34Impl.isAccessibilityDataSensitive(mInfo);
        } else {
            return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_DATA_SENSITIVE);
        }
    }

    /**
     * Sets whether this node's accessibility data is considered sensitive.
     *
     * <p>
     * For SDK 34 and higher: when set to true the framework will hide this node from
     * accessibility services with the
     * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool}
     * property set to false.
     * </p>
     * <p>
     * Otherwise, for SDK 19 and higher: the framework cannot hide this node but this property may
     * be read by accessibility services to provide modified behavior for sensitive nodes.
     * </p>
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param accessibilityDataSensitive True if the node's accessibility data is considered
     *                                   sensitive.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setAccessibilityDataSensitive(boolean accessibilityDataSensitive) {
        if (Build.VERSION.SDK_INT >= 34) {
            Api34Impl.setAccessibilityDataSensitive(mInfo, accessibilityDataSensitive);
        } else {
            setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_DATA_SENSITIVE,
                    accessibilityDataSensitive);
        }
    }

    /**
     * Gets the package this node comes from.
     *
     * @return The package name.
     */
    public CharSequence getPackageName() {
        return mInfo.getPackageName();
    }

    /**
     * Sets the package this node comes from.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param packageName The package name.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setPackageName(CharSequence packageName) {
        mInfo.setPackageName(packageName);
    }

    /**
     * Gets the class this node comes from.
     *
     * @return The class name.
     */
    public CharSequence getClassName() {
        return mInfo.getClassName();
    }

    /**
     * Sets the class this node comes from.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param className The class name.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setClassName(CharSequence className) {
        mInfo.setClassName(className);
    }

    /**
     * Gets the text of this node.
     *
     * @return The text.
     */
    public CharSequence getText() {
        if (hasSpans()) {
            List<Integer> starts = extrasIntList(SPANS_START_KEY);
            List<Integer> ends = extrasIntList(SPANS_END_KEY);
            List<Integer> flags = extrasIntList(SPANS_FLAGS_KEY);
            List<Integer> ids = extrasIntList(SPANS_ID_KEY);
            Spannable spannable = new SpannableString(TextUtils.substring(mInfo.getText(),
                    0, mInfo.getText().length()));
            for (int i = 0; i < starts.size(); i++) {
                spannable.setSpan(new AccessibilityClickableSpanCompat(ids.get(i), this,
                                getExtras().getInt(SPANS_ACTION_ID_KEY)),
                        starts.get(i), ends.get(i), flags.get(i));
            }
            return spannable;
        } else {
            return mInfo.getText();
        }
    }

    /**
     * Sets the text of this node.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param text The text.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setText(CharSequence text) {
        mInfo.setText(text);
    }

    /**
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public void addSpansToExtras(CharSequence text, View view) {
        if (Build.VERSION.SDK_INT < 26) {
            clearExtrasSpans();
            removeCollectedSpans(view);
            ClickableSpan[] spans = getClickableSpans(text);
            if (spans != null && spans.length > 0) {
                getExtras().putInt(SPANS_ACTION_ID_KEY, R.id.accessibility_action_clickable_span);
                SparseArray<WeakReference<ClickableSpan>> tagSpans =
                        getOrCreateSpansFromViewTags(view);
                for (int i = 0; spans != null && i < spans.length; i++) {
                    int id = idForClickableSpan(spans[i], tagSpans);
                    tagSpans.put(id, new WeakReference<>(spans[i]));
                    addSpanLocationToExtras(spans[i], (Spanned) text, id);
                }
            }
        }
    }

    private SparseArray<WeakReference<ClickableSpan>> getOrCreateSpansFromViewTags(View host) {
        SparseArray<WeakReference<ClickableSpan>> spans = getSpansFromViewTags(host);
        if (spans == null) {
            spans = new SparseArray<>();
            host.setTag(R.id.tag_accessibility_clickable_spans, spans);
        }
        return spans;
    }

    @SuppressWarnings("unchecked")
    private SparseArray<WeakReference<ClickableSpan>> getSpansFromViewTags(View host) {
        return (SparseArray<WeakReference<ClickableSpan>>) host.getTag(
                R.id.tag_accessibility_clickable_spans);
    }

    /**
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public static ClickableSpan[] getClickableSpans(CharSequence text) {
        if (text instanceof Spanned) {
            Spanned spanned = (Spanned) text;
            return spanned.getSpans(0, text.length(), ClickableSpan.class);
        }
        return null;
    }

    private int idForClickableSpan(ClickableSpan span,
            SparseArray<WeakReference<ClickableSpan>> spans) {
        if (spans != null) {
            for (int i = 0; i < spans.size(); i++) {
                ClickableSpan aSpan = spans.valueAt(i).get();
                if (span.equals(aSpan)) {
                    return spans.keyAt(i);
                }
            }
        }
        return sClickableSpanId++;
    }

    private boolean hasSpans() {
        return !extrasIntList(SPANS_START_KEY).isEmpty();
    }

    private void clearExtrasSpans() {
        mInfo.getExtras().remove(SPANS_START_KEY);
        mInfo.getExtras().remove(SPANS_END_KEY);
        mInfo.getExtras().remove(SPANS_FLAGS_KEY);
        mInfo.getExtras().remove(SPANS_ID_KEY);
    }

    private void addSpanLocationToExtras(ClickableSpan span, Spanned spanned, int id) {
        extrasIntList(SPANS_START_KEY).add(spanned.getSpanStart(span));
        extrasIntList(SPANS_END_KEY).add(spanned.getSpanEnd(span));
        extrasIntList(SPANS_FLAGS_KEY).add(spanned.getSpanFlags(span));
        extrasIntList(SPANS_ID_KEY).add(id);
    }

    private void removeCollectedSpans(View view) {
        SparseArray<WeakReference<ClickableSpan>> spans = getSpansFromViewTags(view);
        if (spans != null) {
            List<Integer> toBeRemovedIndices = new ArrayList<>();
            for (int i = 0; i < spans.size(); i++) {
                if (spans.valueAt(i).get() == null) {
                    toBeRemovedIndices.add(i);
                }
            }
            for (int i = 0; i < toBeRemovedIndices.size(); i++) {
                spans.remove(toBeRemovedIndices.get(i));
            }
        }
    }

    /**
     * Gets the content description of this node.
     *
     * @return The content description.
     */
    public CharSequence getContentDescription() {
        return mInfo.getContentDescription();
    }

    /**
     * Gets the state description of this node.
     *
     * @return the state description or null if android version smaller
     * than 19.
     */
    public @Nullable CharSequence getStateDescription() {
        if (Build.VERSION.SDK_INT >= 30) {
            return Api30Impl.getStateDescription(mInfo);
        } else {
            return mInfo.getExtras().getCharSequence(STATE_DESCRIPTION_KEY);
        }
    }

    /**
     * Sets the content description of this node.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param contentDescription The content description.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setContentDescription(CharSequence contentDescription) {
        mInfo.setContentDescription(contentDescription);
    }

    /**
     * Sets the state description of this node.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param stateDescription the state description of this node.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setStateDescription(@Nullable CharSequence stateDescription) {
        if (Build.VERSION.SDK_INT >= 30) {
            Api30Impl.setStateDescription(mInfo, stateDescription);
        } else {
            mInfo.getExtras().putCharSequence(STATE_DESCRIPTION_KEY, stateDescription);
        }
    }

    /**
     * Gets the unique id of this node.
     *
     * @return the unique id or null if android version smaller
     * than 19.
     */
    public @Nullable String getUniqueId() {
        if (Build.VERSION.SDK_INT >= 33) {
            return Api33Impl.getUniqueId(mInfo);
        } else {
            return mInfo.getExtras().getString(UNIQUE_ID_KEY);
        }
    }

    /**
     * Sets the unique id of this node.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param uniqueId the unique id of this node.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setUniqueId(@Nullable String uniqueId) {
        if (Build.VERSION.SDK_INT >= 33) {
            Api33Impl.setUniqueId(mInfo, uniqueId);
        } else {
            mInfo.getExtras().putString(UNIQUE_ID_KEY, uniqueId);
        }
    }

    /**
     * Sets the container title for app-developer-defined container which can be any type of
     * ViewGroup or layout.
     * Container title will be used to group together related controls, similar to HTML fieldset.
     * Or container title may identify a large piece of the UI that is visibly grouped together,
     * such as a toolbar or a card, etc.
     * <p>
     * Container title helps to assist in navigation across containers and other groups.
     * For example, a screen reader may use this to determine where to put accessibility focus.
     * </p>
     * <p>
     * Container title is different from pane title{@link #setPaneTitle} which indicates that the
     * node represents a window or activity.
     * </p>
     *
     * <p>
     *  Example: An app can set container titles on several non-modal menus, containing TextViews
     *  or ImageButtons that have content descriptions, text, etc. Screen readers can quickly
     *  switch accessibility focus among menus instead of child views.  Other accessibility-services
     *  can easily find the menu.
     * </p>
     * <p>
     * Compatibility:
     * <ul>
     *     <li>API &lt; 19: No-op</li>
     * </ul>
     * @param containerTitle The container title that is associated with a ViewGroup/Layout on the
     *                       screen.
     */
    public void setContainerTitle(@Nullable CharSequence containerTitle) {
        if (Build.VERSION.SDK_INT >= 34) {
            Api34Impl.setContainerTitle(mInfo, containerTitle);
        } else {
            mInfo.getExtras().putCharSequence(CONTAINER_TITLE_KEY, containerTitle);
        }
    }

    /**
     * Returns the container title.
     * <p>
     * Compatibility:
     * <ul>
     *     <li>API &lt; 19: Returns null</li>
     * </ul>
     * @see #setContainerTitle for details.
     */
    @Nullable
    public CharSequence getContainerTitle() {
        if (Build.VERSION.SDK_INT >= 34) {
            return Api34Impl.getContainerTitle(mInfo);
        } else {
            return mInfo.getExtras().getCharSequence(CONTAINER_TITLE_KEY);
        }
    }

    /**
     * Return an instance back to be reused.
     * <p>
     * <strong>Note:</strong> You must not touch the object after calling this function.
     *
     * @throws IllegalStateException If the info is already recycled.
     * @deprecated Accessibility Object recycling is no longer necessary or functional.
     */
    @Deprecated
    public void recycle() { }

    /**
     * Sets the fully qualified resource name of the source view's id.
     *
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param viewId The id resource name.
     */
    public void setViewIdResourceName(String viewId) {
        mInfo.setViewIdResourceName(viewId);
    }

    /**
     * Gets the fully qualified resource name of the source view's id.
     *
     * <p>
     *   <strong>Note:</strong> The primary usage of this API is for UI test automation
     *   and in order to report the source view id of an {@link AccessibilityNodeInfoCompat}
     *   the client has to set the {@link AccessibilityServiceInfoCompat#FLAG_REPORT_VIEW_IDS}
     *   flag when configuring their {@link android.accessibilityservice.AccessibilityService}.
     * </p>
     *
     * @return The id resource name.
     */
    public String getViewIdResourceName() {
        return mInfo.getViewIdResourceName();
    }

    /**
     * Gets the node's live region mode.
     * <p>
     * A live region is a node that contains information that is important for
     * the user and when it changes the user should be notified. For example,
     * in a login screen with a TextView that displays an "incorrect password"
     * notification, that view should be marked as a live region with mode
     * {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_POLITE}.
     * <p>
     * It is the responsibility of the accessibility service to monitor
     * {@link AccessibilityEventCompat#TYPE_WINDOW_CONTENT_CHANGED} events
     * indicating changes to live region nodes and their children.
     *
     * @return The live region mode, or
     *         {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_NONE} if the view is
     *         not a live region.
     * @see ViewCompat#getAccessibilityLiveRegion(View)
     */
    public int getLiveRegion() {
        return mInfo.getLiveRegion();
    }

    /**
     * Sets the node's live region mode.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is
     * made immutable before being delivered to an AccessibilityService.
     *
     * @param mode The live region mode, or
     *        {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_NONE} if the view is
     *        not a live region.
     * @see ViewCompat#setAccessibilityLiveRegion(View, int)
     */
    public void setLiveRegion(int mode) {
        mInfo.setLiveRegion(mode);
    }

    /**
     * Get the drawing order of the view corresponding it this node.
     * <p>
     * Drawing order is determined only within the node's parent, so this index is only relative
     * to its siblings.
     * <p>
     * In some cases, the drawing order is essentially simultaneous, so it is possible for two
     * siblings to return the same value. It is also possible that values will be skipped.
     *
     * @return The drawing position of the view corresponding to this node relative to its siblings.
     */
    public int getDrawingOrder() {
        if (Build.VERSION.SDK_INT >= 24) {
            return mInfo.getDrawingOrder();
        } else {
            return 0;
        }
    }

    /**
     * Set the drawing order of the view corresponding it this node.
     *
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     * @param drawingOrderInParent
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setDrawingOrder(int drawingOrderInParent) {
        if (Build.VERSION.SDK_INT >= 24) {
            mInfo.setDrawingOrder(drawingOrderInParent);
        }
    }

    /**
     * Gets the collection info if the node is a collection. A collection
     * child is always a collection item.
     *
     * @return The collection info.
     */
    public CollectionInfoCompat getCollectionInfo() {
        AccessibilityNodeInfo.CollectionInfo info = mInfo.getCollectionInfo();
        if (info != null) {
            return new CollectionInfoCompat(info);
        }
        return null;
    }

    public void setCollectionInfo(Object collectionInfo) {
        mInfo.setCollectionInfo((collectionInfo == null) ? null
                : (AccessibilityNodeInfo.CollectionInfo) ((CollectionInfoCompat)
                        collectionInfo).mInfo);

    }

    public void setCollectionItemInfo(Object collectionItemInfo) {
        mInfo.setCollectionItemInfo((collectionItemInfo == null) ? null
                : (AccessibilityNodeInfo.CollectionItemInfo) ((CollectionItemInfoCompat)
                        collectionItemInfo).mInfo);
    }

    /**
     * Gets the collection item info if the node is a collection item. A collection
     * item is always a child of a collection.
     *
     * @return The collection item info.
     */
    public CollectionItemInfoCompat getCollectionItemInfo() {
        AccessibilityNodeInfo.CollectionItemInfo info = mInfo.getCollectionItemInfo();
        if (info != null) {
            return new CollectionItemInfoCompat(info);
        }
        return null;
    }

    /**
     * Gets the range info if this node is a range.
     *
     * @return The range.
     */
    public RangeInfoCompat getRangeInfo() {
        AccessibilityNodeInfo.RangeInfo info = mInfo.getRangeInfo();
        if (info != null) {
            return new RangeInfoCompat(info);
        }
        return null;
    }

    /**
     * Sets the range info if this node is a range.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param rangeInfo The range info.
     */
    public void setRangeInfo(RangeInfoCompat rangeInfo) {
        mInfo.setRangeInfo((AccessibilityNodeInfo.RangeInfo) rangeInfo.mInfo);
    }

    /**
     * Gets the {@link android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo
     * extra rendering info} if the node is meant to be refreshed with extra data
     * to examine rendering related accessibility issues.
     *
     * @return The {@link android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo
     * extra rendering info}.
     */
    @Nullable
    public AccessibilityNodeInfo.ExtraRenderingInfo getExtraRenderingInfo() {
        if (Build.VERSION.SDK_INT >= 33) {
            return Api33Impl.getExtraRenderingInfo(mInfo);
        } else {
            return null;
        }
    }

    /**
     * Gets the actions that can be performed on the node.
     *
     * @return A list of AccessibilityActions.
     * <p>
     * Compatibility:
     * <ul>
     *     <li>API &lt; 21: Always returns {@code null}</li>
     * </ul>
     */
    @SuppressWarnings({"unchecked", "MixedMutabilityReturnType"})
    public List<AccessibilityActionCompat> getActionList() {
        List<Object> actions = null;
        if (Build.VERSION.SDK_INT >= 21) {
            actions = (List<Object>) (List<?>) mInfo.getActionList();
        }
        if (actions != null) {
            List<AccessibilityActionCompat> result = new ArrayList<AccessibilityActionCompat>();
            final int actionCount = actions.size();
            for (int i = 0; i < actionCount; i++) {
                Object action = actions.get(i);
                result.add(new AccessibilityActionCompat(action));
            }
            return result;
        } else {
            return Collections.<AccessibilityActionCompat>emptyList();
        }
    }

    /**
     * Sets if the content of this node is invalid. For example,
     * a date is not well-formed.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param contentInvalid If the node content is invalid.
     */
    public void setContentInvalid(boolean contentInvalid) {
        mInfo.setContentInvalid(contentInvalid);
    }

    /**
     * Gets if the content of this node is invalid. For example,
     * a date is not well-formed.
     *
     * @return If the node content is invalid.
     */
    public boolean isContentInvalid() {
        return mInfo.isContentInvalid();
    }

    /**
     * Gets whether this node is context clickable.
     *
     * @return True if the node is context clickable.
     */
    public boolean isContextClickable() {
        if (Build.VERSION.SDK_INT >= 23) {
            return mInfo.isContextClickable();
        } else {
            return false;
        }
    }

    /**
     * Sets whether this node is context clickable.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}. This class is made immutable
     * before being delivered to an AccessibilityService.
     * </p>
     *
     * @param contextClickable True if the node is context clickable.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setContextClickable(boolean contextClickable) {
        if (Build.VERSION.SDK_INT >= 23) {
            mInfo.setContextClickable(contextClickable);
        }
    }

    /**
     * Gets the hint text of this node. Only applies to nodes where text can be entered.
     *
     * @return The hint text.
     */
    public @Nullable CharSequence getHintText() {
        if (Build.VERSION.SDK_INT >= 26) {
            return mInfo.getHintText();
        } else {
            return mInfo.getExtras().getCharSequence(HINT_TEXT_KEY);
        }
    }

    /**
     * Sets the hint text of this node. Only applies to nodes where text can be entered.
     * <p>This method has no effect below API 19</p>
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param hintText The hint text for this mode.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setHintText(@Nullable CharSequence hintText) {
        if (Build.VERSION.SDK_INT >= 26) {
            mInfo.setHintText(hintText);
        } else {
            mInfo.getExtras().putCharSequence(HINT_TEXT_KEY, hintText);
        }
    }


    /**
     * Sets the error text of this node.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param error The error text.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setError(CharSequence error) {
        if (Build.VERSION.SDK_INT >= 21) {
            mInfo.setError(error);
        }
    }

    /**
     * Gets the error text of this node.
     *
     * @return The error text.
     */
    public CharSequence getError() {
        if (Build.VERSION.SDK_INT >= 21) {
            return mInfo.getError();
        } else {
            return null;
        }
    }

    /**
     * Sets the view for which the view represented by this info serves as a
     * label for accessibility purposes.
     *
     * @param labeled The view for which this info serves as a label.
     */
    public void setLabelFor(View labeled) {
        mInfo.setLabelFor(labeled);
    }

    /**
     * Sets the view for which the view represented by this info serves as a
     * label for accessibility purposes. If <code>virtualDescendantId</code>
     * is {@link View#NO_ID} the root is set as the labeled.
     * <p>
     * A virtual descendant is an imaginary View that is reported as a part of the view
     * hierarchy for accessibility purposes. This enables custom views that draw complex
     * content to report themselves as a tree of virtual views, thus conveying their
     * logical structure.
     * </p>
     *
     * @param root The root whose virtual descendant serves as a label.
     * @param virtualDescendantId The id of the virtual descendant.
     */
    public void setLabelFor(View root, int virtualDescendantId) {
        mInfo.setLabelFor(root, virtualDescendantId);
    }

    /**
     * Gets the node info for which the view represented by this info serves as
     * a label for accessibility purposes.
     *
     * @return The labeled info.
     */
    public AccessibilityNodeInfoCompat getLabelFor() {
        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getLabelFor());
    }

    /**
     * Sets the view which serves as the label of the view represented by
     * this info for accessibility purposes.
     *
     * @param label The view that labels this node's source.
     */
    public void setLabeledBy(View label) {
        mInfo.setLabeledBy(label);
    }

    /**
     * Sets the view which serves as the label of the view represented by
     * this info for accessibility purposes. If <code>virtualDescendantId</code>
     * is {@link View#NO_ID} the root is set as the label.
     * <p>
     * A virtual descendant is an imaginary View that is reported as a part of the view
     * hierarchy for accessibility purposes. This enables custom views that draw complex
     * content to report themselves as a tree of virtual views, thus conveying their
     * logical structure.
     * </p>
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param root The root whose virtual descendant labels this node's source.
     * @param virtualDescendantId The id of the virtual descendant.
     */
    public void setLabeledBy(View root, int virtualDescendantId) {
        mInfo.setLabeledBy(root, virtualDescendantId);
    }

    /**
     * Gets the node info which serves as the label of the view represented by
     * this info for accessibility purposes.
     *
     * @return The label.
     */
    public AccessibilityNodeInfoCompat getLabeledBy() {
        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getLabeledBy());
    }

    /**
     * Gets if this node opens a popup or a dialog.
     *
     * @return If the the node opens a popup.
     */
    public boolean canOpenPopup() {
        return mInfo.canOpenPopup();
    }

    /**
     * Sets if this node opens a popup or a dialog.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param opensPopup If the the node opens a popup.
     */
    public void setCanOpenPopup(boolean opensPopup) {
        mInfo.setCanOpenPopup(opensPopup);
    }

    /**
     * Finds {@link AccessibilityNodeInfoCompat}s by the fully qualified view id's resource
     * name where a fully qualified id is of the from "package:id/id_resource_name".
     * For example, if the target application's package is "foo.bar" and the id
     * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz".
     *
     * <p>
     *   <strong>Note:</strong> The primary usage of this API is for UI test automation
     *   and in order to report the fully qualified view id if an
     *   {@link AccessibilityNodeInfoCompat} the client has to set the
     *   {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
     *   flag when configuring their {@link android.accessibilityservice.AccessibilityService}.
     * </p>
     *
     * @param viewId The fully qualified resource name of the view id to find.
     * @return A list of node info.
     */
    @SuppressWarnings("MixedMutabilityReturnType")
    public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(String viewId) {
        List<AccessibilityNodeInfo> nodes = mInfo.findAccessibilityNodeInfosByViewId(viewId);
        List<AccessibilityNodeInfoCompat> result = new ArrayList<>();
        for (AccessibilityNodeInfo node : nodes) {
                result.add(AccessibilityNodeInfoCompat.wrap(node));
            }
        return result;
    }

    /**
     * Gets an optional bundle with extra data. The bundle
     * is lazily created and never <code>null</code>.
     * <p>
     * <strong>Note:</strong> It is recommended to use the package
     * name of your application as a prefix for the keys to avoid
     * collisions which may confuse an accessibility service if the
     * same key has different meaning when emitted from different
     * applications.
     * </p>
     *
     * @return The bundle.
     */
    public Bundle getExtras() {
        return mInfo.getExtras();
    }

    /**
     * Gets the input type of the source as defined by {@link InputType}.
     *
     * @return The input type.
     */
    public int getInputType() {
        return mInfo.getInputType();
    }

    /**
     * Sets the input type of the source as defined by {@link InputType}.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an
     *   AccessibilityService.
     * </p>
     *
     * @param inputType The input type.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setInputType(int inputType) {
        mInfo.setInputType(inputType);
    }

    /**
     * Get the extra data available for this node.
     * <p>
     * Some data that is useful for some accessibility services is expensive to compute, and would
     * place undue overhead on apps to compute all the time. That data can be requested with
     * {@link #refreshWithExtraData(String, Bundle)}.
     *
     * @return An unmodifiable list of keys corresponding to extra data that can be requested.
     * @see #EXTRA_DATA_RENDERING_INFO_KEY
     * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
     */
    @NonNull
    public List<String> getAvailableExtraData() {
        if (Build.VERSION.SDK_INT >= 26) {
            return mInfo.getAvailableExtraData();
        } else {
            return emptyList();
        }
    }

    /**
     * Set the extra data available for this node.
     * <p>
     * <strong>Note:</strong> When a {@code View} passes in a non-empty list, it promises that
     * it will populate the node's extras with corresponding pieces of information in
     * {@link View#addExtraDataToAccessibilityNodeInfo(AccessibilityNodeInfo, String, Bundle)}.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}.
     * This class is made immutable before being delivered to an AccessibilityService.
     *
     * @param extraDataKeys A list of types of extra data that are available.
     * @see #getAvailableExtraData()
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setAvailableExtraData(@NonNull List<String> extraDataKeys) {
        if (Build.VERSION.SDK_INT >= 26) {
            mInfo.setAvailableExtraData(extraDataKeys);
        }
    }

    /**
     * Sets the maximum text length, or -1 for no limit.
     * <p>
     * Typically used to indicate that an editable text field has a limit on
     * the number of characters entered.
     * <p>
     * <strong>Note:</strong> Cannot be called from an
     * {@link android.accessibilityservice.AccessibilityService}.
     * This class is made immutable before being delivered to an AccessibilityService.
     *
     * @param max The maximum text length.
     * @see #getMaxTextLength()
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setMaxTextLength(int max) {
        if (Build.VERSION.SDK_INT >= 21) {
            mInfo.setMaxTextLength(max);
        }
    }

    /**
     * Returns the maximum text length for this node.
     *
     * @return The maximum text length, or -1 for no limit.
     * @see #setMaxTextLength(int)
     */
    public int getMaxTextLength() {
        if (Build.VERSION.SDK_INT >= 21) {
            return mInfo.getMaxTextLength();
        } else {
            return -1;
        }
    }

    /**
     * Sets the text selection start and end.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param start The text selection start.
     * @param end The text selection end.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setTextSelection(int start, int end) {
        mInfo.setTextSelection(start, end);
    }

    /**
     * Gets the text selection start.
     *
     * @return The text selection start if there is selection or -1.
     */
    public int getTextSelectionStart() {
        return mInfo.getTextSelectionStart();
    }

    /**
     * Gets the text selection end.
     *
     * @return The text selection end if there is selection or -1.
     */
    public int getTextSelectionEnd() {
        return mInfo.getTextSelectionEnd();
    }

    /**
     * Gets the node before which this one is visited during traversal. A screen-reader
     * must visit the content of this node before the content of the one it precedes.
     *
     * @return The succeeding node if such or <code>null</code>.
     *
     * @see #setTraversalBefore(android.view.View)
     * @see #setTraversalBefore(android.view.View, int)
     */
    public AccessibilityNodeInfoCompat getTraversalBefore() {
        if (Build.VERSION.SDK_INT >= 22) {
            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getTraversalBefore());
        } else {
            return null;
        }
    }

    /**
     * Sets the view before whose node this one should be visited during traversal. A
     * screen-reader must visit the content of this node before the content of the one
     * it precedes.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param view The view providing the preceding node.
     *
     * @see #getTraversalBefore()
     */
    public void setTraversalBefore(View view) {
        if (Build.VERSION.SDK_INT >= 22) {
            mInfo.setTraversalBefore(view);
        }
    }

    /**
     * Sets the node before which this one is visited during traversal. A screen-reader
     * must visit the content of this node before the content of the one it precedes.
     * The successor is a virtual descendant of the given <code>root</code>. If
     * <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root is set
     * as the successor.
     * <p>
     * A virtual descendant is an imaginary View that is reported as a part of the view
     * hierarchy for accessibility purposes. This enables custom views that draw complex
     * content to report them selves as a tree of virtual views, thus conveying their
     * logical structure.
     * </p>
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param root The root of the virtual subtree.
     * @param virtualDescendantId The id of the virtual descendant.
     */
    public void setTraversalBefore(View root, int virtualDescendantId) {
        if (Build.VERSION.SDK_INT >= 22) {
            mInfo.setTraversalBefore(root, virtualDescendantId);
        }
    }

    /**
     * Gets the node after which this one is visited in accessibility traversal.
     * A screen-reader must visit the content of the other node before the content
     * of this one.
     *
     * @return The succeeding node if such or <code>null</code>.
     *
     * @see #setTraversalAfter(android.view.View)
     * @see #setTraversalAfter(android.view.View, int)
     */
    public AccessibilityNodeInfoCompat getTraversalAfter() {
        if (Build.VERSION.SDK_INT >= 22) {
            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getTraversalAfter());
        } else {
            return null;
        }
    }

    /**
     * Sets the view whose node is visited after this one in accessibility traversal.
     * A screen-reader must visit the content of the other node before the content
     * of this one.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param view The previous view.
     *
     * @see #getTraversalAfter()
     */
    public void setTraversalAfter(View view) {
        if (Build.VERSION.SDK_INT >= 22) {
            mInfo.setTraversalAfter(view);
        }
    }

    /**
     * Sets the node after which this one is visited in accessibility traversal.
     * A screen-reader must visit the content of the other node before the content
     * of this one. If <code>virtualDescendantId</code> equals to {@link View#NO_ID}
     * the root is set as the predecessor.
     * <p>
     * A virtual descendant is an imaginary View that is reported as a part of the view
     * hierarchy for accessibility purposes. This enables custom views that draw complex
     * content to report them selves as a tree of virtual views, thus conveying their
     * logical structure.
     * </p>
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param root The root of the virtual subtree.
     * @param virtualDescendantId The id of the virtual descendant.
     */
    public void setTraversalAfter(View root, int virtualDescendantId) {
        if (Build.VERSION.SDK_INT >= 22) {
            mInfo.setTraversalAfter(root, virtualDescendantId);
        }
    }

    /**
     * Gets the window to which this node belongs.
     *
     * @return The window.
     *
     * @see android.accessibilityservice.AccessibilityService#getWindows()
     */
    public AccessibilityWindowInfoCompat getWindow() {
        if (Build.VERSION.SDK_INT >= 21) {
            return AccessibilityWindowInfoCompat.wrapNonNullInstance(mInfo.getWindow());
        } else {
            return null;
        }
    }

    /**
     * Gets if the node can be dismissed.
     *
     * @return If the node can be dismissed.
     */
    public boolean isDismissable() {
        return mInfo.isDismissable();
    }

    /**
     * Sets if the node can be dismissed.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param dismissable If the node can be dismissed.
     */
    public void setDismissable(boolean dismissable) {
        mInfo.setDismissable(dismissable);
    }

    /**
     * Gets if the node is editable.
     *
     * @return True if the node is editable, false otherwise.
     */
    public boolean isEditable() {
        return mInfo.isEditable();
    }

    /**
     * Sets whether this node is editable.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param editable True if the node is editable.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setEditable(boolean editable) {
        mInfo.setEditable(editable);
    }

    /**
     * Gets if the node is a multi line editable text.
     *
     * @return True if the node is multi line.
     */
    public boolean isMultiLine() {
        return mInfo.isMultiLine();
    }

    /**
     * Sets if the node is a multi line editable text.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param multiLine True if the node is multi line.
     */
    public void setMultiLine(boolean multiLine) {
        mInfo.setMultiLine(multiLine);
    }

    /**
     * Gets the tooltip text of this node.
     *
     * @return The tooltip text.
     */
    @Nullable
    public CharSequence getTooltipText() {
        if (Build.VERSION.SDK_INT >= 28) {
            return mInfo.getTooltipText();
        } else {
            return mInfo.getExtras().getCharSequence(TOOLTIP_TEXT_KEY);
        }
    }

    /**
     * Sets the tooltip text of this node.
     * <p>This method has no effect below API 19</p>
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param tooltipText The tooltip text.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setTooltipText(@Nullable CharSequence tooltipText) {
        if (Build.VERSION.SDK_INT >= 28) {
            mInfo.setTooltipText(tooltipText);
        } else {
            mInfo.getExtras().putCharSequence(TOOLTIP_TEXT_KEY, tooltipText);
        }
    }

    /**
     * If this node represents a visually distinct region of the screen that may update separately
     * from the rest of the window, it is considered a pane. Set the pane title to indicate that
     * the node is a pane, and to provide a title for it.
     * <p>This method has no effect below API 19</p>
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     * @param paneTitle The title of the window represented by this node.
     */
    public void setPaneTitle(@Nullable CharSequence paneTitle) {
        if (Build.VERSION.SDK_INT >= 28) {
            mInfo.setPaneTitle(paneTitle);
        } else {
            mInfo.getExtras().putCharSequence(PANE_TITLE_KEY, paneTitle);
        }
    }

    /**
     * Get the title of the pane represented by this node.
     *
     * @return The title of the pane represented by this node, or {@code null} if this node does
     *         not represent a pane.
     */
    public @Nullable CharSequence getPaneTitle() {
        if (Build.VERSION.SDK_INT >= 28) {
            return mInfo.getPaneTitle();
        } else {
            return mInfo.getExtras().getCharSequence(PANE_TITLE_KEY);
        }
    }

    /**
     * Returns whether the node is explicitly marked as a focusable unit by a screen reader. Note
     * that {@code false} indicates that it is not explicitly marked, not that the node is not
     * a focusable unit. Screen readers should generally use other signals, such as
     * {@link #isFocusable()}, or the presence of text in a node, to determine what should receive
     * focus.
     *
     * @return {@code true} if the node is specifically marked as a focusable unit for screen
     *         readers, {@code false} otherwise.
     */
    public boolean isScreenReaderFocusable() {
        if (Build.VERSION.SDK_INT >= 28) {
            return mInfo.isScreenReaderFocusable();
        }
        return getBooleanProperty(BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE);
    }

    /**
     * Sets whether the node should be considered a focusable unit by a screen reader.
     * <p>This method has no effect below API 19</p>
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param screenReaderFocusable {@code true} if the node is a focusable unit for screen readers,
     *                              {@code false} otherwise.
     */
    public void setScreenReaderFocusable(boolean screenReaderFocusable) {
        if (Build.VERSION.SDK_INT >= 28) {
            mInfo.setScreenReaderFocusable(screenReaderFocusable);
        } else {
            setBooleanProperty(BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE, screenReaderFocusable);
        }
    }

    /**
     * Returns whether the node's text represents a hint for the user to enter text. It should only
     * be {@code true} if the node has editable text.
     *
     * @return {@code true} if the text in the node represents a hint to the user, {@code false}
     * otherwise.
     */
    public boolean isShowingHintText() {
        if (Build.VERSION.SDK_INT >= 26) {
            return mInfo.isShowingHintText();
        }
        return getBooleanProperty(BOOLEAN_PROPERTY_IS_SHOWING_HINT);
    }

    /**
     * Sets whether the node's text represents a hint for the user to enter text. It should only
     * be {@code true} if the node has editable text.
     * <p>This method has no effect below API 19</p>
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param showingHintText {@code true} if the text in the node represents a hint to the user,
     * {@code false} otherwise.
     */
    public void setShowingHintText(boolean showingHintText) {
        if (Build.VERSION.SDK_INT >= 26) {
            mInfo.setShowingHintText(showingHintText);
        } else {
            setBooleanProperty(BOOLEAN_PROPERTY_IS_SHOWING_HINT, showingHintText);
        }
    }

    /**
     * Returns whether node represents a heading.
     * <p><strong>Note:</strong> Returns {@code true} if either {@link #setHeading(boolean)}
     * marks this node as a heading or if the node has a {@link CollectionItemInfoCompat} that marks
     * it as such, to accommodate apps that use the now-deprecated API.</p>
     *
     * @return {@code true} if the node is a heading, {@code false} otherwise.
     */
    @SuppressWarnings("deprecation")
    public boolean isHeading() {
        if (Build.VERSION.SDK_INT >= 28) {
            return mInfo.isHeading();
        }
        if (getBooleanProperty(BOOLEAN_PROPERTY_IS_HEADING)) return true;
        CollectionItemInfoCompat collectionItemInfo = getCollectionItemInfo();
        return (collectionItemInfo != null) && collectionItemInfo.isHeading();
    }

    /**
     * Sets whether the node represents a heading.
     * <p>This method has no effect below API 19</p>
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param isHeading {@code true} if the node is a heading, {@code false} otherwise.
     */
    public void setHeading(boolean isHeading) {
        if (Build.VERSION.SDK_INT >= 28) {
            mInfo.setHeading(isHeading);
        } else {
            setBooleanProperty(BOOLEAN_PROPERTY_IS_HEADING, isHeading);
        }
    }

    /**
     * Returns whether node represents a text entry key that is part of a keyboard or keypad.
     *
     * @return {@code true} if the node is a text entry key, {@code false} otherwise.
     */
    public boolean isTextEntryKey() {
        if (Build.VERSION.SDK_INT >= 29) {
            return mInfo.isTextEntryKey();
        }
        return getBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY);
    }

    /**
     * Sets whether the node represents a text entry key that is part of a keyboard or keypad.
     * <p>This method has no effect below API 19</p>
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param isTextEntryKey {@code true} if the node is a text entry key, {@code false} otherwise.
     */
    public void setTextEntryKey(boolean isTextEntryKey) {
        if (Build.VERSION.SDK_INT >= 29) {
            mInfo.setTextEntryKey(isTextEntryKey);
        } else {
            setBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY, isTextEntryKey);
        }
    }

    /**
     * Gets whether the node has {@link #setRequestInitialAccessibilityFocus}.
     *
     * @return True if the node has requested initial accessibility focus.
     */
    @SuppressLint("KotlinPropertyAccess")
    public boolean hasRequestInitialAccessibilityFocus() {
        if (Build.VERSION.SDK_INT >= 34) {
            return Api34Impl.hasRequestInitialAccessibilityFocus(mInfo);
        } else {
            return getBooleanProperty(BOOLEAN_PROPERTY_HAS_REQUEST_INITIAL_ACCESSIBILITY_FOCUS);
        }
    }

    /**
     * Sets whether the node has requested initial accessibility focus.
     *
     * <p>
     * If the node {@link #hasRequestInitialAccessibilityFocus}, this node would be one of
     * candidates to be accessibility focused when the window appears.
     * </p>
     *
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param requestInitialAccessibilityFocus True if the node requests to receive initial
     *                                         accessibility focus.
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    @SuppressLint("GetterSetterNames")
    public void setRequestInitialAccessibilityFocus(boolean requestInitialAccessibilityFocus) {
        if (Build.VERSION.SDK_INT >= 34) {
            Api34Impl.setRequestInitialAccessibilityFocus(mInfo, requestInitialAccessibilityFocus);
        } else {
            setBooleanProperty(BOOLEAN_PROPERTY_HAS_REQUEST_INITIAL_ACCESSIBILITY_FOCUS,
                    requestInitialAccessibilityFocus);
        }
    }

    /**
     * Refreshes this info with the latest state of the view it represents.
     * <p>
     * <strong>Note:</strong> If this method returns false this info is obsolete
     * since it represents a view that is no longer in the view tree.
     * </p>
     * @return Whether the refresh succeeded.
     */
    public boolean refresh() {
        return mInfo.refresh();
    }

    /**
     * Gets the custom role description.
     * @return The role description.
     */
    public @Nullable CharSequence getRoleDescription() {
        return mInfo.getExtras().getCharSequence(ROLE_DESCRIPTION_KEY);
    }

    /**
     * Sets the custom role description.
     *
     * <p>
     *   The role description allows you to customize the name for the view's semantic
     *   role. For example, if you create a custom subclass of {@link android.view.View}
     *   to display a menu bar, you could assign it the role description of "menu bar".
     * </p>
     * <p>
     *   <strong>Warning:</strong> For consistency with other applications, you should
     *   not use the role description to force accessibility services to describe
     *   standard views (such as buttons or checkboxes) using specific wording. For
     *   example, you should not set a role description of "check box" or "tick box" for
     *   a standard {@link android.widget.CheckBox}. Instead let accessibility services
     *   decide what feedback to provide.
     * </p>
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     * </p>
     *
     * @param roleDescription The role description.
     */
    public void setRoleDescription(@Nullable CharSequence roleDescription) {
        mInfo.getExtras().putCharSequence(ROLE_DESCRIPTION_KEY, roleDescription);
    }

    /**
     * Get the {@link TouchDelegateInfoCompat} for touch delegate behavior with the represented
     * view. It is possible for the same node to be pointed to by several regions. Use
     * {@link TouchDelegateInfoCompat#getRegionAt(int)} to get touch delegate target
     * {@link Region}, and {@link TouchDelegateInfoCompat#getTargetForRegion(Region)}
     * for {@link AccessibilityNodeInfoCompat} from the given region.
     * <p>
     * Compatibility:
     * <ul>
     *     <li>API &lt; 29: Always returns {@code null}</li>
     * </ul>
     *
     * @return {@link TouchDelegateInfoCompat} or {@code null} if there are no touch delegates
     * in this node.
     */
    @Nullable
    public TouchDelegateInfoCompat getTouchDelegateInfo() {
        if (Build.VERSION.SDK_INT >= 29) {
            TouchDelegateInfo delegateInfo = mInfo.getTouchDelegateInfo();
            if (delegateInfo != null) {
                return new TouchDelegateInfoCompat(delegateInfo);
            }
        }
        return null;
    }

    /**
     * Set touch delegate info if the represented view has a {@link android.view.TouchDelegate}.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an
     *   AccessibilityService.
     * </p>
     * <p>
     * Compatibility:
     * <ul>
     *     <li>API &lt; 29: No-op</li>
     * </ul>
     *
     * @param delegatedInfo {@link TouchDelegateInfoCompat}
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setTouchDelegateInfo(@NonNull TouchDelegateInfoCompat delegatedInfo) {
        if (Build.VERSION.SDK_INT >= 29) {
            mInfo.setTouchDelegateInfo(delegatedInfo.mInfo);
        }
    }

    /**
     * Connects this node to the View's root so that operations on this node can query the entire
     * {@link AccessibilityNodeInfoCompat} tree and perform accessibility actions on nodes.
     *
     * <p>
     * Testing or debugging tools should create this {@link AccessibilityNodeInfoCompat} node using
     * {@link ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)}
     * or {@link AccessibilityNodeProviderCompat} and call this
     * method, then navigate and interact with the node tree by calling methods on the node.
     * Calling this method more than once on the same node is a no-op. After calling this method,
     * all nodes linked to this node (children, ancestors, etc.) are also queryable.
     * </p>
     *
     * <p>
     * Here "query" refers to the following node operations:
     * <ul>
     *      <li>check properties of this node (example: {@link #isScrollable()})</li>
     *      <li>find and query children (example: {@link #getChild(int)})</li>
     *      <li>find and query the parent (example: {@link #getParent()})</li>
     *      <li>find focus (examples: {@link #findFocus(int)}, {@link #focusSearch(int)})</li>
     *      <li>find and query other nodes (example:
     *      {@link #findAccessibilityNodeInfosByText(String)},
     *      {@link #findAccessibilityNodeInfosByViewId(String)})</li>
     *      <li>perform actions (example: {@link #performAction(int)})</li>
     * </ul>
     * </p>
     *
     * <p>
     * This is intended for short-lived inspections from testing or debugging tools in the app
     * process, as operations on this node tree will only succeed as long as the associated
     * view hierarchy remains attached to a window. {@link AccessibilityNodeInfoCompat} objects can
     * quickly become out of sync with their corresponding {@link View} objects; if you wish to
     * inspect a changed or different view hierarchy then create a new node from any view in that
     * hierarchy and call this method on that new node, instead of disabling & re-enabling the
     * connection on the previous node.
     * </p>
     * <p>
     * Compatibility:
     * <ul>
     *     <li>API &lt; 34: No-op</li>
     * </ul>
     *
     * @param view The view that generated this node, or any view in the same view-root hierarchy.
     * @param enabled Whether to enable (true) or disable (false) querying from the app process.
     * @throws IllegalStateException If called from an {@link AccessibilityService}, or if provided
     *                               a {@link View} that is not attached to a window.
     */
    public void setQueryFromAppProcessEnabled(@NonNull View view, boolean enabled) {
        if (Build.VERSION.SDK_INT >= 34) {
            Api34Impl.setQueryFromAppProcessEnabled(mInfo, view, enabled);
        }
    }

    @Override
    public int hashCode() {
        return (mInfo == null) ? 0 : mInfo.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof AccessibilityNodeInfoCompat)) {
            return false;
        }
        AccessibilityNodeInfoCompat other = (AccessibilityNodeInfoCompat) obj;
        if (mInfo == null) {
            if (other.mInfo != null) {
                return false;
            }
        } else if (!mInfo.equals(other.mInfo)) {
            return false;
        }
        if (mVirtualDescendantId != other.mVirtualDescendantId) {
            return false;
        }
        if (mParentVirtualDescendantId != other.mParentVirtualDescendantId) {
            return false;
        }
        return true;
    }

    @SuppressWarnings("deprecation")
    @NonNull
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(super.toString());

        Rect bounds = new Rect();

        getBoundsInParent(bounds);
        builder.append("; boundsInParent: " + bounds);

        getBoundsInScreen(bounds);
        builder.append("; boundsInScreen: " + bounds);

        getBoundsInWindow(bounds);
        builder.append("; boundsInWindow: " + bounds);

        builder.append("; packageName: ").append(getPackageName());
        builder.append("; className: ").append(getClassName());
        builder.append("; text: ").append(getText());
        builder.append("; error: ").append(getError());
        builder.append("; maxTextLength: ").append(getMaxTextLength());
        builder.append("; stateDescription: ").append(getStateDescription());
        builder.append("; contentDescription: ").append(getContentDescription());
        builder.append("; tooltipText: ").append(getTooltipText());
        builder.append("; viewIdResName: ").append(getViewIdResourceName());
        builder.append("; uniqueId: ").append(getUniqueId());

        builder.append("; checkable: ").append(isCheckable());
        builder.append("; checked: ").append(isChecked());
        builder.append("; focusable: ").append(isFocusable());
        builder.append("; focused: ").append(isFocused());
        builder.append("; selected: ").append(isSelected());
        builder.append("; clickable: ").append(isClickable());
        builder.append("; longClickable: ").append(isLongClickable());
        builder.append("; contextClickable: ").append(isContextClickable());
        builder.append("; enabled: ").append(isEnabled());
        builder.append("; password: ").append(isPassword());
        builder.append("; scrollable: " + isScrollable());
        builder.append("; containerTitle: ").append(getContainerTitle());
        builder.append("; granularScrollingSupported: ").append(isGranularScrollingSupported());
        builder.append("; importantForAccessibility: ").append(isImportantForAccessibility());
        builder.append("; visible: ").append(isVisibleToUser());
        builder.append("; isTextSelectable: ").append(isTextSelectable());
        builder.append("; accessibilityDataSensitive: ").append(isAccessibilityDataSensitive());

        builder.append("; [");
        if (Build.VERSION.SDK_INT >= 21) {
            List<AccessibilityActionCompat> actions = getActionList();
            for (int i = 0; i < actions.size(); i++) {
                AccessibilityActionCompat action = actions.get(i);
                String actionName = getActionSymbolicName(action.getId());
                if (actionName.equals("ACTION_UNKNOWN") && action.getLabel() != null) {
                    actionName = action.getLabel().toString();
                }
                builder.append(actionName);
                if (i != actions.size() - 1) {
                    builder.append(", ");
                }
            }
        } else {
            for (int actionBits = getActions(); actionBits != 0;) {
                final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
                actionBits &= ~action;
                builder.append(getActionSymbolicName(action));
                if (actionBits != 0) {
                    builder.append(", ");
                }
            }
        }
        builder.append("]");

        return builder.toString();
    }

    private void setBooleanProperty(int property, boolean value) {
        Bundle extras = getExtras();
        if (extras != null) {
            int booleanProperties = extras.getInt(BOOLEAN_PROPERTY_KEY, 0);
            booleanProperties &= ~property;
            booleanProperties |= value ? property : 0;
            extras.putInt(BOOLEAN_PROPERTY_KEY, booleanProperties);
        }
    }

    private boolean getBooleanProperty(int property) {
        Bundle extras = getExtras();
        if (extras == null) return false;
        return (extras.getInt(BOOLEAN_PROPERTY_KEY, 0) & property) == property;
    }

    static String getActionSymbolicName(int action) {
        switch (action) {
            case ACTION_FOCUS:
                return "ACTION_FOCUS";
            case ACTION_CLEAR_FOCUS:
                return "ACTION_CLEAR_FOCUS";
            case ACTION_SELECT:
                return "ACTION_SELECT";
            case ACTION_CLEAR_SELECTION:
                return "ACTION_CLEAR_SELECTION";
            case ACTION_CLICK:
                return "ACTION_CLICK";
            case ACTION_LONG_CLICK:
                return "ACTION_LONG_CLICK";
            case ACTION_ACCESSIBILITY_FOCUS:
                return "ACTION_ACCESSIBILITY_FOCUS";
            case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
                return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
            case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
                return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
            case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
                return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
            case ACTION_NEXT_HTML_ELEMENT:
                return "ACTION_NEXT_HTML_ELEMENT";
            case ACTION_PREVIOUS_HTML_ELEMENT:
                return "ACTION_PREVIOUS_HTML_ELEMENT";
            case ACTION_SCROLL_FORWARD:
                return "ACTION_SCROLL_FORWARD";
            case ACTION_SCROLL_BACKWARD:
                return "ACTION_SCROLL_BACKWARD";
            case ACTION_CUT:
                return "ACTION_CUT";
            case ACTION_COPY:
                return "ACTION_COPY";
            case ACTION_PASTE:
                return "ACTION_PASTE";
            case ACTION_SET_SELECTION:
                return "ACTION_SET_SELECTION";
            case ACTION_EXPAND:
                return "ACTION_EXPAND";
            case ACTION_COLLAPSE:
                return "ACTION_COLLAPSE";
            case ACTION_SET_TEXT:
                return "ACTION_SET_TEXT";
            case android.R.id.accessibilityActionScrollUp:
                return "ACTION_SCROLL_UP";
            case android.R.id.accessibilityActionScrollLeft:
                return "ACTION_SCROLL_LEFT";
            case android.R.id.accessibilityActionScrollDown:
                return "ACTION_SCROLL_DOWN";
            case android.R.id.accessibilityActionScrollRight:
                return "ACTION_SCROLL_RIGHT";
            case android.R.id.accessibilityActionPageDown:
                return "ACTION_PAGE_DOWN";
            case android.R.id.accessibilityActionPageUp:
                return "ACTION_PAGE_UP";
            case android.R.id.accessibilityActionPageLeft:
                return "ACTION_PAGE_LEFT";
            case android.R.id.accessibilityActionPageRight:
                return "ACTION_PAGE_RIGHT";
            case android.R.id.accessibilityActionShowOnScreen:
                return "ACTION_SHOW_ON_SCREEN";
            case android.R.id.accessibilityActionScrollToPosition:
                return "ACTION_SCROLL_TO_POSITION";
            case android.R.id.accessibilityActionContextClick:
                return "ACTION_CONTEXT_CLICK";
            case android.R.id.accessibilityActionSetProgress:
                return "ACTION_SET_PROGRESS";
            case android.R.id.accessibilityActionMoveWindow:
                return "ACTION_MOVE_WINDOW";
            case android.R.id.accessibilityActionShowTooltip:
                return "ACTION_SHOW_TOOLTIP";
            case android.R.id.accessibilityActionHideTooltip:
                return "ACTION_HIDE_TOOLTIP";
            case android.R.id.accessibilityActionPressAndHold:
                return "ACTION_PRESS_AND_HOLD";
            case android.R.id.accessibilityActionImeEnter:
                return "ACTION_IME_ENTER";
            case android.R.id.accessibilityActionDragStart:
                return "ACTION_DRAG_START";
            case android.R.id.accessibilityActionDragDrop:
                return "ACTION_DRAG_DROP";
            case android.R.id.accessibilityActionDragCancel:
                return "ACTION_DRAG_CANCEL";
            case android.R.id.accessibilityActionScrollInDirection:
                return "ACTION_SCROLL_IN_DIRECTION";
            default:
                return "ACTION_UNKNOWN";
        }
    }

    @RequiresApi(21)
    private static class Api21Impl {
        private Api21Impl() {
            // This class is non instantiable.
        }

        public static CollectionItemInfoCompat createCollectionItemInfo(int rowIndex, int rowSpan,
                int columnIndex, int columnSpan, boolean heading, boolean selected) {
            return new CollectionItemInfoCompat(
                    AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndex, rowSpan, columnIndex,
                            columnSpan, heading, selected));
        }
    }

    @RequiresApi(30)
    private static class Api30Impl {
        private Api30Impl() {
            // This class is non instantiable.
        }

        public static void setStateDescription(AccessibilityNodeInfo info,
                CharSequence stateDescription) {
            info.setStateDescription(stateDescription);
        }

        public static CharSequence getStateDescription(AccessibilityNodeInfo info) {
            return info.getStateDescription();
        }

        public static Object createRangeInfo(int type, float min, float max, float current) {
            return new AccessibilityNodeInfo.RangeInfo(type, min, max, current);
        }
    }

    @RequiresApi(33)
    private static class Api33Impl {
        private Api33Impl() {
            // This class is non instantiable.
        }

        public static AccessibilityNodeInfo.ExtraRenderingInfo getExtraRenderingInfo(
                AccessibilityNodeInfo info) {
            return info.getExtraRenderingInfo();
        }

        public static boolean isTextSelectable(AccessibilityNodeInfo info) {
            return info.isTextSelectable();
        }

        public static void setTextSelectable(AccessibilityNodeInfo info, boolean selectable) {
            info.setTextSelectable(selectable);
        }

        public static CollectionItemInfoCompat buildCollectionItemInfoCompat(
                boolean heading, int columnIndex, int rowIndex, int columnSpan,
                int rowSpan, boolean selected, String rowTitle, String columnTitle) {
            return new CollectionItemInfoCompat(
                    new AccessibilityNodeInfo.CollectionItemInfo.Builder()
                    .setHeading(heading).setColumnIndex(columnIndex)
                    .setRowIndex(rowIndex)
                    .setColumnSpan(columnSpan)
                    .setRowSpan(rowSpan)
                    .setSelected(selected)
                    .setRowTitle(rowTitle)
                    .setColumnTitle(columnTitle)
                    .build());
        }

        public static AccessibilityNodeInfoCompat getChild(AccessibilityNodeInfo info, int index,
                int prefetchingStrategy) {
            return AccessibilityNodeInfoCompat.wrapNonNullInstance(info.getChild(index,
                    prefetchingStrategy));
        }

        public static AccessibilityNodeInfoCompat getParent(AccessibilityNodeInfo info,
                int prefetchingStrategy) {
            return AccessibilityNodeInfoCompat.wrapNonNullInstance(info.getParent(
                    prefetchingStrategy));
        }

        public static String getUniqueId(AccessibilityNodeInfo info) {
            return info.getUniqueId();
        }

        public static void setUniqueId(AccessibilityNodeInfo info, String uniqueId) {
            info.setUniqueId(uniqueId);
        }

        public static String getCollectionItemRowTitle(Object info) {
            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getRowTitle();

        }

        public static String getCollectionItemColumnTitle(Object info) {
            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getColumnTitle();
        }
    }

    @RequiresApi(34)
    private static class Api34Impl {
        private Api34Impl() {
            // This class is non instantiable.
        }

        public static boolean isAccessibilityDataSensitive(AccessibilityNodeInfo info) {
            return info.isAccessibilityDataSensitive();
        }

        public static void setAccessibilityDataSensitive(AccessibilityNodeInfo info,
                boolean accessibilityDataSensitive) {
            info.setAccessibilityDataSensitive(accessibilityDataSensitive);
        }

        public static CharSequence getContainerTitle(AccessibilityNodeInfo info) {
            return info.getContainerTitle();
        }

        public static void setContainerTitle(AccessibilityNodeInfo info,
                CharSequence containerTitle) {
            info.setContainerTitle(containerTitle);
        }

        public static void getBoundsInWindow(AccessibilityNodeInfo info, Rect bounds) {
            info.getBoundsInWindow(bounds);
        }

        public static void setBoundsInWindow(AccessibilityNodeInfo info, Rect bounds) {
            info.setBoundsInWindow(bounds);
        }

        public static boolean hasRequestInitialAccessibilityFocus(AccessibilityNodeInfo info) {
            return info.hasRequestInitialAccessibilityFocus();
        }

        public static void setRequestInitialAccessibilityFocus(AccessibilityNodeInfo info,
                boolean requestInitialAccessibilityFocus) {
            info.setRequestInitialAccessibilityFocus(requestInitialAccessibilityFocus);
        }

        public static long getMinDurationBetweenContentChangeMillis(AccessibilityNodeInfo info) {
            return info.getMinDurationBetweenContentChanges().toMillis();
        }

        public static void setMinDurationBetweenContentChangeMillis(AccessibilityNodeInfo info,
                long duration) {
            info.setMinDurationBetweenContentChanges(Duration.ofMillis(duration));
        }

        public static void setQueryFromAppProcessEnabled(AccessibilityNodeInfo info, View view,
                boolean enabled) {
            info.setQueryFromAppProcessEnabled(view, enabled);
        }

        public static AccessibilityNodeInfo.AccessibilityAction getActionScrollInDirection() {
            return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_IN_DIRECTION;
        }
    }
}