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.9.0-alpha04'

  • groupId: androidx.core
  • artifactId: core
  • version: 1.9.0-alpha04

Artifact androidx.core:core:1.9.0-alpha04 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_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_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 dismissable 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 intFOCUS_ACCESSIBILITY

The accessibility focus.

public static final intFOCUS_INPUT

The input focus.

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 AccessibilityNodeInfoCompatgetChild(int index)

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.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 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 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 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 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 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 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 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 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 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 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 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 voidsetRangeInfo(AccessibilityNodeInfoCompat.RangeInfoCompat rangeInfo)

Sets the range info if this node is a range.

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 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.

public static final int ACTION_CLEAR_FOCUS

Action that unfocuses the node.

public static final int ACTION_SELECT

Action that selects the node.

public static final int ACTION_CLEAR_SELECTION

Action that unselects the node.

public static final int ACTION_CLICK

Action that clicks on the node info.

public static final int ACTION_LONG_CLICK

Action that long clicks on the node.

public static final int ACTION_ACCESSIBILITY_FOCUS

Action that gives accessibility focus to the node.

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

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

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);

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);

public static final int ACTION_SCROLL_FORWARD

Action to scroll the node content forward.

public static final int ACTION_SCROLL_BACKWARD

Action to scroll the node content backward.

public static final int ACTION_COPY

Action to copy the current selection to the clipboard.

public static final int ACTION_PASTE

Action to paste the current clipboard content.

public static final int ACTION_CUT

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

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

public static final int ACTION_EXPAND

Action to expand an expandable node.

public static final int ACTION_COLLAPSE

Action to collapse an expandable node.

public static final int ACTION_DISMISS

Action to dismiss a dismissable node.

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);

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.ACTION_NEXT_HTML_ELEMENT, AccessibilityNodeInfoCompat.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.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, AccessibilityNodeInfoCompat.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.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.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.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 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.

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.

Note: It is a client responsibility to recycle the received info by calling AccessibilityNodeInfoCompat.recycle() to avoid creating of multiple instances.

Parameters:

index: The child index.

Returns:

The child node.

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()

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.

Note: It is a client responsibility to recycle the received info by calling android.view.accessibility.AccessibilityNodeInfo to avoid creating of multiple instances.

Parameters:

text: The searched text.

Returns:

A list of node info.

public AccessibilityNodeInfoCompat getParent()

Gets the parent.

Note: It is a client responsibility to recycle the received info by calling android.view.accessibility.AccessibilityNodeInfo to avoid creating of multiple instances.

Returns:

The parent.

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 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 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 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 recycle()

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 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.

Note: It is a client responsibility to recycle the received info by calling AccessibilityNodeInfoCompat.recycle() to avoid creating of multiple instances.

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.

Note: It is a client responsibility to recycle the received info by calling AccessibilityNodeInfoCompat.recycle() to avoid creating of multiple instances.

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: It is a client responsibility to recycle the received info by calling AccessibilityNodeInfoCompat.recycle() to avoid creating of multiple instances.

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

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 accomodate 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 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 and should be recycled.

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 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.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.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.RestrictTo;
import androidx.core.R;
import androidx.core.accessibilityservice.AccessibilityServiceInfoCompat;
import androidx.core.os.BuildCompat;
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.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. For each standard action
     * there is a static constant 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 View#setImportantForAccessibility(int)} with
     * {@link View#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.
         */
        public static final AccessibilityActionCompat ACTION_FOCUS =
                new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_FOCUS, null);

        /**
         * Action that clears input focus of the node.
         */
        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.
         */
        public static final AccessibilityActionCompat ACTION_CLICK =
                new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_CLICK, null);

        /**
         * Action that long clicks on the node.
         */
        public static final AccessibilityActionCompat ACTION_LONG_CLICK =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_LONG_CLICK, null);

        /**
         * Action that gives accessibility focus to the node.
         */
        public static final AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS =
                new AccessibilityActionCompat(
                        AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null);

        /**
         * Action that clears accessibility focus of the node.
         */
        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>
         *
         * @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>
         */
        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.
         */
        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);

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

        /**
         * @hide
         */
        @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
         * @hide
         */
        @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.
         * @hide
         */
        @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;
        }

        /**
         * @hide
         */
        @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;
        }
    }

    /**
     * Class with information if a node is a collection.
     * <p>
     * A collection of items has rows and columns and may be hierarchical.
     * For example, a horizontal list is a collection with one column, as
     * many rows as the list items, and is not hierarchical; A table is a
     * collection with several rows, several columns, and is not hierarchical;
     * A vertical tree is a hierarchical collection with one column and
     * as many rows as the first level children.
     * </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 if (Build.VERSION.SDK_INT >= 19) {
                return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain(
                        rowCount, columnCount, hierarchical));
            } else {
                return new CollectionInfoCompat(null);
            }
        }

        /**
         * 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) {
            if (Build.VERSION.SDK_INT >= 19) {
                return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain(
                        rowCount, columnCount, hierarchical));
            } else {
                return new CollectionInfoCompat(null);
            }
        }

        CollectionInfoCompat(Object info) {
            mInfo = info;
        }

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

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

        /**
         * Gets if the collection is a hierarchically ordered.
         *
         * @return Whether the collection is hierarchical.
         */
        public boolean isHierarchical() {
            if (Build.VERSION.SDK_INT >= 19) {
                return ((AccessibilityNodeInfo.CollectionInfo) mInfo).isHierarchical();
            } else {
                return false;
            }
        }

        /**
         * 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 if (Build.VERSION.SDK_INT >= 19) {
                return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain(
                        rowIndex, rowSpan, columnIndex, columnSpan, heading));
            } else {
                return new CollectionItemInfoCompat(null);
            }
        }

        /**
         * 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) {
            if (Build.VERSION.SDK_INT >= 19) {
                return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain(
                        rowIndex, rowSpan, columnIndex, columnSpan, heading));
            } else {
                return new CollectionItemInfoCompat(null);
            }
        }

        CollectionItemInfoCompat(Object info) {
            mInfo = info;
        }

        /**
         * Gets the column index at which the item is located.
         *
         * @return The column index.
         */
        public int getColumnIndex() {
            if (Build.VERSION.SDK_INT >= 19) {
                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getColumnIndex();
            } else {
                return 0;
            }
        }

        /**
         * Gets the number of columns the item spans.
         *
         * @return The column span.
         */
        public int getColumnSpan() {
            if (Build.VERSION.SDK_INT >= 19) {
                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getColumnSpan();
            } else {
                return 0;
            }
        }

        /**
         * Gets the row index at which the item is located.
         *
         * @return The row index.
         */
        public int getRowIndex() {
            if (Build.VERSION.SDK_INT >= 19) {
                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getRowIndex();
            } else {
                return 0;
            }
        }

        /**
         * Gets the number of rows the item spans.
         *
         * @return The row span.
         */
        public int getRowSpan() {
            if (Build.VERSION.SDK_INT >= 19) {
                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getRowSpan();
            } else {
                return 0;
            }
        }

        /**
         * 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() {
            if (Build.VERSION.SDK_INT >= 19) {
                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).isHeading();
            } else {
                return false;
            }
        }

        /**
         * 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;
            }
        }
    }

    /**
     * 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) {
            if (Build.VERSION.SDK_INT >= 19) {
                return new RangeInfoCompat(
                        AccessibilityNodeInfo.RangeInfo.obtain(type, min, max, current));
            } else {
                return new RangeInfoCompat(null);
            }
        }

        final Object mInfo;

        RangeInfoCompat(Object info) {
            mInfo = info;
        }

        /**
         * Gets the current value.
         *
         * @return The current value.
         */
        public float getCurrent() {
            if (Build.VERSION.SDK_INT >= 19) {
                return ((AccessibilityNodeInfo.RangeInfo) mInfo).getCurrent();
            } else {
                return 0;
            }
        }

        /**
         * Gets the max value.
         *
         * @return The max value.
         */
        public float getMax() {
            if (Build.VERSION.SDK_INT >= 19) {
                return ((AccessibilityNodeInfo.RangeInfo) mInfo).getMax();
            } else {
                return 0;
            }
        }

        /**
         * Gets the min value.
         *
         * @return The min value.
         */
        public float getMin() {
            if (Build.VERSION.SDK_INT >= 19) {
                return ((AccessibilityNodeInfo.RangeInfo) mInfo).getMin();
            } else {
                return 0;
            }
        }

        /**
         * Gets the range type.
         *
         * @return The range type.
         *
         * @see #RANGE_TYPE_INT
         * @see #RANGE_TYPE_FLOAT
         * @see #RANGE_TYPE_PERCENT
         */
        public int getType() {
            if (Build.VERSION.SDK_INT >= 19) {
                return ((AccessibilityNodeInfo.RangeInfo) mInfo).getType();
            } else {
                return RANGE_TYPE_INT;
            }
        }
    }

    /**
     * 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>
         *   <strong>Note:</strong> It is a client responsibility to recycle the
         *     received info by calling {@link AccessibilityNodeInfo#recycle()}
         *     to avoid creating of multiple instances.
         * </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";

    // 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 final AccessibilityNodeInfo mInfo;

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

    private int mVirtualDescendantId = NO_ID;

    // Actions introduced in IceCreamSandwich

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

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

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

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

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

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

    // Actions introduced in JellyBean

    /**
     * Action that gives accessibility focus to the node.
     */
    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
     */
    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
     */
    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>
     */
    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>
     */
    public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;

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

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

    // Actions introduced in JellyBeanMr2

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

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

    /**
     * Action to cut the current selection and place it to the clipboard.
     */
    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
     */
    public static final int ACTION_SET_SELECTION = 0x00020000;

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

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

    /**
     * Action to dismiss a dismissable node.
     */
    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>
     */
    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 #ACTION_NEXT_HTML_ELEMENT},
     *         {@link #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 #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
     * @see #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 #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 #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 #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";

    // 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;

    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) {
        if (Build.VERSION.SDK_INT >= 16) {
            return AccessibilityNodeInfoCompat.wrapNonNullInstance(
                    AccessibilityNodeInfo.obtain(root, virtualDescendantId));
        } else {
            return null;
        }
    }

    /**
     * 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;

        if (Build.VERSION.SDK_INT >= 16) {
            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) {
        if (Build.VERSION.SDK_INT >= 16) {
            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.findFocus(focus));
        } else {
            return null;
        }
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 16) {
            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.focusSearch(direction));
        } else {
            return null;
        }
    }

    /**
     * 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.
     * <p>
     * <strong>Note:</strong> It is a client responsibility to recycle the
     * received info by calling {@link AccessibilityNodeInfoCompat#recycle()} to
     * avoid creating of multiple instances.
     * </p>
     *
     * @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));
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 16) {
            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
     */
    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) {
        if (Build.VERSION.SDK_INT < 19) {
            return new ArrayList<Integer>();
        }
        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) {
        if (Build.VERSION.SDK_INT >= 16) {
            return mInfo.performAction(action, arguments);
        } else {
            return false;
        }
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 16) {
            mInfo.setMovementGranularities(granularities);
        }
    }

    /**
     * Gets the movement granularities for traversing the text of this node.
     *
     * @return The bit mask with granularities.
     */
    public int getMovementGranularities() {
        if (Build.VERSION.SDK_INT >= 16) {
            return mInfo.getMovementGranularities();
        } else {
            return 0;
        }
    }

    /**
     * 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.
     * <p>
     * <strong>Note:</strong> It is a client responsibility to recycle the
     * received info by calling {@link android.view.accessibility.AccessibilityNodeInfo#recycle()}
     * to avoid creating of multiple instances.
     * </p>
     *
     * @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.
     * <p>
     * <strong>Note:</strong> It is a client responsibility to recycle the
     * received info by calling {@link android.view.accessibility.AccessibilityNodeInfo#recycle()}
     * to avoid creating of multiple instances.
     * </p>
     *
     * @return The parent.
     */
    public AccessibilityNodeInfoCompat getParent() {
        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.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;

        if (Build.VERSION.SDK_INT >= 16) {
            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 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() {
        if (Build.VERSION.SDK_INT >= 16) {
            return mInfo.isVisibleToUser();
        } else {
            return false;
        }
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 16) {
            mInfo.setVisibleToUser(visibleToUser);
        }
    }

    /**
     * Gets whether this node is accessibility focused.
     *
     * @return True if the node is accessibility focused.
     */
    public boolean isAccessibilityFocused() {
        if (Build.VERSION.SDK_INT >= 16) {
            return mInfo.isAccessibilityFocused();
        } else {
            return false;
        }
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 16) {
            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);
    }

    /**
     * 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 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);
    }

    /**
     * @hide
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public void addSpansToExtras(CharSequence text, View view) {
        if (Build.VERSION.SDK_INT >= 19 && 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);
    }

    /**
     * @hide
     */
    @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() {
        if (Build.VERSION.SDK_INT >= 19) {
            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 (BuildCompat.isAtLeastR()) {
            return mInfo.getStateDescription();
        } else if (Build.VERSION.SDK_INT >= 19) {
            return mInfo.getExtras().getCharSequence(STATE_DESCRIPTION_KEY);
        }
        return null;
    }

    /**
     * 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 (BuildCompat.isAtLeastR()) {
            mInfo.setStateDescription(stateDescription);
        } else if (Build.VERSION.SDK_INT >= 19) {
            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.
     */
    @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
    public @Nullable String getUniqueId() {
        if (BuildCompat.isAtLeastT()) {
            return mInfo.getUniqueId();
        } else if (Build.VERSION.SDK_INT >= 19) {
            return mInfo.getExtras().getString(UNIQUE_ID_KEY);
        }
        return null;
    }

    /**
     * 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.
     */
    @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
    public void setUniqueId(@Nullable String uniqueId) {
        if (BuildCompat.isAtLeastT()) {
            mInfo.setUniqueId(uniqueId);
        } else if (Build.VERSION.SDK_INT >= 19) {
            mInfo.getExtras().putString(UNIQUE_ID_KEY, uniqueId);
        }
    }

    /**
     * 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.
     */
    public void recycle() {
        mInfo.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) {
        if (Build.VERSION.SDK_INT >= 18) {
            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() {
        if (Build.VERSION.SDK_INT >= 18) {
            return mInfo.getViewIdResourceName();
        } else {
            return null;
        }
    }

    /**
     * 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() {
        if (Build.VERSION.SDK_INT >= 19) {
            return mInfo.getLiveRegion();
        } else {
            return ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE;
        }
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 19) {
            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() {
        if (Build.VERSION.SDK_INT >= 19) {
            AccessibilityNodeInfo.CollectionInfo info = mInfo.getCollectionInfo();
            if (info != null) {
                return new CollectionInfoCompat(info);
            }
        }
        return null;
    }

    public void setCollectionInfo(Object collectionInfo) {
        if (Build.VERSION.SDK_INT >= 19) {
            mInfo.setCollectionInfo((collectionInfo == null) ? null
                    : (AccessibilityNodeInfo.CollectionInfo) ((CollectionInfoCompat)
                            collectionInfo).mInfo);
        }

    }

    public void setCollectionItemInfo(Object collectionItemInfo) {
        if (Build.VERSION.SDK_INT >= 19) {
            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() {
        if (Build.VERSION.SDK_INT >= 19) {
            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() {
        if (Build.VERSION.SDK_INT >= 19) {
            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) {
        if (Build.VERSION.SDK_INT >= 19) {
            mInfo.setRangeInfo((AccessibilityNodeInfo.RangeInfo) rangeInfo.mInfo);
        }
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 19) {
            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() {
        if (Build.VERSION.SDK_INT >= 19) {
            return mInfo.isContentInvalid();
        } else {
            return false;
        }
    }

    /**
     * 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 if (Build.VERSION.SDK_INT >= 19) {
            return mInfo.getExtras().getCharSequence(HINT_TEXT_KEY);
        }
        return null;
    }

    /**
     * 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 if (Build.VERSION.SDK_INT >= 19) {
            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) {
        if (Build.VERSION.SDK_INT >= 17) {
            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) {
        if (Build.VERSION.SDK_INT >= 17) {
            mInfo.setLabelFor(root, virtualDescendantId);
        }
    }

    /**
     * Gets the node info for which the view represented by this info serves as
     * a label for accessibility purposes.
     * <p>
     *   <strong>Note:</strong> It is a client responsibility to recycle the
     *     received info by calling {@link AccessibilityNodeInfoCompat#recycle()}
     *     to avoid creating of multiple instances.
     * </p>
     *
     * @return The labeled info.
     */
    public AccessibilityNodeInfoCompat getLabelFor() {
        if (Build.VERSION.SDK_INT >= 17) {
            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getLabelFor());
        } else {
            return null;
        }
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 17) {
            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) {
        if (Build.VERSION.SDK_INT >= 17) {
            mInfo.setLabeledBy(root, virtualDescendantId);
        }
    }

    /**
     * Gets the node info which serves as the label of the view represented by
     * this info for accessibility purposes.
     * <p>
     *   <strong>Note:</strong> It is a client responsibility to recycle the
     *     received info by calling {@link AccessibilityNodeInfoCompat#recycle()}
     *     to avoid creating of multiple instances.
     * </p>
     *
     * @return The label.
     */
    public AccessibilityNodeInfoCompat getLabeledBy() {
        if (Build.VERSION.SDK_INT >= 17) {
            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getLabeledBy());
        } else {
            return null;
        }
    }

    /**
     * Gets if this node opens a popup or a dialog.
     *
     * @return If the the node opens a popup.
     */
    public boolean canOpenPopup() {
        if (Build.VERSION.SDK_INT >= 19) {
            return mInfo.canOpenPopup();
        } else {
            return false;
        }
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 19) {
            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> It is a client responsibility to recycle the
     *     received info by calling {@link AccessibilityNodeInfoCompat#recycle()}
     *     to avoid creating of multiple instances.
     * </p>
     * <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) {
        if (Build.VERSION.SDK_INT >= 18) {
            List<AccessibilityNodeInfo> nodes = mInfo.findAccessibilityNodeInfosByViewId(viewId);
            List<AccessibilityNodeInfoCompat> result = new ArrayList<>();
            for (AccessibilityNodeInfo node : nodes) {
                result.add(AccessibilityNodeInfoCompat.wrap(node));
            }
            return result;
        } else {
            return Collections.emptyList();
        }
    }

    /**
     * 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() {
        if (Build.VERSION.SDK_INT >= 19) {
            return mInfo.getExtras();
        } else {
            return new Bundle();
        }
    }

    /**
     * Gets the input type of the source as defined by {@link InputType}.
     *
     * @return The input type.
     */
    public int getInputType() {
        if (Build.VERSION.SDK_INT >= 19) {
            return mInfo.getInputType();
        } else {
            return InputType.TYPE_NULL;
        }
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 19) {
            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) {
        if (Build.VERSION.SDK_INT >= 18) {
            mInfo.setTextSelection(start, end);
        }
    }

    /**
     * Gets the text selection start.
     *
     * @return The text selection start if there is selection or -1.
     */
    public int getTextSelectionStart() {
        if (Build.VERSION.SDK_INT >= 18) {
            return mInfo.getTextSelectionStart();
        } else {
            return -1;
        }
    }

    /**
     * Gets the text selection end.
     *
     * @return The text selection end if there is selection or -1.
     */
    public int getTextSelectionEnd() {
        if (Build.VERSION.SDK_INT >= 18) {
            return mInfo.getTextSelectionEnd();
        } else {
            return -1;
        }
    }

    /**
     * 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() {
        if (Build.VERSION.SDK_INT >= 19) {
            return mInfo.isDismissable();
        } else {
            return false;
        }
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 19) {
            mInfo.setDismissable(dismissable);
        }
    }

    /**
     * Gets if the node is editable.
     *
     * @return True if the node is editable, false otherwise.
     */
    public boolean isEditable() {
        if (Build.VERSION.SDK_INT >= 18) {
            return mInfo.isEditable();
        } else {
            return false;
        }
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 18) {
            mInfo.setEditable(editable);
        }
    }

    /**
     * Gets if the node is a multi line editable text.
     *
     * @return True if the node is multi line.
     */
    public boolean isMultiLine() {
        if (Build.VERSION.SDK_INT >= 19) {
            return mInfo.isMultiLine();
        } else {
            return false;
        }
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 19) {
            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 if (Build.VERSION.SDK_INT >= 19) {
            return mInfo.getExtras().getCharSequence(TOOLTIP_TEXT_KEY);
        }
        return null;
    }

    /**
     * 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 if (Build.VERSION.SDK_INT >= 19) {
            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 if (Build.VERSION.SDK_INT >= 19) {
            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 if (Build.VERSION.SDK_INT >= 19) {
            return mInfo.getExtras().getCharSequence(PANE_TITLE_KEY);
        }
        return null;
    }

    /**
     * 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 accomodate 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);
        }
    }

    /**
     * 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 and should
     * be recycled.
     * </p>
     * @return Whether the refresh succeeded.
     */
    public boolean refresh() {
        if (Build.VERSION.SDK_INT >= 18) {
            return mInfo.refresh();
        } else {
            return false;
        }
    }

    /**
     * Gets the custom role description.
     * @return The role description.
     */
    public @Nullable CharSequence getRoleDescription() {
        if (Build.VERSION.SDK_INT >= 19) {
            return getExtras().getCharSequence(ROLE_DESCRIPTION_KEY);
        } else {
            return null;
        }
    }

    /**
     * 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) {
        if (Build.VERSION.SDK_INT >= 19) {
            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);
        }
    }

    @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);

        builder.append("; packageName: ").append(getPackageName());
        builder.append("; className: ").append(getClassName());
        builder.append("; text: ").append(getText());
        builder.append("; contentDescription: ").append(getContentDescription());
        builder.append("; viewId: ").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("; enabled: ").append(isEnabled());
        builder.append("; password: ").append(isPassword());
        builder.append("; scrollable: " + isScrollable());

        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;
    }

    private 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";
            default:
                return "ACTION_UNKNOWN";
        }
    }
}