blob: 48a81ece18dd9d6a304e74b380c4766c4dc66b69 [file] [log] [blame]
/*
* 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 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.RestrictTo;
import androidx.core.R;
import androidx.core.accessibilityservice.AccessibilityServiceInfoCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.MoveAtGranularityArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.MoveHtmlArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.MoveWindowArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.ScrollToPositionArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.SetProgressArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.SetSelectionArguments;
import androidx.core.view.accessibility.AccessibilityViewCommand.SetTextArguments;
import java.lang.ref.WeakReference;
import java.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);
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.
* @param columnCount The number of columns.
* @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.
*/
public int getColumnCount() {
if (Build.VERSION.SDK_INT >= 19) {
return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getColumnCount();
} else {
return 0;
}
}
/**
* Gets the number of rows.
*
* @return The row count.
*/
public int getRowCount() {
if (Build.VERSION.SDK_INT >= 19) {
return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getRowCount();
} else {
return 0;
}
}
/**
* 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";
// 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";
// 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<CharSequence> extrasCharSequenceList(String key) {
if (Build.VERSION.SDK_INT < 19) {
return new ArrayList<CharSequence>();
}
ArrayList<CharSequence> list = mInfo.getExtras()
.getCharSequenceArrayList(key);
if (list == null) {
list = new ArrayList<CharSequence>();
mInfo.getExtras().putCharSequenceArrayList(key, list);
}
return list;
}
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();
}
/**
* 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);
}
/**
* 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 his {@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")
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 his {@link android.accessibilityservice.AccessibilityService}.
* </p>
*
* @param viewId The fully qualified resource name of the view id to find.
* @return A list of node info.
*/
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);
}
}
/**
* 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 mInfo.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) {
mInfo.getExtras().putCharSequence(ROLE_DESCRIPTION_KEY, roleDescription);
}
}
/**
* Get the {@link TouchDelegateInfoCompat} for touch delegate behavior with the represented
* view. It is possible for the same node to be pointed to by several regions. Use
* {@link TouchDelegateInfoCompat#getRegionAt(int)} to get touch delegate target
* {@link Region}, and {@link TouchDelegateInfoCompat#getTargetForRegion(Region)}
* for {@link AccessibilityNodeInfoCompat} from the given region.
* <p>
* Compatibility:
* <ul>
* <li>API &lt; 29: Always returns {@code null}</li>
* </ul>
*
* @return {@link TouchDelegateInfoCompat} or {@code null} if there are no touch delegates
* in this node.
*/
@Nullable
public TouchDelegateInfoCompat getTouchDelegateInfo() {
if (Build.VERSION.SDK_INT >= 29) {
TouchDelegateInfo delegateInfo = mInfo.getTouchDelegateInfo();
if (delegateInfo != null) {
return new TouchDelegateInfoCompat(delegateInfo);
}
}
return null;
}
/**
* Set touch delegate info if the represented view has a {@link android.view.TouchDelegate}.
* <p>
* <strong>Note:</strong> Cannot be called from an
* {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an
* AccessibilityService.
* </p>
* <p>
* Compatibility:
* <ul>
* <li>API &lt; 29: No-op</li>
* </ul>
*
* @param delegatedInfo {@link TouchDelegateInfoCompat}
* @throws IllegalStateException If called from an AccessibilityService.
*/
public void setTouchDelegateInfo(@NonNull TouchDelegateInfoCompat delegatedInfo) {
if (Build.VERSION.SDK_INT >= 29) {
mInfo.setTouchDelegateInfo(delegatedInfo.mInfo);
}
}
@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("; 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";
default:
return"ACTION_UNKNOWN";
}
}
}