| /* |
| * 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 android.view.accessibility; |
| |
| import static com.android.internal.util.BitUtils.bitAt; |
| import static com.android.internal.util.BitUtils.isBitSet; |
| |
| import static java.util.Collections.EMPTY_LIST; |
| |
| import android.accessibilityservice.AccessibilityService; |
| import android.accessibilityservice.AccessibilityServiceInfo; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.TestApi; |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.graphics.Rect; |
| import android.graphics.Region; |
| import android.os.Bundle; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.text.InputType; |
| import android.text.Spannable; |
| import android.text.SpannableStringBuilder; |
| import android.text.Spanned; |
| import android.text.TextUtils; |
| import android.text.style.AccessibilityClickableSpan; |
| import android.text.style.AccessibilityURLSpan; |
| import android.text.style.ClickableSpan; |
| import android.text.style.URLSpan; |
| import android.util.ArrayMap; |
| import android.util.ArraySet; |
| import android.util.Log; |
| import android.util.LongArray; |
| import android.util.Pools.SynchronizedPool; |
| import android.view.TouchDelegate; |
| import android.view.View; |
| |
| import com.android.internal.R; |
| import com.android.internal.util.CollectionUtils; |
| import com.android.internal.util.Preconditions; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| /** |
| * This class represents a node of the window content as well as actions that |
| * can be requested from its source. From the point of view of an |
| * {@link android.accessibilityservice.AccessibilityService} a window's content is |
| * presented as a tree of accessibility node infos, which may or may not map one-to-one |
| * to the view hierarchy. In other words, a custom view is free to report itself as |
| * a tree of accessibility node info. |
| * </p> |
| * <p> |
| * Once an accessibility node info is delivered to an accessibility service it is |
| * made immutable and calling a state mutation method generates an error. |
| * </p> |
| * <p> |
| * Please refer to {@link android.accessibilityservice.AccessibilityService} for |
| * details about how to obtain a handle to window content as a tree of accessibility |
| * node info as well as details about the security model. |
| * </p> |
| * <div class="special reference"> |
| * <h3>Developer Guides</h3> |
| * <p>For more information about making applications accessible, read the |
| * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> |
| * developer guide.</p> |
| * </div> |
| * |
| * @see android.accessibilityservice.AccessibilityService |
| * @see AccessibilityEvent |
| * @see AccessibilityManager |
| */ |
| public class AccessibilityNodeInfo implements Parcelable { |
| |
| private static final boolean DEBUG = false; |
| |
| private static final String TAG = "AccessibilityNodeInfo"; |
| |
| /** @hide */ |
| public static final int UNDEFINED_CONNECTION_ID = -1; |
| |
| /** @hide */ |
| public static final int UNDEFINED_SELECTION_INDEX = -1; |
| |
| /** @hide */ |
| public static final int UNDEFINED_ITEM_ID = Integer.MAX_VALUE; |
| |
| /** @hide */ |
| public static final int ROOT_ITEM_ID = Integer.MAX_VALUE - 1; |
| |
| /** @hide */ |
| public static final long UNDEFINED_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID); |
| |
| /** @hide */ |
| public static final long ROOT_NODE_ID = makeNodeId(ROOT_ITEM_ID, |
| AccessibilityNodeProvider.HOST_VIEW_ID); |
| |
| /** @hide */ |
| public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001; |
| |
| /** @hide */ |
| public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002; |
| |
| /** @hide */ |
| public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004; |
| |
| /** @hide */ |
| public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008; |
| |
| /** @hide */ |
| public static final int FLAG_REPORT_VIEW_IDS = 0x00000010; |
| |
| // Actions. |
| |
| /** |
| * Action that gives input focus to the node. |
| */ |
| public static final int ACTION_FOCUS = 0x00000001; |
| |
| /** |
| * Action that clears input focus of the node. |
| */ |
| public static final int ACTION_CLEAR_FOCUS = 0x00000002; |
| |
| /** |
| * Action that selects the node. |
| */ |
| public static final int ACTION_SELECT = 0x00000004; |
| |
| /** |
| * Action that deselects the node. |
| */ |
| public static final int ACTION_CLEAR_SELECTION = 0x00000008; |
| |
| /** |
| * Action that clicks on the node info. |
| * |
| * See {@link AccessibilityAction#ACTION_CLICK} |
| */ |
| public static final int ACTION_CLICK = 0x00000010; |
| |
| /** |
| * Action that long clicks on the node. |
| */ |
| public static final int ACTION_LONG_CLICK = 0x00000020; |
| |
| /** |
| * 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; |
| |
| /** |
| * 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; |
| |
| /** @hide */ |
| public static final int LAST_LEGACY_STANDARD_ACTION = ACTION_SET_TEXT; |
| |
| /** |
| * Mask to see if the value is larger than the largest ACTION_ constant |
| */ |
| private static final int ACTION_TYPE_MASK = 0xFF000000; |
| |
| // Action arguments |
| |
| /** |
| * Argument for which movement granularity to be used when traversing the node text. |
| * <p> |
| * <strong>Type:</strong> int<br> |
| * <strong>Actions:</strong> |
| * <ul> |
| * <li>{@link AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY}</li> |
| * <li>{@link AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}</li> |
| * </ul> |
| * </p> |
| * |
| * @see AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY |
| * @see AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY |
| */ |
| 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> |
| * <ul> |
| * <li>{@link AccessibilityAction#ACTION_NEXT_HTML_ELEMENT}</li> |
| * <li>{@link AccessibilityAction#ACTION_PREVIOUS_HTML_ELEMENT}</li> |
| * </ul> |
| * </p> |
| * |
| * @see AccessibilityAction#ACTION_NEXT_HTML_ELEMENT |
| * @see AccessibilityAction#ACTION_PREVIOUS_HTML_ELEMENT |
| */ |
| 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> |
| * <ul> |
| * <li>{@link AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY}</li> |
| * <li>{@link AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}</li> |
| * </ul> |
| * |
| * @see AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY |
| * @see AccessibilityAction#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> |
| * <ul> |
| * <li>{@link AccessibilityAction#ACTION_SET_SELECTION}</li> |
| * </ul> |
| * |
| * @see AccessibilityAction#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> |
| * <ul> |
| * <li>{@link AccessibilityAction#ACTION_SET_SELECTION}</li> |
| * </ul> |
| * |
| * @see AccessibilityAction#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> |
| * <ul> |
| * <li>{@link AccessibilityAction#ACTION_SET_TEXT}</li> |
| * </ul> |
| * |
| * @see AccessibilityAction#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 AccessibilityAction#ACTION_SCROLL_TO_POSITION}</li> |
| * </ul> |
| * |
| * @see AccessibilityAction#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 AccessibilityAction#ACTION_SCROLL_TO_POSITION}</li> |
| * </ul> |
| * |
| * @see AccessibilityAction#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 AccessibilityAction#ACTION_SET_PROGRESS}</li> |
| * </ul> |
| * |
| * @see AccessibilityAction#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 AccessibilityAction#ACTION_MOVE_WINDOW}</li> |
| * </ul> |
| * |
| * @see AccessibilityAction#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 AccessibilityAction#ACTION_MOVE_WINDOW}</li> |
| * </ul> |
| * |
| * @see AccessibilityAction#ACTION_MOVE_WINDOW |
| */ |
| public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y = |
| "ACTION_ARGUMENT_MOVE_WINDOW_Y"; |
| |
| /** |
| * Argument to pass the {@link AccessibilityClickableSpan}. |
| * For use with R.id.accessibilityActionClickOnClickableSpan |
| * @hide |
| */ |
| public static final String ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN = |
| "android.view.accessibility.action.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN"; |
| |
| // Focus types |
| |
| /** |
| * The input focus. |
| */ |
| public static final int FOCUS_INPUT = 1; |
| |
| /** |
| * The accessibility focus. |
| */ |
| public static final int FOCUS_ACCESSIBILITY = 2; |
| |
| // Movement granularities |
| |
| /** |
| * Movement granularity bit for traversing the text of a node by character. |
| */ |
| public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001; |
| |
| /** |
| * Movement granularity bit for traversing the text of a node by word. |
| */ |
| public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002; |
| |
| /** |
| * Movement granularity bit for traversing the text of a node by line. |
| */ |
| public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004; |
| |
| /** |
| * Movement granularity bit for traversing the text of a node by paragraph. |
| */ |
| public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008; |
| |
| /** |
| * Movement granularity bit for traversing the text of a node by page. |
| */ |
| public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010; |
| |
| /** |
| * Key used to request and locate extra data for text character location. This key requests that |
| * an array of {@link android.graphics.RectF}s be added to the extras. This request is made with |
| * {@link #refreshWithExtraData(String, Bundle)}. The arguments taken by this request are two |
| * integers: {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX} and |
| * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH}. The starting index must be valid |
| * inside the CharSequence returned by {@link #getText()}, and the length must be positive. |
| * <p> |
| * The data can be retrieved from the {@code Bundle} returned by {@link #getExtras()} using this |
| * string as a key for {@link Bundle#getParcelableArray(String)}. The |
| * {@link android.graphics.RectF} will be null for characters that either do not exist or are |
| * off the screen. |
| * |
| * {@see #refreshWithExtraData(String, Bundle)} |
| */ |
| public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = |
| "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY"; |
| |
| /** |
| * Integer argument specifying the start index of the requested text location data. Must be |
| * valid inside the CharSequence returned by {@link #getText()}. |
| * |
| * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY |
| */ |
| public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = |
| "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX"; |
| |
| /** |
| * Integer argument specifying the end index of the requested text location data. Must be |
| * positive. |
| * |
| * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY |
| */ |
| public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = |
| "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH"; |
| |
| /** @hide */ |
| public static final String EXTRA_DATA_REQUESTED_KEY = |
| "android.view.accessibility.AccessibilityNodeInfo.extra_data_requested"; |
| |
| // Boolean attributes. |
| |
| private static final int BOOLEAN_PROPERTY_CHECKABLE = 0x00000001; |
| |
| private static final int BOOLEAN_PROPERTY_CHECKED = 0x00000002; |
| |
| private static final int BOOLEAN_PROPERTY_FOCUSABLE = 0x00000004; |
| |
| private static final int BOOLEAN_PROPERTY_FOCUSED = 0x00000008; |
| |
| private static final int BOOLEAN_PROPERTY_SELECTED = 0x00000010; |
| |
| private static final int BOOLEAN_PROPERTY_CLICKABLE = 0x00000020; |
| |
| private static final int BOOLEAN_PROPERTY_LONG_CLICKABLE = 0x00000040; |
| |
| private static final int BOOLEAN_PROPERTY_ENABLED = 0x00000080; |
| |
| private static final int BOOLEAN_PROPERTY_PASSWORD = 0x00000100; |
| |
| private static final int BOOLEAN_PROPERTY_SCROLLABLE = 0x00000200; |
| |
| private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400; |
| |
| private static final int BOOLEAN_PROPERTY_VISIBLE_TO_USER = 0x00000800; |
| |
| private static final int BOOLEAN_PROPERTY_EDITABLE = 0x00001000; |
| |
| private static final int BOOLEAN_PROPERTY_OPENS_POPUP = 0x00002000; |
| |
| private static final int BOOLEAN_PROPERTY_DISMISSABLE = 0x00004000; |
| |
| private static final int BOOLEAN_PROPERTY_MULTI_LINE = 0x00008000; |
| |
| private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 0x00010000; |
| |
| private static final int BOOLEAN_PROPERTY_CONTEXT_CLICKABLE = 0x00020000; |
| |
| private static final int BOOLEAN_PROPERTY_IMPORTANCE = 0x0040000; |
| |
| private static final int BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE = 0x0080000; |
| |
| private static final int BOOLEAN_PROPERTY_IS_SHOWING_HINT = 0x0100000; |
| |
| private static final int BOOLEAN_PROPERTY_IS_HEADING = 0x0200000; |
| |
| private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x0400000; |
| |
| /** |
| * Bits that provide the id of a virtual descendant of a view. |
| */ |
| private static final long VIRTUAL_DESCENDANT_ID_MASK = 0xffffffff00000000L; |
| /** |
| * Bit shift of {@link #VIRTUAL_DESCENDANT_ID_MASK} to get to the id for a |
| * virtual descendant of a view. Such a descendant does not exist in the view |
| * hierarchy and is only reported via the accessibility APIs. |
| */ |
| private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32; |
| |
| // TODO(b/129300068): Remove sNumInstancesInUse. |
| private static AtomicInteger sNumInstancesInUse; |
| |
| /** |
| * Gets the accessibility view id which identifies a View in the view three. |
| * |
| * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}. |
| * @return The accessibility view id part of the node id. |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public static int getAccessibilityViewId(long accessibilityNodeId) { |
| return (int) accessibilityNodeId; |
| } |
| |
| /** |
| * Gets the virtual descendant id which identifies an imaginary view in a |
| * containing View. |
| * |
| * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}. |
| * @return The virtual view id part of the node id. |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public static int getVirtualDescendantId(long accessibilityNodeId) { |
| return (int) ((accessibilityNodeId & VIRTUAL_DESCENDANT_ID_MASK) |
| >> VIRTUAL_DESCENDANT_ID_SHIFT); |
| } |
| |
| /** |
| * Makes a node id by shifting the <code>virtualDescendantId</code> |
| * by {@link #VIRTUAL_DESCENDANT_ID_SHIFT} and taking |
| * the bitwise or with the <code>accessibilityViewId</code>. |
| * |
| * @param accessibilityViewId A View accessibility id. |
| * @param virtualDescendantId A virtual descendant id. |
| * @return The node id. |
| * |
| * @hide |
| */ |
| public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) { |
| return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId; |
| } |
| |
| // Housekeeping. |
| private static final int MAX_POOL_SIZE = 50; |
| private static final SynchronizedPool<AccessibilityNodeInfo> sPool = |
| new SynchronizedPool<>(MAX_POOL_SIZE); |
| |
| private static final AccessibilityNodeInfo DEFAULT = new AccessibilityNodeInfo(); |
| |
| @UnsupportedAppUsage |
| private boolean mSealed; |
| |
| // Data. |
| private int mWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; |
| @UnsupportedAppUsage |
| private long mSourceNodeId = UNDEFINED_NODE_ID; |
| private long mParentNodeId = UNDEFINED_NODE_ID; |
| private long mLabelForId = UNDEFINED_NODE_ID; |
| private long mLabeledById = UNDEFINED_NODE_ID; |
| private long mTraversalBefore = UNDEFINED_NODE_ID; |
| private long mTraversalAfter = UNDEFINED_NODE_ID; |
| |
| private int mBooleanProperties; |
| private final Rect mBoundsInParent = new Rect(); |
| private final Rect mBoundsInScreen = new Rect(); |
| private int mDrawingOrderInParent; |
| |
| private CharSequence mPackageName; |
| private CharSequence mClassName; |
| // Hidden, unparceled value used to hold the original value passed to setText |
| private CharSequence mOriginalText; |
| private CharSequence mText; |
| private CharSequence mHintText; |
| private CharSequence mError; |
| private CharSequence mPaneTitle; |
| private CharSequence mContentDescription; |
| private CharSequence mTooltipText; |
| private String mViewIdResourceName; |
| private ArrayList<String> mExtraDataKeys; |
| |
| @UnsupportedAppUsage |
| private LongArray mChildNodeIds; |
| private ArrayList<AccessibilityAction> mActions; |
| |
| private int mMaxTextLength = -1; |
| private int mMovementGranularities; |
| |
| private int mTextSelectionStart = UNDEFINED_SELECTION_INDEX; |
| private int mTextSelectionEnd = UNDEFINED_SELECTION_INDEX; |
| private int mInputType = InputType.TYPE_NULL; |
| private int mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE; |
| |
| private Bundle mExtras; |
| |
| private int mConnectionId = UNDEFINED_CONNECTION_ID; |
| |
| private RangeInfo mRangeInfo; |
| private CollectionInfo mCollectionInfo; |
| private CollectionItemInfo mCollectionItemInfo; |
| |
| private TouchDelegateInfo mTouchDelegateInfo; |
| |
| /** |
| * Hide constructor from clients. |
| */ |
| private AccessibilityNodeInfo() { |
| /* do nothing */ |
| } |
| |
| /** @hide */ |
| AccessibilityNodeInfo(AccessibilityNodeInfo info) { |
| init(info); |
| } |
| |
| /** |
| * Sets the source. |
| * <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 source The info source. |
| */ |
| public void setSource(View source) { |
| setSource(source, AccessibilityNodeProvider.HOST_VIEW_ID); |
| } |
| |
| /** |
| * 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> |
| * <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 setSource(View root, int virtualDescendantId) { |
| enforceNotSealed(); |
| mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED_ITEM_ID; |
| final int rootAccessibilityViewId = |
| (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; |
| mSourceNodeId = makeNodeId(rootAccessibilityViewId, 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 AccessibilityNodeInfo findFocus(int focus) { |
| enforceSealed(); |
| enforceValidFocusType(focus); |
| if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { |
| return null; |
| } |
| return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId, |
| mSourceNodeId, focus); |
| } |
| |
| /** |
| * Searches for the nearest view in the specified direction that can take |
| * the 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 AccessibilityNodeInfo focusSearch(int direction) { |
| enforceSealed(); |
| enforceValidFocusDirection(direction); |
| if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { |
| return null; |
| } |
| return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId, |
| mSourceNodeId, direction); |
| } |
| |
| /** |
| * Gets the id of the window from which the info comes from. |
| * |
| * @return The window id. |
| */ |
| public int getWindowId() { |
| return mWindowId; |
| } |
| |
| /** |
| * 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> |
| * |
| * @param bypassCache Whether to bypass the cache. |
| * @return Whether the refresh succeeded. |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public boolean refresh(Bundle arguments, boolean bypassCache) { |
| enforceSealed(); |
| if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { |
| return false; |
| } |
| AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); |
| AccessibilityNodeInfo refreshedInfo = client.findAccessibilityNodeInfoByAccessibilityId( |
| mConnectionId, mWindowId, mSourceNodeId, bypassCache, 0, arguments); |
| if (refreshedInfo == null) { |
| return false; |
| } |
| // Hard-to-reproduce bugs seem to be due to some tools recycling a node on another |
| // thread. If that happens, the init will re-seal the node, which then is in a bad state |
| // when it is obtained. Enforce sealing again before we init to fail when a node has been |
| // recycled during a refresh to catch such errors earlier. |
| enforceSealed(); |
| init(refreshedInfo); |
| refreshedInfo.recycle(); |
| return true; |
| } |
| |
| /** |
| * Refreshes this info with the latest state of the view it represents. |
| * |
| * @return {@code true} if the refresh succeeded. {@code false} if the {@link View} represented |
| * by this node is no longer in the view tree (and thus this node is obsolete and should be |
| * recycled). |
| */ |
| public boolean refresh() { |
| return refresh(null, true); |
| } |
| |
| /** |
| * Refreshes this info with the latest state of the view it represents, and request new |
| * data be added by the View. |
| * |
| * @param extraDataKey The extra data requested. Data that must be requested |
| * with this mechanism is generally expensive to retrieve, so should only be |
| * requested when needed. See |
| * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY} and |
| * {@link #getAvailableExtraData()}. |
| * @param args A bundle of arguments for the request. These depend on the particular request. |
| * |
| * @return {@code true} if the refresh succeeded. {@code false} if the {@link View} represented |
| * by this node is no longer in the view tree (and thus this node is obsolete and should be |
| * recycled). |
| */ |
| public boolean refreshWithExtraData(String extraDataKey, Bundle args) { |
| args.putString(EXTRA_DATA_REQUESTED_KEY, extraDataKey); |
| return refresh(args, true); |
| } |
| |
| /** |
| * Returns the array containing the IDs of this node's children. |
| * |
| * @hide |
| */ |
| public LongArray getChildNodeIds() { |
| return mChildNodeIds; |
| } |
| |
| /** |
| * Returns the id of the child at the specified index. |
| * |
| * @throws IndexOutOfBoundsException when index < 0 || index >= |
| * getChildCount() |
| * @hide |
| */ |
| public long getChildId(int index) { |
| if (mChildNodeIds == null) { |
| throw new IndexOutOfBoundsException(); |
| } |
| return mChildNodeIds.get(index); |
| } |
| |
| /** |
| * Gets the number of children. |
| * |
| * @return The child count. |
| */ |
| public int getChildCount() { |
| return mChildNodeIds == null ? 0 : mChildNodeIds.size(); |
| } |
| |
| /** |
| * Get the child at given index. |
| * <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> |
| * |
| * @param index The child index. |
| * @return The child node. |
| * |
| * @throws IllegalStateException If called outside of an AccessibilityService. |
| * |
| */ |
| public AccessibilityNodeInfo getChild(int index) { |
| enforceSealed(); |
| if (mChildNodeIds == null) { |
| return null; |
| } |
| if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { |
| return null; |
| } |
| final long childId = mChildNodeIds.get(index); |
| AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); |
| return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId, |
| childId, false, FLAG_PREFETCH_DESCENDANTS, null); |
| } |
| |
| /** |
| * 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. |
| * Note that a view cannot be made its own child. |
| * </p> |
| * |
| * @param child The child. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void addChild(View child) { |
| addChildInternal(child, AccessibilityNodeProvider.HOST_VIEW_ID, true); |
| } |
| |
| /** |
| * Unchecked version of {@link #addChild(View)} that does not verify |
| * uniqueness. For framework use only. |
| * |
| * @hide |
| */ |
| public void addChildUnchecked(View child) { |
| addChildInternal(child, AccessibilityNodeProvider.HOST_VIEW_ID, false); |
| } |
| |
| /** |
| * 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) { |
| return removeChild(child, AccessibilityNodeProvider.HOST_VIEW_ID); |
| } |
| |
| /** |
| * 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. |
| * Note that a view cannot be made its own child. |
| * </p> |
| * |
| * @param root The root of the virtual subtree. |
| * @param virtualDescendantId The id of the virtual child. |
| */ |
| public void addChild(View root, int virtualDescendantId) { |
| addChildInternal(root, virtualDescendantId, true); |
| } |
| |
| private void addChildInternal(View root, int virtualDescendantId, boolean checked) { |
| enforceNotSealed(); |
| if (mChildNodeIds == null) { |
| mChildNodeIds = new LongArray(); |
| } |
| final int rootAccessibilityViewId = |
| (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; |
| final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); |
| if (childNodeId == mSourceNodeId) { |
| Log.e(TAG, "Rejecting attempt to make a View its own child"); |
| return; |
| } |
| |
| // If we're checking uniqueness and the ID already exists, abort. |
| if (checked && mChildNodeIds.indexOf(childNodeId) >= 0) { |
| return; |
| } |
| mChildNodeIds.add(childNodeId); |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| final LongArray childIds = mChildNodeIds; |
| if (childIds == null) { |
| return false; |
| } |
| final int rootAccessibilityViewId = |
| (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; |
| final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); |
| final int index = childIds.indexOf(childNodeId); |
| if (index < 0) { |
| return false; |
| } |
| childIds.remove(index); |
| return true; |
| } |
| |
| /** |
| * Gets the actions that can be performed on the node. |
| */ |
| public List<AccessibilityAction> getActionList() { |
| return CollectionUtils.emptyIfNull(mActions); |
| } |
| |
| /** |
| * Gets the actions that can be performed on the node. |
| * |
| * @return The bit mask of with actions. |
| * |
| * @see AccessibilityNodeInfo#ACTION_FOCUS |
| * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS |
| * @see AccessibilityNodeInfo#ACTION_SELECT |
| * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION |
| * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS |
| * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS |
| * @see AccessibilityNodeInfo#ACTION_CLICK |
| * @see AccessibilityNodeInfo#ACTION_LONG_CLICK |
| * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY |
| * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY |
| * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT |
| * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT |
| * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD |
| * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD |
| * |
| * @deprecated Use {@link #getActionList()}. |
| */ |
| @Deprecated |
| public int getActions() { |
| int returnValue = 0; |
| |
| if (mActions == null) { |
| return returnValue; |
| } |
| |
| final int actionSize = mActions.size(); |
| for (int i = 0; i < actionSize; i++) { |
| int actionId = mActions.get(i).getId(); |
| if (actionId <= LAST_LEGACY_STANDARD_ACTION) { |
| returnValue |= actionId; |
| } |
| } |
| |
| return returnValue; |
| } |
| |
| /** |
| * Adds an action that can be performed on the node. |
| * <p> |
| * To add a standard action use the static constants on {@link AccessibilityAction}. |
| * To add a custom action create a new {@link AccessibilityAction} by passing in a |
| * resource id from your application as the action id and an optional label that |
| * describes the action. To override one of the standard actions use as the action |
| * id of a standard action id such as {@link #ACTION_CLICK} and an optional label that |
| * describes the action. |
| * </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 action The action. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void addAction(AccessibilityAction action) { |
| enforceNotSealed(); |
| |
| addActionUnchecked(action); |
| } |
| |
| private void addActionUnchecked(AccessibilityAction action) { |
| if (action == null) { |
| return; |
| } |
| |
| if (mActions == null) { |
| mActions = new ArrayList<>(); |
| } |
| |
| mActions.remove(action); |
| mActions.add(action); |
| } |
| |
| /** |
| * 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. |
| * @throws IllegalArgumentException If the argument is not one of the standard actions. |
| * |
| * @deprecated This has been deprecated for {@link #addAction(AccessibilityAction)} |
| */ |
| @Deprecated |
| public void addAction(int action) { |
| enforceNotSealed(); |
| |
| if ((action & ACTION_TYPE_MASK) != 0) { |
| throw new IllegalArgumentException("Action is not a combination of the standard " + |
| "actions: " + action); |
| } |
| |
| addStandardActions(action); |
| } |
| |
| /** |
| * Removes an action that can be performed on the node. If the action was |
| * not already added to the node, calling this method has no effect. |
| * <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. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| * @deprecated Use {@link #removeAction(AccessibilityAction)} |
| */ |
| @Deprecated |
| public void removeAction(int action) { |
| enforceNotSealed(); |
| |
| removeAction(getActionSingleton(action)); |
| } |
| |
| /** |
| * Removes an action that can be performed on the node. If the action was |
| * not already added to the node, calling this method has no effect. |
| * <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. |
| */ |
| public boolean removeAction(AccessibilityAction action) { |
| enforceNotSealed(); |
| |
| if (mActions == null || action == null) { |
| return false; |
| } |
| |
| return mActions.remove(action); |
| } |
| |
| /** |
| * Removes all actions. |
| * |
| * @hide |
| */ |
| public void removeAllActions() { |
| if (mActions != null) { |
| mActions.clear(); |
| } |
| } |
| |
| /** |
| * 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 AccessibilityNodeInfo getTraversalBefore() { |
| enforceSealed(); |
| return getNodeForAccessibilityId(mConnectionId, mWindowId, mTraversalBefore); |
| } |
| |
| /** |
| * 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) { |
| setTraversalBefore(view, AccessibilityNodeProvider.HOST_VIEW_ID); |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| final int rootAccessibilityViewId = (root != null) |
| ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; |
| mTraversalBefore = makeNodeId(rootAccessibilityViewId, 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 AccessibilityNodeInfo getTraversalAfter() { |
| enforceSealed(); |
| return getNodeForAccessibilityId(mConnectionId, mWindowId, mTraversalAfter); |
| } |
| |
| /** |
| * 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) { |
| setTraversalAfter(view, AccessibilityNodeProvider.HOST_VIEW_ID); |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| final int rootAccessibilityViewId = (root != null) |
| ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; |
| mTraversalAfter = makeNodeId(rootAccessibilityViewId, virtualDescendantId); |
| } |
| |
| /** |
| * Get the extra data available for this node. |
| * <p> |
| * Some data that is useful for some accessibility services is expensive to compute, and would |
| * place undue overhead on apps to compute all the time. That data can be requested with |
| * {@link #refreshWithExtraData(String, Bundle)}. |
| * |
| * @return An unmodifiable list of keys corresponding to extra data that can be requested. |
| * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY |
| */ |
| public List<String> getAvailableExtraData() { |
| if (mExtraDataKeys != null) { |
| return Collections.unmodifiableList(mExtraDataKeys); |
| } else { |
| return EMPTY_LIST; |
| } |
| } |
| |
| /** |
| * Set the extra data available for this node. |
| * <p> |
| * <strong>Note:</strong> When a {@code View} passes in a non-empty list, it promises that |
| * it will populate the node's extras with corresponding pieces of information in |
| * {@link View#addExtraDataToAccessibilityNodeInfo(AccessibilityNodeInfo, String, Bundle)}. |
| * <p> |
| * <strong>Note:</strong> Cannot be called from an |
| * {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * |
| * @param extraDataKeys A list of types of extra data that are available. |
| * @see #getAvailableExtraData() |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setAvailableExtraData(List<String> extraDataKeys) { |
| enforceNotSealed(); |
| mExtraDataKeys = new ArrayList<>(extraDataKeys); |
| } |
| |
| /** |
| * Sets the maximum text length, or -1 for no limit. |
| * <p> |
| * Typically used to indicate that an editable text field has a limit on |
| * the number of characters entered. |
| * <p> |
| * <strong>Note:</strong> Cannot be called from an |
| * {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * |
| * @param max The maximum text length. |
| * @see #getMaxTextLength() |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setMaxTextLength(int max) { |
| enforceNotSealed(); |
| mMaxTextLength = 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() { |
| return mMaxTextLength; |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| mMovementGranularities = granularities; |
| } |
| |
| /** |
| * Gets the movement granularities for traversing the text of this node. |
| * |
| * @return The bit mask with granularities. |
| */ |
| public int getMovementGranularities() { |
| return mMovementGranularities; |
| } |
| |
| /** |
| * 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) { |
| enforceSealed(); |
| if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { |
| return false; |
| } |
| AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); |
| return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, |
| action, null); |
| } |
| |
| /** |
| * 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) { |
| enforceSealed(); |
| if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { |
| return false; |
| } |
| AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); |
| return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, |
| action, arguments); |
| } |
| |
| /** |
| * Finds {@link 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 AccessibilityNodeInfo#recycle()} |
| * to avoid creating of multiple instances. |
| * </p> |
| * |
| * @param text The searched text. |
| * @return A list of node info. |
| */ |
| public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) { |
| enforceSealed(); |
| if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { |
| return Collections.emptyList(); |
| } |
| AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); |
| return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId, |
| text); |
| } |
| |
| /** |
| * Finds {@link AccessibilityNodeInfo}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 AccessibilityNodeInfo#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 AccessibilityNodeInfo} |
| * the client has to set the {@link 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<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) { |
| enforceSealed(); |
| if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { |
| return Collections.emptyList(); |
| } |
| AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); |
| return client.findAccessibilityNodeInfosByViewId(mConnectionId, mWindowId, mSourceNodeId, |
| viewId); |
| } |
| |
| /** |
| * Gets the window to which this node belongs. |
| * |
| * @return The window. |
| * |
| * @see android.accessibilityservice.AccessibilityService#getWindows() |
| */ |
| public AccessibilityWindowInfo getWindow() { |
| enforceSealed(); |
| if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { |
| return null; |
| } |
| AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); |
| return client.getWindow(mConnectionId, mWindowId); |
| } |
| |
| /** |
| * Gets the parent. |
| * <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> |
| * |
| * @return The parent. |
| */ |
| public AccessibilityNodeInfo getParent() { |
| enforceSealed(); |
| return getNodeForAccessibilityId(mConnectionId, mWindowId, mParentNodeId); |
| } |
| |
| /** |
| * @return The parent node id. |
| * |
| * @hide |
| */ |
| public long getParentNodeId() { |
| return mParentNodeId; |
| } |
| |
| /** |
| * 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) { |
| setParent(parent, AccessibilityNodeProvider.HOST_VIEW_ID); |
| } |
| |
| /** |
| * 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> |
| * <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 setParent(View root, int virtualDescendantId) { |
| enforceNotSealed(); |
| final int rootAccessibilityViewId = |
| (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; |
| mParentNodeId = makeNodeId(rootAccessibilityViewId, 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) { |
| outBounds.set(mBoundsInParent.left, mBoundsInParent.top, |
| mBoundsInParent.right, mBoundsInParent.bottom); |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom); |
| } |
| |
| /** |
| * Gets the node bounds in screen coordinates. |
| * |
| * @param outBounds The output node bounds. |
| */ |
| public void getBoundsInScreen(Rect outBounds) { |
| outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top, |
| mBoundsInScreen.right, mBoundsInScreen.bottom); |
| } |
| |
| /** |
| * Returns the actual rect containing the node bounds in screen coordinates. |
| * |
| * @hide Not safe to expose outside the framework. |
| */ |
| public Rect getBoundsInScreen() { |
| return mBoundsInScreen; |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom); |
| } |
| |
| /** |
| * Gets whether this node is checkable. |
| * |
| * @return True if the node is checkable. |
| */ |
| public boolean isCheckable() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE, checkable); |
| } |
| |
| /** |
| * Gets whether this node is checked. |
| * |
| * @return True if the node is checked. |
| */ |
| public boolean isChecked() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_CHECKED); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked); |
| } |
| |
| /** |
| * Gets whether this node is focusable. |
| * |
| * @return True if the node is focusable. |
| */ |
| public boolean isFocusable() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable); |
| } |
| |
| /** |
| * Gets whether this node is focused. |
| * |
| * @return True if the node is focused. |
| */ |
| public boolean isFocused() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused); |
| } |
| |
| /** |
| * Gets whether this node is visible to the user. |
| * |
| * @return Whether the node is visible to the user. |
| */ |
| public boolean isVisibleToUser() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER, visibleToUser); |
| } |
| |
| /** |
| * Gets whether this node is accessibility focused. |
| * |
| * @return True if the node is accessibility focused. |
| */ |
| public boolean isAccessibilityFocused() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused); |
| } |
| |
| /** |
| * Gets whether this node is selected. |
| * |
| * @return True if the node is selected. |
| */ |
| public boolean isSelected() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_SELECTED); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_SELECTED, selected); |
| } |
| |
| /** |
| * Gets whether this node is clickable. |
| * |
| * @return True if the node is clickable. |
| */ |
| public boolean isClickable() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE, clickable); |
| } |
| |
| /** |
| * Gets whether this node is long clickable. |
| * |
| * @return True if the node is long clickable. |
| */ |
| public boolean isLongClickable() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE, longClickable); |
| } |
| |
| /** |
| * Gets whether this node is enabled. |
| * |
| * @return True if the node is enabled. |
| */ |
| public boolean isEnabled() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_ENABLED); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_ENABLED, enabled); |
| } |
| |
| /** |
| * Gets whether this node is a password. |
| * |
| * @return True if the node is a password. |
| */ |
| public boolean isPassword() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_PASSWORD); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_PASSWORD, password); |
| } |
| |
| /** |
| * Gets if the node is scrollable. |
| * |
| * @return True if the node is scrollable, false otherwise. |
| */ |
| public boolean isScrollable() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable); |
| } |
| |
| /** |
| * Gets if the node is editable. |
| * |
| * @return True if the node is editable, false otherwise. |
| */ |
| public boolean isEditable() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_EDITABLE); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_EDITABLE, editable); |
| } |
| |
| /** |
| * 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> |
| * <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 pane represented by this node. |
| */ |
| public void setPaneTitle(@Nullable CharSequence paneTitle) { |
| enforceNotSealed(); |
| mPaneTitle = (paneTitle == null) |
| ? null : paneTitle.subSequence(0, paneTitle.length()); |
| } |
| |
| /** |
| * 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() { |
| return mPaneTitle; |
| } |
| |
| /** |
| * 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() { |
| return mDrawingOrderInParent; |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| mDrawingOrderInParent = drawingOrderInParent; |
| } |
| |
| /** |
| * Gets the collection info if the node is a collection. A collection |
| * child is always a collection item. |
| * |
| * @return The collection info. |
| */ |
| public CollectionInfo getCollectionInfo() { |
| return mCollectionInfo; |
| } |
| |
| /** |
| * Sets the collection info if the node is a collection. A collection |
| * child is always a collection item. |
| * <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 collectionInfo The collection info. |
| */ |
| public void setCollectionInfo(CollectionInfo collectionInfo) { |
| enforceNotSealed(); |
| mCollectionInfo = collectionInfo; |
| } |
| |
| /** |
| * 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 CollectionItemInfo getCollectionItemInfo() { |
| return mCollectionItemInfo; |
| } |
| |
| /** |
| * Sets the collection item info if the node is a collection item. A collection |
| * item is always a child of a collection. |
| * <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> |
| */ |
| public void setCollectionItemInfo(CollectionItemInfo collectionItemInfo) { |
| enforceNotSealed(); |
| mCollectionItemInfo = collectionItemInfo; |
| } |
| |
| /** |
| * Gets the range info if this node is a range. |
| * |
| * @return The range. |
| */ |
| public RangeInfo getRangeInfo() { |
| return mRangeInfo; |
| } |
| |
| /** |
| * 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(RangeInfo rangeInfo) { |
| enforceNotSealed(); |
| mRangeInfo = rangeInfo; |
| } |
| |
| /** |
| * Gets if the content of this node is invalid. For example, |
| * a date is not well-formed. |
| * |
| * @return If the node content is invalid. |
| */ |
| public boolean isContentInvalid() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID, contentInvalid); |
| } |
| |
| /** |
| * Gets whether this node is context clickable. |
| * |
| * @return True if the node is context clickable. |
| */ |
| public boolean isContextClickable() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_CONTEXT_CLICKABLE); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_CONTEXT_CLICKABLE, contextClickable); |
| } |
| |
| /** |
| * 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 View#ACCESSIBILITY_LIVE_REGION_POLITE}. |
| * <p> |
| * It is the responsibility of the accessibility service to monitor |
| * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events indicating |
| * changes to live region nodes and their children. |
| * |
| * @return The live region mode, or |
| * {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a |
| * live region. |
| * @see android.view.View#getAccessibilityLiveRegion() |
| */ |
| public int getLiveRegion() { |
| return mLiveRegion; |
| } |
| |
| /** |
| * 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 View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a |
| * live region. |
| * @see android.view.View#setAccessibilityLiveRegion(int) |
| */ |
| public void setLiveRegion(int mode) { |
| enforceNotSealed(); |
| mLiveRegion = mode; |
| } |
| |
| /** |
| * Gets if the node is a multi line editable text. |
| * |
| * @return True if the node is multi line. |
| */ |
| public boolean isMultiLine() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE, multiLine); |
| } |
| |
| /** |
| * Gets if this node opens a popup or a dialog. |
| * |
| * @return If the the node opens a popup. |
| */ |
| public boolean canOpenPopup() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP); |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| setBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP, opensPopup); |
| } |
| |
| /** |
| * Gets if the node can be dismissed. |
| * |
| * @return If the node can be dismissed. |
| */ |
| public boolean isDismissable() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE, dismissable); |
| } |
| |
| /** |
| * 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() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_IMPORTANCE); |
| } |
| |
| /** |
| * 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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_IMPORTANCE, important); |
| } |
| |
| /** |
| * 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. |
| * |
| * @see View#isScreenReaderFocusable() |
| */ |
| public boolean isScreenReaderFocusable() { |
| return getBooleanProperty(BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE); |
| } |
| |
| /** |
| * Sets whether the node should be considered a focusable unit by a screen reader. |
| * <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) { |
| 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() { |
| 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> |
| * <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) { |
| 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 CollectionItemInfo} 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. |
| */ |
| public boolean isHeading() { |
| if (getBooleanProperty(BOOLEAN_PROPERTY_IS_HEADING)) return true; |
| CollectionItemInfo itemInfo = getCollectionItemInfo(); |
| return ((itemInfo != null) && itemInfo.mHeading); |
| } |
| |
| /** |
| * Sets whether the node represents a heading. |
| * |
| * <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) { |
| 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() { |
| 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> |
| * <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) { |
| setBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY, isTextEntryKey); |
| } |
| |
| /** |
| * Gets the package this node comes from. |
| * |
| * @return The package name. |
| */ |
| public CharSequence getPackageName() { |
| return mPackageName; |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| mPackageName = packageName; |
| } |
| |
| /** |
| * Gets the class this node comes from. |
| * |
| * @return The class name. |
| */ |
| public CharSequence getClassName() { |
| return mClassName; |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| mClassName = className; |
| } |
| |
| /** |
| * Gets the text of this node. |
| * <p> |
| * <strong>Note:</strong> If the text contains {@link ClickableSpan}s or {@link URLSpan}s, |
| * these spans will have been replaced with ones whose {@link ClickableSpan#onClick(View)} |
| * can be called from an {@link AccessibilityService}. When called from a service, the |
| * {@link View} argument is ignored and the corresponding span will be found on the view that |
| * this {@code AccessibilityNodeInfo} represents and called with that view as its argument. |
| * <p> |
| * This treatment of {@link ClickableSpan}s means that the text returned from this method may |
| * different slightly one passed to {@link #setText(CharSequence)}, although they will be |
| * equivalent according to {@link TextUtils#equals(CharSequence, CharSequence)}. The |
| * {@link ClickableSpan#onClick(View)} of any spans, however, will generally not work outside |
| * of an accessibility service. |
| * </p> |
| * |
| * @return The text. |
| */ |
| public CharSequence getText() { |
| // Attach this node to any spans that need it |
| if (mText instanceof Spanned) { |
| Spanned spanned = (Spanned) mText; |
| AccessibilityClickableSpan[] clickableSpans = |
| spanned.getSpans(0, mText.length(), AccessibilityClickableSpan.class); |
| for (int i = 0; i < clickableSpans.length; i++) { |
| clickableSpans[i].copyConnectionDataFrom(this); |
| } |
| AccessibilityURLSpan[] urlSpans = |
| spanned.getSpans(0, mText.length(), AccessibilityURLSpan.class); |
| for (int i = 0; i < urlSpans.length; i++) { |
| urlSpans[i].copyConnectionDataFrom(this); |
| } |
| } |
| return mText; |
| } |
| |
| /** |
| * Get the text passed to setText before any changes to the spans. |
| * @hide |
| */ |
| public CharSequence getOriginalText() { |
| return mOriginalText; |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| mOriginalText = text; |
| // Replace any ClickableSpans in mText with placeholders |
| if (text instanceof Spanned) { |
| ClickableSpan[] spans = |
| ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class); |
| if (spans.length > 0) { |
| Spannable spannable = new SpannableStringBuilder(text); |
| for (int i = 0; i < spans.length; i++) { |
| ClickableSpan span = spans[i]; |
| if ((span instanceof AccessibilityClickableSpan) |
| || (span instanceof AccessibilityURLSpan)) { |
| // We've already done enough |
| break; |
| } |
| int spanToReplaceStart = spannable.getSpanStart(span); |
| int spanToReplaceEnd = spannable.getSpanEnd(span); |
| int spanToReplaceFlags = spannable.getSpanFlags(span); |
| spannable.removeSpan(span); |
| ClickableSpan replacementSpan = (span instanceof URLSpan) |
| ? new AccessibilityURLSpan((URLSpan) span) |
| : new AccessibilityClickableSpan(span.getId()); |
| spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd, |
| spanToReplaceFlags); |
| } |
| mText = spannable; |
| return; |
| } |
| } |
| mText = (text == null) ? null : text.subSequence(0, text.length()); |
| } |
| |
| /** |
| * Gets the hint text of this node. Only applies to nodes where text can be entered. |
| * |
| * @return The hint text. |
| */ |
| public CharSequence getHintText() { |
| return mHintText; |
| } |
| |
| /** |
| * Sets the hint text of this node. Only applies to nodes where text can be 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. |
| * </p> |
| * |
| * @param hintText The hint text for this mode. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setHintText(CharSequence hintText) { |
| enforceNotSealed(); |
| mHintText = (hintText == null) ? null : hintText.subSequence(0, hintText.length()); |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| mError = (error == null) ? null : error.subSequence(0, error.length()); |
| } |
| |
| /** |
| * Gets the error text of this node. |
| * |
| * @return The error text. |
| */ |
| public CharSequence getError() { |
| return mError; |
| } |
| |
| /** |
| * Gets the content description of this node. |
| * |
| * @return The content description. |
| */ |
| public CharSequence getContentDescription() { |
| return mContentDescription; |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| mContentDescription = (contentDescription == null) ? null |
| : contentDescription.subSequence(0, contentDescription.length()); |
| } |
| |
| /** |
| * Gets the tooltip text of this node. |
| * |
| * @return The tooltip text. |
| */ |
| @Nullable |
| public CharSequence getTooltipText() { |
| return mTooltipText; |
| } |
| |
| /** |
| * Sets the tooltip 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 tooltipText The tooltip text. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setTooltipText(@Nullable CharSequence tooltipText) { |
| enforceNotSealed(); |
| mTooltipText = (tooltipText == null) ? null |
| : tooltipText.subSequence(0, tooltipText.length()); |
| } |
| |
| /** |
| * 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) { |
| setLabelFor(labeled, AccessibilityNodeProvider.HOST_VIEW_ID); |
| } |
| |
| /** |
| * 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> |
| * <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 serves as a label. |
| * @param virtualDescendantId The id of the virtual descendant. |
| */ |
| public void setLabelFor(View root, int virtualDescendantId) { |
| enforceNotSealed(); |
| final int rootAccessibilityViewId = (root != null) |
| ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; |
| mLabelForId = makeNodeId(rootAccessibilityViewId, 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 AccessibilityNodeInfo#recycle()} |
| * to avoid creating of multiple instances. |
| * </p> |
| * |
| * @return The labeled info. |
| */ |
| public AccessibilityNodeInfo getLabelFor() { |
| enforceSealed(); |
| return getNodeForAccessibilityId(mConnectionId, mWindowId, mLabelForId); |
| } |
| |
| /** |
| * 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) { |
| setLabeledBy(label, AccessibilityNodeProvider.HOST_VIEW_ID); |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| final int rootAccessibilityViewId = (root != null) |
| ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; |
| mLabeledById = makeNodeId(rootAccessibilityViewId, 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 AccessibilityNodeInfo#recycle()} |
| * to avoid creating of multiple instances. |
| * </p> |
| * |
| * @return The label. |
| */ |
| public AccessibilityNodeInfo getLabeledBy() { |
| enforceSealed(); |
| return getNodeForAccessibilityId(mConnectionId, mWindowId, mLabeledById); |
| } |
| |
| /** |
| * 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 viewIdResName The id resource name. |
| */ |
| public void setViewIdResourceName(String viewIdResName) { |
| enforceNotSealed(); |
| mViewIdResourceName = viewIdResName; |
| } |
| |
| /** |
| * 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 AccessibilityNodeInfo} the |
| * client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} |
| * flag when configuring his {@link android.accessibilityservice.AccessibilityService}. |
| * </p> |
| |
| * @return The id resource name. |
| */ |
| public String getViewIdResourceName() { |
| return mViewIdResourceName; |
| } |
| |
| /** |
| * Gets the text selection start or the cursor position. |
| * <p> |
| * If no text is selected, both this method and |
| * {@link AccessibilityNodeInfo#getTextSelectionEnd()} return the same value: |
| * the current location of the cursor. |
| * </p> |
| * |
| * @return The text selection start, the cursor location if there is no selection, or -1 if |
| * there is no text selection and no cursor. |
| */ |
| public int getTextSelectionStart() { |
| return mTextSelectionStart; |
| } |
| |
| /** |
| * Gets the text selection end if text is selected. |
| * <p> |
| * If no text is selected, both this method and |
| * {@link AccessibilityNodeInfo#getTextSelectionStart()} return the same value: |
| * the current location of the cursor. |
| * </p> |
| * |
| * @return The text selection end, the cursor location if there is no selection, or -1 if |
| * there is no text selection and no cursor. |
| */ |
| public int getTextSelectionEnd() { |
| return mTextSelectionEnd; |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| mTextSelectionStart = start; |
| mTextSelectionEnd = end; |
| } |
| |
| /** |
| * Gets the input type of the source as defined by {@link InputType}. |
| * |
| * @return The input type. |
| */ |
| public int getInputType() { |
| return mInputType; |
| } |
| |
| /** |
| * 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) { |
| enforceNotSealed(); |
| mInputType = inputType; |
| } |
| |
| /** |
| * 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 (mExtras == null) { |
| mExtras = new Bundle(); |
| } |
| return mExtras; |
| } |
| |
| /** |
| * Check if a node has an extras bundle |
| * @hide |
| */ |
| public boolean hasExtras() { |
| return mExtras != null; |
| } |
| |
| /** |
| * Get the {@link TouchDelegateInfo} for touch delegate behavior with the represented view. |
| * It is possible for the same node to be pointed to by several regions. Use |
| * {@link TouchDelegateInfo#getRegionAt(int)} to get touch delegate target {@link Region}, and |
| * {@link TouchDelegateInfo#getTargetForRegion(Region)} for {@link AccessibilityNodeInfo} from |
| * the given region. |
| * |
| * @return {@link TouchDelegateInfo} or {@code null} if there are no touch delegates. |
| */ |
| @Nullable |
| public TouchDelegateInfo getTouchDelegateInfo() { |
| if (mTouchDelegateInfo != null) { |
| mTouchDelegateInfo.setConnectionId(mConnectionId); |
| mTouchDelegateInfo.setWindowId(mWindowId); |
| } |
| return mTouchDelegateInfo; |
| } |
| |
| /** |
| * Set touch delegate info if the represented view has a {@link 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> |
| * |
| * @param delegatedInfo {@link TouchDelegateInfo} returned from |
| * {@link TouchDelegate#getTouchDelegateInfo()}. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setTouchDelegateInfo(@NonNull TouchDelegateInfo delegatedInfo) { |
| enforceNotSealed(); |
| mTouchDelegateInfo = delegatedInfo; |
| } |
| |
| /** |
| * Gets the value of a boolean property. |
| * |
| * @param property The property. |
| * @return The value. |
| */ |
| private boolean getBooleanProperty(int property) { |
| return (mBooleanProperties & property) != 0; |
| } |
| |
| /** |
| * Sets a boolean property. |
| * |
| * @param property The property. |
| * @param value The value. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| private void setBooleanProperty(int property, boolean value) { |
| enforceNotSealed(); |
| if (value) { |
| mBooleanProperties |= property; |
| } else { |
| mBooleanProperties &= ~property; |
| } |
| } |
| |
| /** |
| * Sets the unique id of the IAccessibilityServiceConnection over which |
| * this instance can send requests to the system. |
| * |
| * @param connectionId The connection id. |
| * |
| * @hide |
| */ |
| public void setConnectionId(int connectionId) { |
| enforceNotSealed(); |
| mConnectionId = connectionId; |
| } |
| |
| /** |
| * Get the connection ID. |
| * |
| * @return The connection id |
| * |
| * @hide |
| */ |
| public int getConnectionId() { |
| return mConnectionId; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** |
| * Sets the id of the source node. |
| * |
| * @param sourceId The id. |
| * @param windowId The window id. |
| * |
| * @hide |
| */ |
| public void setSourceNodeId(long sourceId, int windowId) { |
| enforceNotSealed(); |
| mSourceNodeId = sourceId; |
| mWindowId = windowId; |
| } |
| |
| /** |
| * Gets the id of the source node. |
| * |
| * @return The id. |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public long getSourceNodeId() { |
| return mSourceNodeId; |
| } |
| |
| /** |
| * Sets if this instance is sealed. |
| * |
| * @param sealed Whether is sealed. |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public void setSealed(boolean sealed) { |
| mSealed = sealed; |
| } |
| |
| /** |
| * Gets if this instance is sealed. |
| * |
| * @return Whether is sealed. |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public boolean isSealed() { |
| return mSealed; |
| } |
| |
| /** |
| * Enforces that this instance is sealed. |
| * |
| * @throws IllegalStateException If this instance is not sealed. |
| * |
| * @hide |
| */ |
| protected void enforceSealed() { |
| if (!isSealed()) { |
| throw new IllegalStateException("Cannot perform this " |
| + "action on a not sealed instance."); |
| } |
| } |
| |
| private void enforceValidFocusDirection(int direction) { |
| switch (direction) { |
| case View.FOCUS_DOWN: |
| case View.FOCUS_UP: |
| case View.FOCUS_LEFT: |
| case View.FOCUS_RIGHT: |
| case View.FOCUS_FORWARD: |
| case View.FOCUS_BACKWARD: |
| return; |
| default: |
| throw new IllegalArgumentException("Unknown direction: " + direction); |
| } |
| } |
| |
| private void enforceValidFocusType(int focusType) { |
| switch (focusType) { |
| case FOCUS_INPUT: |
| case FOCUS_ACCESSIBILITY: |
| return; |
| default: |
| throw new IllegalArgumentException("Unknown focus type: " + focusType); |
| } |
| } |
| |
| /** |
| * Enforces that this instance is not sealed. |
| * |
| * @throws IllegalStateException If this instance is sealed. |
| * |
| * @hide |
| */ |
| protected void enforceNotSealed() { |
| if (isSealed()) { |
| throw new IllegalStateException("Cannot perform this " |
| + "action on a sealed instance."); |
| } |
| } |
| |
| /** |
| * Returns a cached instance if such is available otherwise a new one |
| * and sets the source. |
| * |
| * @param source The source view. |
| * @return An instance. |
| * |
| * @see #setSource(View) |
| */ |
| public static AccessibilityNodeInfo obtain(View source) { |
| AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); |
| info.setSource(source); |
| return info; |
| } |
| |
| /** |
| * 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 AccessibilityNodeInfo obtain(View root, int virtualDescendantId) { |
| AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); |
| info.setSource(root, virtualDescendantId); |
| return info; |
| } |
| |
| /** |
| * Returns a cached instance if such is available otherwise a new one. |
| * |
| * @return An instance. |
| */ |
| public static AccessibilityNodeInfo obtain() { |
| AccessibilityNodeInfo info = sPool.acquire(); |
| if (sNumInstancesInUse != null) { |
| sNumInstancesInUse.incrementAndGet(); |
| } |
| return (info != null) ? info : new AccessibilityNodeInfo(); |
| } |
| |
| /** |
| * 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 AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) { |
| AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain(); |
| infoClone.init(info); |
| return infoClone; |
| } |
| |
| /** |
| * 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() { |
| clear(); |
| sPool.release(this); |
| if (sNumInstancesInUse != null) { |
| sNumInstancesInUse.decrementAndGet(); |
| } |
| } |
| |
| /** |
| * Specify a counter that will be incremented on obtain() and decremented on recycle() |
| * |
| * @hide |
| */ |
| @TestApi |
| public static void setNumInstancesInUseCounter(AtomicInteger counter) { |
| sNumInstancesInUse = counter; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * <p> |
| * <strong>Note:</strong> After the instance is written to a parcel it |
| * is recycled. You must not touch the object after calling this function. |
| * </p> |
| */ |
| @Override |
| public void writeToParcel(Parcel parcel, int flags) { |
| writeToParcelNoRecycle(parcel, flags); |
| // Since instances of this class are fetched via synchronous i.e. blocking |
| // calls in IPCs we always recycle as soon as the instance is marshaled. |
| recycle(); |
| } |
| |
| /** @hide */ |
| @TestApi |
| public void writeToParcelNoRecycle(Parcel parcel, int flags) { |
| // Write bit set of indices of fields with values differing from default |
| long nonDefaultFields = 0; |
| int fieldIndex = 0; // index of the current field |
| if (isSealed() != DEFAULT.isSealed()) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (mSourceNodeId != DEFAULT.mSourceNodeId) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (mWindowId != DEFAULT.mWindowId) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (mParentNodeId != DEFAULT.mParentNodeId) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (mLabelForId != DEFAULT.mLabelForId) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (mLabeledById != DEFAULT.mLabeledById) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (mTraversalBefore != DEFAULT.mTraversalBefore) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (mTraversalAfter != DEFAULT.mTraversalAfter) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (mConnectionId != DEFAULT.mConnectionId) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (!LongArray.elementsEqual(mChildNodeIds, DEFAULT.mChildNodeIds)) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (!Objects.equals(mBoundsInParent, DEFAULT.mBoundsInParent)) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (!Objects.equals(mBoundsInScreen, DEFAULT.mBoundsInScreen)) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (!Objects.equals(mActions, DEFAULT.mActions)) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (mMaxTextLength != DEFAULT.mMaxTextLength) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (mMovementGranularities != DEFAULT.mMovementGranularities) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (mBooleanProperties != DEFAULT.mBooleanProperties) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (!Objects.equals(mPackageName, DEFAULT.mPackageName)) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (!Objects.equals(mClassName, DEFAULT.mClassName)) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (!Objects.equals(mText, DEFAULT.mText)) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (!Objects.equals(mHintText, DEFAULT.mHintText)) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (!Objects.equals(mError, DEFAULT.mError)) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (!Objects.equals(mContentDescription, DEFAULT.mContentDescription)) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (!Objects.equals(mPaneTitle, DEFAULT.mPaneTitle)) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (!Objects.equals(mTooltipText, DEFAULT.mTooltipText)) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (!Objects.equals(mViewIdResourceName, DEFAULT.mViewIdResourceName)) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (mTextSelectionStart != DEFAULT.mTextSelectionStart) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (mTextSelectionEnd != DEFAULT.mTextSelectionEnd) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (mInputType != DEFAULT.mInputType) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (mLiveRegion != DEFAULT.mLiveRegion) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (mDrawingOrderInParent != DEFAULT.mDrawingOrderInParent) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (!Objects.equals(mExtraDataKeys, DEFAULT.mExtraDataKeys)) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (!Objects.equals(mExtras, DEFAULT.mExtras)) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (!Objects.equals(mRangeInfo, DEFAULT.mRangeInfo)) nonDefaultFields |= bitAt(fieldIndex); |
| fieldIndex++; |
| if (!Objects.equals(mCollectionInfo, DEFAULT.mCollectionInfo)) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (!Objects.equals(mCollectionItemInfo, DEFAULT.mCollectionItemInfo)) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| fieldIndex++; |
| if (!Objects.equals(mTouchDelegateInfo, DEFAULT.mTouchDelegateInfo)) { |
| nonDefaultFields |= bitAt(fieldIndex); |
| } |
| int totalFields = fieldIndex; |
| parcel.writeLong(nonDefaultFields); |
| |
| fieldIndex = 0; |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(isSealed() ? 1 : 0); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mSourceNodeId); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mWindowId); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mParentNodeId); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mLabelForId); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mLabeledById); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mTraversalBefore); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mTraversalAfter); |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mConnectionId); |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| final LongArray childIds = mChildNodeIds; |
| if (childIds == null) { |
| parcel.writeInt(0); |
| } else { |
| final int childIdsSize = childIds.size(); |
| parcel.writeInt(childIdsSize); |
| for (int i = 0; i < childIdsSize; i++) { |
| parcel.writeLong(childIds.get(i)); |
| } |
| } |
| } |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| parcel.writeInt(mBoundsInParent.top); |
| parcel.writeInt(mBoundsInParent.bottom); |
| parcel.writeInt(mBoundsInParent.left); |
| parcel.writeInt(mBoundsInParent.right); |
| } |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| parcel.writeInt(mBoundsInScreen.top); |
| parcel.writeInt(mBoundsInScreen.bottom); |
| parcel.writeInt(mBoundsInScreen.left); |
| parcel.writeInt(mBoundsInScreen.right); |
| } |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| if (mActions != null && !mActions.isEmpty()) { |
| final int actionCount = mActions.size(); |
| |
| int nonStandardActionCount = 0; |
| long defaultStandardActions = 0; |
| for (int i = 0; i < actionCount; i++) { |
| AccessibilityAction action = mActions.get(i); |
| if (isDefaultStandardAction(action)) { |
| defaultStandardActions |= action.mSerializationFlag; |
| } else { |
| nonStandardActionCount++; |
| } |
| } |
| parcel.writeLong(defaultStandardActions); |
| |
| parcel.writeInt(nonStandardActionCount); |
| for (int i = 0; i < actionCount; i++) { |
| AccessibilityAction action = mActions.get(i); |
| if (!isDefaultStandardAction(action)) { |
| parcel.writeInt(action.getId()); |
| parcel.writeCharSequence(action.getLabel()); |
| } |
| } |
| } else { |
| parcel.writeLong(0); |
| parcel.writeInt(0); |
| } |
| } |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mMaxTextLength); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mMovementGranularities); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mBooleanProperties); |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mPackageName); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mClassName); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mText); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mHintText); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mError); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| parcel.writeCharSequence(mContentDescription); |
| } |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mPaneTitle); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mTooltipText); |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeString(mViewIdResourceName); |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mTextSelectionStart); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mTextSelectionEnd); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mInputType); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mLiveRegion); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mDrawingOrderInParent); |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeStringList(mExtraDataKeys); |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeBundle(mExtras); |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| parcel.writeInt(mRangeInfo.getType()); |
| parcel.writeFloat(mRangeInfo.getMin()); |
| parcel.writeFloat(mRangeInfo.getMax()); |
| parcel.writeFloat(mRangeInfo.getCurrent()); |
| } |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| parcel.writeInt(mCollectionInfo.getRowCount()); |
| parcel.writeInt(mCollectionInfo.getColumnCount()); |
| parcel.writeInt(mCollectionInfo.isHierarchical() ? 1 : 0); |
| parcel.writeInt(mCollectionInfo.getSelectionMode()); |
| } |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| parcel.writeInt(mCollectionItemInfo.getRowIndex()); |
| parcel.writeInt(mCollectionItemInfo.getRowSpan()); |
| parcel.writeInt(mCollectionItemInfo.getColumnIndex()); |
| parcel.writeInt(mCollectionItemInfo.getColumnSpan()); |
| parcel.writeInt(mCollectionItemInfo.isHeading() ? 1 : 0); |
| parcel.writeInt(mCollectionItemInfo.isSelected() ? 1 : 0); |
| } |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| mTouchDelegateInfo.writeToParcel(parcel, flags); |
| } |
| |
| if (DEBUG) { |
| fieldIndex--; |
| if (totalFields != fieldIndex) { |
| throw new IllegalStateException("Number of fields mismatch: " + totalFields |
| + " vs " + fieldIndex); |
| } |
| } |
| } |
| |
| /** |
| * Initializes this instance from another one. |
| * |
| * @param other The other instance. |
| */ |
| private void init(AccessibilityNodeInfo other) { |
| mSealed = other.mSealed; |
| mSourceNodeId = other.mSourceNodeId; |
| mParentNodeId = other.mParentNodeId; |
| mLabelForId = other.mLabelForId; |
| mLabeledById = other.mLabeledById; |
| mTraversalBefore = other.mTraversalBefore; |
| mTraversalAfter = other.mTraversalAfter; |
| mWindowId = other.mWindowId; |
| mConnectionId = other.mConnectionId; |
| mBoundsInParent.set(other.mBoundsInParent); |
| mBoundsInScreen.set(other.mBoundsInScreen); |
| mPackageName = other.mPackageName; |
| mClassName = other.mClassName; |
| mText = other.mText; |
| mOriginalText = other.mOriginalText; |
| mHintText = other.mHintText; |
| mError = other.mError; |
| mContentDescription = other.mContentDescription; |
| mPaneTitle = other.mPaneTitle; |
| mTooltipText = other.mTooltipText; |
| mViewIdResourceName = other.mViewIdResourceName; |
| |
| if (mActions != null) mActions.clear(); |
| final ArrayList<AccessibilityAction> otherActions = other.mActions; |
| if (otherActions != null && otherActions.size() > 0) { |
| if (mActions == null) { |
| mActions = new ArrayList(otherActions); |
| } else { |
| mActions.addAll(other.mActions); |
| } |
| } |
| |
| mBooleanProperties = other.mBooleanProperties; |
| mMaxTextLength = other.mMaxTextLength; |
| mMovementGranularities = other.mMovementGranularities; |
| |
| |
| if (mChildNodeIds != null) mChildNodeIds.clear(); |
| final LongArray otherChildNodeIds = other.mChildNodeIds; |
| if (otherChildNodeIds != null && otherChildNodeIds.size() > 0) { |
| if (mChildNodeIds == null) { |
| mChildNodeIds = otherChildNodeIds.clone(); |
| } else { |
| mChildNodeIds.addAll(otherChildNodeIds); |
| } |
| } |
| |
| mTextSelectionStart = other.mTextSelectionStart; |
| mTextSelectionEnd = other.mTextSelectionEnd; |
| mInputType = other.mInputType; |
| mLiveRegion = other.mLiveRegion; |
| mDrawingOrderInParent = other.mDrawingOrderInParent; |
| |
| mExtraDataKeys = other.mExtraDataKeys; |
| |
| mExtras = other.mExtras != null ? new Bundle(other.mExtras) : null; |
| |
| if (mRangeInfo != null) mRangeInfo.recycle(); |
| mRangeInfo = (other.mRangeInfo != null) |
| ? RangeInfo.obtain(other.mRangeInfo) : null; |
| if (mCollectionInfo != null) mCollectionInfo.recycle(); |
| mCollectionInfo = (other.mCollectionInfo != null) |
| ? CollectionInfo.obtain(other.mCollectionInfo) : null; |
| if (mCollectionItemInfo != null) mCollectionItemInfo.recycle(); |
| mCollectionItemInfo = (other.mCollectionItemInfo != null) |
| ? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null; |
| |
| final TouchDelegateInfo otherInfo = other.mTouchDelegateInfo; |
| mTouchDelegateInfo = (otherInfo != null) |
| ? new TouchDelegateInfo(otherInfo.mTargetMap, true) : null; |
| } |
| |
| /** |
| * Creates a new instance from a {@link Parcel}. |
| * |
| * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}. |
| */ |
| private void initFromParcel(Parcel parcel) { |
| // Bit mask of non-default-valued field indices |
| long nonDefaultFields = parcel.readLong(); |
| int fieldIndex = 0; |
| final boolean sealed = isBitSet(nonDefaultFields, fieldIndex++) |
| ? (parcel.readInt() == 1) |
| : DEFAULT.mSealed; |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mSourceNodeId = parcel.readLong(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mWindowId = parcel.readInt(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mParentNodeId = parcel.readLong(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mLabelForId = parcel.readLong(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mLabeledById = parcel.readLong(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mTraversalBefore = parcel.readLong(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mTraversalAfter = parcel.readLong(); |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mConnectionId = parcel.readInt(); |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| final int childrenSize = parcel.readInt(); |
| if (childrenSize <= 0) { |
| mChildNodeIds = null; |
| } else { |
| mChildNodeIds = new LongArray(childrenSize); |
| for (int i = 0; i < childrenSize; i++) { |
| final long childId = parcel.readLong(); |
| mChildNodeIds.add(childId); |
| } |
| } |
| } |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| mBoundsInParent.top = parcel.readInt(); |
| mBoundsInParent.bottom = parcel.readInt(); |
| mBoundsInParent.left = parcel.readInt(); |
| mBoundsInParent.right = parcel.readInt(); |
| } |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| mBoundsInScreen.top = parcel.readInt(); |
| mBoundsInScreen.bottom = parcel.readInt(); |
| mBoundsInScreen.left = parcel.readInt(); |
| mBoundsInScreen.right = parcel.readInt(); |
| } |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| final long standardActions = parcel.readLong(); |
| addStandardActions(standardActions); |
| final int nonStandardActionCount = parcel.readInt(); |
| for (int i = 0; i < nonStandardActionCount; i++) { |
| final AccessibilityAction action = new AccessibilityAction( |
| parcel.readInt(), parcel.readCharSequence()); |
| addActionUnchecked(action); |
| } |
| } |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mMaxTextLength = parcel.readInt(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mMovementGranularities = parcel.readInt(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mBooleanProperties = parcel.readInt(); |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mPackageName = parcel.readCharSequence(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mClassName = parcel.readCharSequence(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mText = parcel.readCharSequence(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mHintText = parcel.readCharSequence(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mError = parcel.readCharSequence(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| mContentDescription = parcel.readCharSequence(); |
| } |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readCharSequence(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString(); |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mTextSelectionStart = parcel.readInt(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mTextSelectionEnd = parcel.readInt(); |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mInputType = parcel.readInt(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mLiveRegion = parcel.readInt(); |
| if (isBitSet(nonDefaultFields, fieldIndex++)) mDrawingOrderInParent = parcel.readInt(); |
| |
| mExtraDataKeys = isBitSet(nonDefaultFields, fieldIndex++) |
| ? parcel.createStringArrayList() |
| : null; |
| |
| mExtras = isBitSet(nonDefaultFields, fieldIndex++) |
| ? parcel.readBundle() |
| : null; |
| |
| if (mRangeInfo != null) mRangeInfo.recycle(); |
| mRangeInfo = isBitSet(nonDefaultFields, fieldIndex++) |
| ? RangeInfo.obtain( |
| parcel.readInt(), |
| parcel.readFloat(), |
| parcel.readFloat(), |
| parcel.readFloat()) |
| : null; |
| |
| if (mCollectionInfo != null) mCollectionInfo.recycle(); |
| mCollectionInfo = isBitSet(nonDefaultFields, fieldIndex++) |
| ? CollectionInfo.obtain( |
| parcel.readInt(), |
| parcel.readInt(), |
| parcel.readInt() == 1, |
| parcel.readInt()) |
| : null; |
| |
| if (mCollectionItemInfo != null) mCollectionItemInfo.recycle(); |
| mCollectionItemInfo = isBitSet(nonDefaultFields, fieldIndex++) |
| ? CollectionItemInfo.obtain( |
| parcel.readInt(), |
| parcel.readInt(), |
| parcel.readInt(), |
| parcel.readInt(), |
| parcel.readInt() == 1, |
| parcel.readInt() == 1) |
| : null; |
| |
| if (isBitSet(nonDefaultFields, fieldIndex++)) { |
| mTouchDelegateInfo = TouchDelegateInfo.CREATOR.createFromParcel(parcel); |
| } |
| |
| mSealed = sealed; |
| } |
| |
| /** |
| * Clears the state of this instance. |
| */ |
| private void clear() { |
| init(DEFAULT); |
| } |
| |
| private static boolean isDefaultStandardAction(AccessibilityAction action) { |
| return (action.mSerializationFlag != -1L) && TextUtils.isEmpty(action.getLabel()); |
| } |
| |
| private static AccessibilityAction getActionSingleton(int actionId) { |
| final int actions = AccessibilityAction.sStandardActions.size(); |
| for (int i = 0; i < actions; i++) { |
| AccessibilityAction currentAction = AccessibilityAction.sStandardActions.valueAt(i); |
| if (actionId == currentAction.getId()) { |
| return currentAction; |
| } |
| } |
| |
| return null; |
| } |
| |
| private static AccessibilityAction getActionSingletonBySerializationFlag(long flag) { |
| final int actions = AccessibilityAction.sStandardActions.size(); |
| for (int i = 0; i < actions; i++) { |
| AccessibilityAction currentAction = AccessibilityAction.sStandardActions.valueAt(i); |
| if (flag == currentAction.mSerializationFlag) { |
| return currentAction; |
| } |
| } |
| |
| return null; |
| } |
| |
| private void addStandardActions(long serializationIdMask) { |
| long remainingIds = serializationIdMask; |
| while (remainingIds > 0) { |
| final long id = 1L << Long.numberOfTrailingZeros(remainingIds); |
| remainingIds &= ~id; |
| AccessibilityAction action = getActionSingletonBySerializationFlag(id); |
| addAction(action); |
| } |
| } |
| |
| /** |
| * Gets the human readable action symbolic name. |
| * |
| * @param action The action. |
| * @return The symbolic name. |
| */ |
| 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_DISMISS: |
| return "ACTION_DISMISS"; |
| case ACTION_SET_TEXT: |
| return "ACTION_SET_TEXT"; |
| case R.id.accessibilityActionShowOnScreen: |
| return "ACTION_SHOW_ON_SCREEN"; |
| case R.id.accessibilityActionScrollToPosition: |
| return "ACTION_SCROLL_TO_POSITION"; |
| case R.id.accessibilityActionScrollUp: |
| return "ACTION_SCROLL_UP"; |
| case R.id.accessibilityActionScrollLeft: |
| return "ACTION_SCROLL_LEFT"; |
| case R.id.accessibilityActionScrollDown: |
| return "ACTION_SCROLL_DOWN"; |
| case R.id.accessibilityActionScrollRight: |
| return "ACTION_SCROLL_RIGHT"; |
| case R.id.accessibilityActionPageDown: |
| return "ACTION_PAGE_DOWN"; |
| case R.id.accessibilityActionPageUp: |
| return "ACTION_PAGE_UP"; |
| case R.id.accessibilityActionPageLeft: |
| return "ACTION_PAGE_LEFT"; |
| case R.id.accessibilityActionPageRight: |
| return "ACTION_PAGE_RIGHT"; |
| case R.id.accessibilityActionSetProgress: |
| return "ACTION_SET_PROGRESS"; |
| case R.id.accessibilityActionContextClick: |
| return "ACTION_CONTEXT_CLICK"; |
| case R.id.accessibilityActionShowTooltip: |
| return "ACTION_SHOW_TOOLTIP"; |
| case R.id.accessibilityActionHideTooltip: |
| return "ACTION_HIDE_TOOLTIP"; |
| default: |
| return "ACTION_UNKNOWN"; |
| } |
| } |
| |
| /** |
| * Gets the human readable movement granularity symbolic name. |
| * |
| * @param granularity The granularity. |
| * @return The symbolic name. |
| */ |
| private static String getMovementGranularitySymbolicName(int granularity) { |
| switch (granularity) { |
| case MOVEMENT_GRANULARITY_CHARACTER: |
| return "MOVEMENT_GRANULARITY_CHARACTER"; |
| case MOVEMENT_GRANULARITY_WORD: |
| return "MOVEMENT_GRANULARITY_WORD"; |
| case MOVEMENT_GRANULARITY_LINE: |
| return "MOVEMENT_GRANULARITY_LINE"; |
| case MOVEMENT_GRANULARITY_PARAGRAPH: |
| return "MOVEMENT_GRANULARITY_PARAGRAPH"; |
| case MOVEMENT_GRANULARITY_PAGE: |
| return "MOVEMENT_GRANULARITY_PAGE"; |
| default: |
| throw new IllegalArgumentException("Unknown movement granularity: " + granularity); |
| } |
| } |
| |
| private static boolean canPerformRequestOverConnection(int connectionId, |
| int windowId, long accessibilityNodeId) { |
| return ((windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) |
| && (getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID) |
| && (connectionId != UNDEFINED_CONNECTION_ID)); |
| } |
| |
| @Override |
| public boolean equals(Object object) { |
| if (this == object) { |
| return true; |
| } |
| if (object == null) { |
| return false; |
| } |
| if (getClass() != object.getClass()) { |
| return false; |
| } |
| AccessibilityNodeInfo other = (AccessibilityNodeInfo) object; |
| if (mSourceNodeId != other.mSourceNodeId) { |
| return false; |
| } |
| if (mWindowId != other.mWindowId) { |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + getAccessibilityViewId(mSourceNodeId); |
| result = prime * result + getVirtualDescendantId(mSourceNodeId); |
| result = prime * result + mWindowId; |
| return result; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder builder = new StringBuilder(); |
| builder.append(super.toString()); |
| |
| if (DEBUG) { |
| builder.append("; sourceNodeId: " + mSourceNodeId); |
| builder.append("; windowId: " + mWindowId); |
| builder.append("; accessibilityViewId: ").append(getAccessibilityViewId(mSourceNodeId)); |
| builder.append("; virtualDescendantId: ").append(getVirtualDescendantId(mSourceNodeId)); |
| builder.append("; mParentNodeId: " + mParentNodeId); |
| builder.append("; traversalBefore: ").append(mTraversalBefore); |
| builder.append("; traversalAfter: ").append(mTraversalAfter); |
| |
| int granularities = mMovementGranularities; |
| builder.append("; MovementGranularities: ["); |
| while (granularities != 0) { |
| final int granularity = 1 << Integer.numberOfTrailingZeros(granularities); |
| granularities &= ~granularity; |
| builder.append(getMovementGranularitySymbolicName(granularity)); |
| if (granularities != 0) { |
| builder.append(", "); |
| } |
| } |
| builder.append("]"); |
| |
| builder.append("; childAccessibilityIds: ["); |
| final LongArray childIds = mChildNodeIds; |
| if (childIds != null) { |
| for (int i = 0, count = childIds.size(); i < count; i++) { |
| builder.append(childIds.get(i)); |
| if (i < count - 1) { |
| builder.append(", "); |
| } |
| } |
| } |
| builder.append("]"); |
| } |
| |
| builder.append("; boundsInParent: ").append(mBoundsInParent); |
| builder.append("; boundsInScreen: ").append(mBoundsInScreen); |
| |
| builder.append("; packageName: ").append(mPackageName); |
| builder.append("; className: ").append(mClassName); |
| builder.append("; text: ").append(mText); |
| builder.append("; error: ").append(mError); |
| builder.append("; maxTextLength: ").append(mMaxTextLength); |
| builder.append("; contentDescription: ").append(mContentDescription); |
| builder.append("; tooltipText: ").append(mTooltipText); |
| builder.append("; viewIdResName: ").append(mViewIdResourceName); |
| |
| builder.append("; checkable: ").append(isCheckable()); |
| builder.append("; checked: ").append(isChecked()); |
| builder.append("; focusable: ").append(isFocusable()); |
| builder.append("; focused: ").append(isFocused()); |
| builder.append("; selected: ").append(isSelected()); |
| builder.append("; clickable: ").append(isClickable()); |
| builder.append("; longClickable: ").append(isLongClickable()); |
| builder.append("; contextClickable: ").append(isContextClickable()); |
| builder.append("; enabled: ").append(isEnabled()); |
| builder.append("; password: ").append(isPassword()); |
| builder.append("; scrollable: ").append(isScrollable()); |
| builder.append("; importantForAccessibility: ").append(isImportantForAccessibility()); |
| builder.append("; visible: ").append(isVisibleToUser()); |
| builder.append("; actions: ").append(mActions); |
| |
| return builder.toString(); |
| } |
| |
| private static AccessibilityNodeInfo getNodeForAccessibilityId(int connectionId, |
| int windowId, long accessibilityId) { |
| if (!canPerformRequestOverConnection(connectionId, windowId, accessibilityId)) { |
| return null; |
| } |
| AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); |
| return client.findAccessibilityNodeInfoByAccessibilityId(connectionId, |
| windowId, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS |
| | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS, null); |
| } |
| |
| /** @hide */ |
| public static String idToString(long accessibilityId) { |
| int accessibilityViewId = getAccessibilityViewId(accessibilityId); |
| int virtualDescendantId = getVirtualDescendantId(accessibilityId); |
| return virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID |
| ? idItemToString(accessibilityViewId) |
| : idItemToString(accessibilityViewId) + ":" + idItemToString(virtualDescendantId); |
| } |
| |
| private static String idItemToString(int item) { |
| switch (item) { |
| case ROOT_ITEM_ID: return "ROOT"; |
| case UNDEFINED_ITEM_ID: return "UNDEFINED"; |
| case AccessibilityNodeProvider.HOST_VIEW_ID: return "HOST"; |
| default: return "" + item; |
| } |
| } |
| |
| /** |
| * A class defining an action that can be performed on an {@link AccessibilityNodeInfo}. |
| * Each action has a unique id that is mandatory and optional data. |
| * <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> |
| * Actions are typically added to an {@link AccessibilityNodeInfo} by using |
| * {@link AccessibilityNodeInfo#addAction(AccessibilityAction)} within |
| * {@link View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} and are performed |
| * within {@link View#performAccessibilityAction(int, Bundle)}. |
| * </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 AccessibilityService} |
| * can discover the set of supported actions. |
| * </p> |
| */ |
| public static final class AccessibilityAction { |
| |
| /** @hide */ |
| public static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<>(); |
| |
| /** |
| * Action that gives input focus to the node. |
| */ |
| public static final AccessibilityAction ACTION_FOCUS = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_FOCUS); |
| |
| /** |
| * Action that clears input focus of the node. |
| */ |
| public static final AccessibilityAction ACTION_CLEAR_FOCUS = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS); |
| |
| /** |
| * Action that selects the node. |
| */ |
| public static final AccessibilityAction ACTION_SELECT = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_SELECT); |
| |
| /** |
| * Action that deselects the node. |
| */ |
| public static final AccessibilityAction ACTION_CLEAR_SELECTION = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION); |
| |
| /** |
| * Action that clicks on the node info. |
| */ |
| public static final AccessibilityAction ACTION_CLICK = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK); |
| |
| /** |
| * Action that long clicks on the node. |
| */ |
| public static final AccessibilityAction ACTION_LONG_CLICK = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_LONG_CLICK); |
| |
| /** |
| * Action that gives accessibility focus to the node. |
| */ |
| public static final AccessibilityAction ACTION_ACCESSIBILITY_FOCUS = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); |
| |
| /** |
| * Action that clears accessibility focus of the node. |
| */ |
| public static final AccessibilityAction ACTION_CLEAR_ACCESSIBILITY_FOCUS = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS); |
| |
| /** |
| * 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 AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT |
| * AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}, |
| * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN |
| * AccessibilityNodeInfo.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(AccessibilityAction.ACTION_NEXT_AT_MOVEMENT_GRANULARITY.getId(), |
| * arguments); |
| * </code></pre></p> |
| * </p> |
| * |
| * @see AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT |
| * AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT |
| * @see AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN |
| * AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN |
| * |
| * @see AccessibilityNodeInfo#setMovementGranularities(int) |
| * AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN |
| * @see AccessibilityNodeInfo#getMovementGranularities() |
| * AccessibilityNodeInfo.getMovementGranularities() |
| * |
| * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_CHARACTER |
| * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER |
| * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_WORD |
| * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD |
| * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_LINE |
| * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE |
| * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PARAGRAPH |
| * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH |
| * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PAGE |
| * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE |
| */ |
| public static final AccessibilityAction ACTION_NEXT_AT_MOVEMENT_GRANULARITY = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); |
| |
| /** |
| * Action that requests to go to the previous entity in this node's text |
| * at a given movement granularity. For example, move to the next character, |
| * word, etc. |
| * <p> |
| * <strong>Arguments:</strong> |
| * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT |
| * AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}, |
| * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN |
| * AccessibilityNodeInfo.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(AccessibilityAction.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY.getId(), |
| * arguments); |
| * </code></pre></p> |
| * </p> |
| * |
| * @see AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT |
| * AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT |
| * @see AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN |
| * AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN |
| * |
| * @see AccessibilityNodeInfo#setMovementGranularities(int) |
| * AccessibilityNodeInfo.setMovementGranularities(int) |
| * @see AccessibilityNodeInfo#getMovementGranularities() |
| * AccessibilityNodeInfo.getMovementGranularities() |
| * |
| * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_CHARACTER |
| * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER |
| * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_WORD |
| * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD |
| * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_LINE |
| * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE |
| * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PARAGRAPH |
| * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH |
| * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PAGE |
| * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE |
| */ |
| public static final AccessibilityAction ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = |
| new AccessibilityAction( |
| AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); |
| |
| /** |
| * 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 AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING |
| * AccessibilityNodeInfo.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(AccessibilityAction.ACTION_NEXT_HTML_ELEMENT.getId(), arguments); |
| * </code></pre></p> |
| * </p> |
| */ |
| public static final AccessibilityAction ACTION_NEXT_HTML_ELEMENT = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT); |
| |
| /** |
| * 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 AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING |
| * AccessibilityNodeInfo.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(AccessibilityAction.ACTION_PREVIOUS_HTML_ELEMENT.getId(), arguments); |
| * </code></pre></p> |
| * </p> |
| */ |
| public static final AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT); |
| |
| /** |
| * Action to scroll the node content forward. |
| */ |
| public static final AccessibilityAction ACTION_SCROLL_FORWARD = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); |
| |
| /** |
| * Action to scroll the node content backward. |
| */ |
| public static final AccessibilityAction ACTION_SCROLL_BACKWARD = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); |
| |
| /** |
| * Action to copy the current selection to the clipboard. |
| */ |
| public static final AccessibilityAction ACTION_COPY = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_COPY); |
| |
| /** |
| * Action to paste the current clipboard content. |
| */ |
| public static final AccessibilityAction ACTION_PASTE = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_PASTE); |
| |
| /** |
| * Action to cut the current selection and place it to the clipboard. |
| */ |
| public static final AccessibilityAction ACTION_CUT = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_CUT); |
| |
| /** |
| * Action to set the selection. Performing this action with no arguments |
| * clears the selection. |
| * <p> |
| * <strong>Arguments:</strong> |
| * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT |
| * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT}, |
| * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT |
| * AccessibilityNodeInfo.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(AccessibilityAction.ACTION_SET_SELECTION.getId(), arguments); |
| * </code></pre></p> |
| * </p> |
| * |
| * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT |
| * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT |
| * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT |
| * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT |
| */ |
| public static final AccessibilityAction ACTION_SET_SELECTION = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_SET_SELECTION); |
| |
| /** |
| * Action to expand an expandable node. |
| */ |
| public static final AccessibilityAction ACTION_EXPAND = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_EXPAND); |
| |
| /** |
| * Action to collapse an expandable node. |
| */ |
| public static final AccessibilityAction ACTION_COLLAPSE = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_COLLAPSE); |
| |
| /** |
| * Action to dismiss a dismissable node. |
| */ |
| public static final AccessibilityAction ACTION_DISMISS = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_DISMISS); |
| |
| /** |
| * 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 AccessibilityNodeInfo#ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE |
| * AccessibilityNodeInfo.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(AccessibilityAction.ACTION_SET_TEXT.getId(), arguments); |
| * </code></pre></p> |
| */ |
| public static final AccessibilityAction ACTION_SET_TEXT = |
| new AccessibilityAction(AccessibilityNodeInfo.ACTION_SET_TEXT); |
| |
| /** |
| * 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 AccessibilityAction ACTION_SHOW_ON_SCREEN = |
| new AccessibilityAction(R.id.accessibilityActionShowOnScreen); |
| |
| /** |
| * Action that scrolls the node to make the specified collection |
| * position visible on screen. |
| * <p> |
| * <strong>Arguments:</strong> |
| * <ul> |
| * <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_ROW_INT}</li> |
| * <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_COLUMN_INT}</li> |
| * <ul> |
| * |
| * @see AccessibilityNodeInfo#getCollectionInfo() |
| */ |
| public static final AccessibilityAction ACTION_SCROLL_TO_POSITION = |
| new AccessibilityAction(R.id.accessibilityActionScrollToPosition); |
| |
| /** |
| * Action to scroll the node content up. |
| */ |
| public static final AccessibilityAction ACTION_SCROLL_UP = |
| new AccessibilityAction(R.id.accessibilityActionScrollUp); |
| |
| /** |
| * Action to scroll the node content left. |
| */ |
| public static final AccessibilityAction ACTION_SCROLL_LEFT = |
| new AccessibilityAction(R.id.accessibilityActionScrollLeft); |
| |
| /** |
| * Action to scroll the node content down. |
| */ |
| public static final AccessibilityAction ACTION_SCROLL_DOWN = |
| new AccessibilityAction(R.id.accessibilityActionScrollDown); |
| |
| /** |
| * Action to scroll the node content right. |
| */ |
| public static final AccessibilityAction ACTION_SCROLL_RIGHT = |
| new AccessibilityAction(R.id.accessibilityActionScrollRight); |
| |
| /** |
| * Action to move to the page above. |
| */ |
| public static final AccessibilityAction ACTION_PAGE_UP = |
| new AccessibilityAction(R.id.accessibilityActionPageUp); |
| |
| /** |
| * Action to move to the page below. |
| */ |
| public static final AccessibilityAction ACTION_PAGE_DOWN = |
| new AccessibilityAction(R.id.accessibilityActionPageDown); |
| |
| /** |
| * Action to move to the page left. |
| */ |
| public static final AccessibilityAction ACTION_PAGE_LEFT = |
| new AccessibilityAction(R.id.accessibilityActionPageLeft); |
| |
| /** |
| * Action to move to the page right. |
| */ |
| public static final AccessibilityAction ACTION_PAGE_RIGHT = |
| new AccessibilityAction(R.id.accessibilityActionPageRight); |
| |
| /** |
| * Action that context clicks the node. |
| */ |
| public static final AccessibilityAction ACTION_CONTEXT_CLICK = |
| new AccessibilityAction(R.id.accessibilityActionContextClick); |
| |
| /** |
| * Action that sets progress between {@link RangeInfo#getMin() RangeInfo.getMin()} and |
| * {@link RangeInfo#getMax() RangeInfo.getMax()}. It should use the same value type as |
| * {@link RangeInfo#getType() RangeInfo.getType()} |
| * <p> |
| * <strong>Arguments:</strong> |
| * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_PROGRESS_VALUE} |
| * |
| * @see RangeInfo |
| */ |
| public static final AccessibilityAction ACTION_SET_PROGRESS = |
| new AccessibilityAction(R.id.accessibilityActionSetProgress); |
| |
| /** |
| * Action to move a window to a new location. |
| * <p> |
| * <strong>Arguments:</strong> |
| * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVE_WINDOW_X} |
| * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVE_WINDOW_Y} |
| */ |
| public static final AccessibilityAction ACTION_MOVE_WINDOW = |
| new AccessibilityAction(R.id.accessibilityActionMoveWindow); |
| |
| /** |
| * Action to show a tooltip. A node should expose this action only for views with tooltip |
| * text that but are not currently showing a tooltip. |
| */ |
| public static final AccessibilityAction ACTION_SHOW_TOOLTIP = |
| new AccessibilityAction(R.id.accessibilityActionShowTooltip); |
| |
| /** |
| * Action to hide a tooltip. A node should expose this action only for views that are |
| * currently showing a tooltip. |
| */ |
| public static final AccessibilityAction ACTION_HIDE_TOOLTIP = |
| new AccessibilityAction(R.id.accessibilityActionHideTooltip); |
| |
| private final int mActionId; |
| private final CharSequence mLabel; |
| |
| /** @hide */ |
| public long mSerializationFlag = -1L; |
| |
| /** |
| * Creates a new AccessibilityAction. For adding a standard action without a specific label, |
| * use the static constants. |
| * |
| * You can also override the description for one the standard actions. Below is an example |
| * how to override the standard click action by adding a custom label: |
| * <pre> |
| * AccessibilityAction action = new AccessibilityAction( |
| * AccessibilityAction.ACTION_CLICK.getId(), getLocalizedLabel()); |
| * node.addAction(action); |
| * </pre> |
| * |
| * @param actionId The id for this action. This should either be one of the |
| * standard actions or a specific action for your app. In that case it is |
| * required to use a resource identifier. |
| * @param label The label for the new AccessibilityAction. |
| */ |
| public AccessibilityAction(int actionId, @Nullable CharSequence label) { |
| if ((actionId & ACTION_TYPE_MASK) == 0 && Integer.bitCount(actionId) != 1) { |
| throw new IllegalArgumentException("Invalid standard action id"); |
| } |
| |
| mActionId = actionId; |
| mLabel = label; |
| } |
| |
| /** |
| * Constructor for a {@link #sStandardActions standard} action |
| */ |
| private AccessibilityAction(int standardActionId) { |
| this(standardActionId, null); |
| |
| mSerializationFlag = bitAt(sStandardActions.size()); |
| sStandardActions.add(this); |
| } |
| |
| /** |
| * Gets the id for this action. |
| * |
| * @return The action id. |
| */ |
| public int getId() { |
| return mActionId; |
| } |
| |
| /** |
| * Gets the label for this action. Its purpose is to describe the |
| * action to user. |
| * |
| * @return The label. |
| */ |
| public CharSequence getLabel() { |
| return mLabel; |
| } |
| |
| @Override |
| public int hashCode() { |
| return mActionId; |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| if (other == null) { |
| return false; |
| } |
| |
| if (other == this) { |
| return true; |
| } |
| |
| if (getClass() != other.getClass()) { |
| return false; |
| } |
| |
| return mActionId == ((AccessibilityAction)other).mActionId; |
| } |
| |
| @Override |
| public String toString() { |
| return "AccessibilityAction: " + getActionSymbolicName(mActionId) + " - " + mLabel; |
| } |
| } |
| |
| /** |
| * Class with information if a node is a range. Use |
| * {@link RangeInfo#obtain(int, float, float, float)} to get an instance. Recycling is |
| * handled by the {@link AccessibilityNodeInfo} to which this object is attached. |
| */ |
| public static final class RangeInfo { |
| private static final int MAX_POOL_SIZE = 10; |
| |
| /** 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; |
| |
| private static final SynchronizedPool<RangeInfo> sPool = |
| new SynchronizedPool<AccessibilityNodeInfo.RangeInfo>(MAX_POOL_SIZE); |
| |
| private int mType; |
| private float mMin; |
| private float mMax; |
| private float mCurrent; |
| |
| /** |
| * Obtains a pooled instance that is a clone of another one. |
| * |
| * @param other The instance to clone. |
| * |
| * @hide |
| */ |
| public static RangeInfo obtain(RangeInfo other) { |
| return obtain(other.mType, other.mMin, other.mMax, other.mCurrent); |
| } |
| |
| /** |
| * Obtains a pooled instance. |
| * |
| * @param type The type of the range. |
| * @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no |
| * minimum. |
| * @param max The maximum value. Use {@code Float.POSITIVE_INFINITY} if the range has no |
| * maximum. |
| * @param current The current value. |
| */ |
| public static RangeInfo obtain(int type, float min, float max, float current) { |
| RangeInfo info = sPool.acquire(); |
| if (info == null) { |
| return new RangeInfo(type, min, max, current); |
| } |
| |
| info.mType = type; |
| info.mMin = min; |
| info.mMax = max; |
| info.mCurrent = current; |
| return info; |
| } |
| |
| /** |
| * Creates a new range. |
| * |
| * @param type The type of the range. |
| * @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no |
| * minimum. |
| * @param max The maximum value. Use {@code Float.POSITIVE_INFINITY} if the range has no |
| * maximum. |
| * @param current The current value. |
| */ |
| private RangeInfo(int type, float min, float max, float current) { |
| mType = type; |
| mMin = min; |
| mMax = max; |
| mCurrent = current; |
| } |
| |
| /** |
| * Gets the range type. |
| * |
| * @return The range type. |
| * |
| * @see #RANGE_TYPE_INT |
| * @see #RANGE_TYPE_FLOAT |
| * @see #RANGE_TYPE_PERCENT |
| */ |
| public int getType() { |
| return mType; |
| } |
| |
| /** |
| * Gets the minimum value. |
| * |
| * @return The minimum value, or {@code Float.NEGATIVE_INFINITY} if no minimum exists. |
| */ |
| public float getMin() { |
| return mMin; |
| } |
| |
| /** |
| * Gets the maximum value. |
| * |
| * @return The maximum value, or {@code Float.POSITIVE_INFINITY} if no maximum exists. |
| */ |
| public float getMax() { |
| return mMax; |
| } |
| |
| /** |
| * Gets the current value. |
| * |
| * @return The current value. |
| */ |
| public float getCurrent() { |
| return mCurrent; |
| } |
| |
| /** |
| * Recycles this instance. |
| */ |
| void recycle() { |
| clear(); |
| sPool.release(this); |
| } |
| |
| private void clear() { |
| mType = 0; |
| mMin = 0; |
| mMax = 0; |
| mCurrent = 0; |
| } |
| } |
| |
| /** |
| * Class with information if a node is a collection. Use |
| * {@link CollectionInfo#obtain(int, int, boolean)} to get an instance. Recycling is |
| * handled by the {@link AccessibilityNodeInfo} to which this object is attached. |
| * <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 final class CollectionInfo { |
| /** 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; |
| |
| private static final int MAX_POOL_SIZE = 20; |
| |
| private static final SynchronizedPool<CollectionInfo> sPool = |
| new SynchronizedPool<>(MAX_POOL_SIZE); |
| |
| private int mRowCount; |
| private int mColumnCount; |
| private boolean mHierarchical; |
| private int mSelectionMode; |
| |
| /** |
| * Obtains a pooled instance that is a clone of another one. |
| * |
| * @param other The instance to clone. |
| * @hide |
| */ |
| public static CollectionInfo obtain(CollectionInfo other) { |
| return CollectionInfo.obtain(other.mRowCount, other.mColumnCount, other.mHierarchical, |
| other.mSelectionMode); |
| } |
| |
| /** |
| * Obtains a pooled instance. |
| * |
| * @param rowCount The number of rows. |
| * @param columnCount The number of columns. |
| * @param hierarchical Whether the collection is hierarchical. |
| */ |
| public static CollectionInfo obtain(int rowCount, int columnCount, |
| boolean hierarchical) { |
| return obtain(rowCount, columnCount, hierarchical, SELECTION_MODE_NONE); |
| } |
| |
| /** |
| * Obtains a pooled instance. |
| * |
| * @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> |
| */ |
| public static CollectionInfo obtain(int rowCount, int columnCount, |
| boolean hierarchical, int selectionMode) { |
| final CollectionInfo info = sPool.acquire(); |
| if (info == null) { |
| return new CollectionInfo(rowCount, columnCount, hierarchical, selectionMode); |
| } |
| |
| info.mRowCount = rowCount; |
| info.mColumnCount = columnCount; |
| info.mHierarchical = hierarchical; |
| info.mSelectionMode = selectionMode; |
| return info; |
| } |
| |
| /** |
| * Creates a new instance. |
| * |
| * @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. |
| */ |
| private CollectionInfo(int rowCount, int columnCount, boolean hierarchical, |
| int selectionMode) { |
| mRowCount = rowCount; |
| mColumnCount = columnCount; |
| mHierarchical = hierarchical; |
| mSelectionMode = selectionMode; |
| } |
| |
| /** |
| * Gets the number of rows. |
| * |
| * @return The row count. |
| */ |
| public int getRowCount() { |
| return mRowCount; |
| } |
| |
| /** |
| * Gets the number of columns. |
| * |
| * @return The column count. |
| */ |
| public int getColumnCount() { |
| return mColumnCount; |
| } |
| |
| /** |
| * Gets if the collection is a hierarchically ordered. |
| * |
| * @return Whether the collection is hierarchical. |
| */ |
| public boolean isHierarchical() { |
| return mHierarchical; |
| } |
| |
| /** |
| * 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() { |
| return mSelectionMode; |
| } |
| |
| /** |
| * Recycles this instance. |
| */ |
| void recycle() { |
| clear(); |
| sPool.release(this); |
| } |
| |
| private void clear() { |
| mRowCount = 0; |
| mColumnCount = 0; |
| mHierarchical = false; |
| mSelectionMode = SELECTION_MODE_NONE; |
| } |
| } |
| |
| /** |
| * Class with information if a node is a collection item. Use |
| * {@link CollectionItemInfo#obtain(int, int, int, int, boolean)} |
| * to get an instance. Recycling is handled by the {@link AccessibilityNodeInfo} to which this |
| * object is attached. |
| * <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 final class CollectionItemInfo { |
| private static final int MAX_POOL_SIZE = 20; |
| |
| private static final SynchronizedPool<CollectionItemInfo> sPool = |
| new SynchronizedPool<>(MAX_POOL_SIZE); |
| |
| /** |
| * Obtains a pooled instance that is a clone of another one. |
| * |
| * @param other The instance to clone. |
| * @hide |
| */ |
| public static CollectionItemInfo obtain(CollectionItemInfo other) { |
| return CollectionItemInfo.obtain(other.mRowIndex, other.mRowSpan, other.mColumnIndex, |
| other.mColumnSpan, other.mHeading, other.mSelected); |
| } |
| |
| /** |
| * Obtains a pooled instance. |
| * |
| * @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. (Prefer |
| * {@link AccessibilityNodeInfo#setHeading(boolean)}). |
| */ |
| public static CollectionItemInfo obtain(int rowIndex, int rowSpan, |
| int columnIndex, int columnSpan, boolean heading) { |
| return obtain(rowIndex, rowSpan, columnIndex, columnSpan, heading, false); |
| } |
| |
| /** |
| * Obtains a pooled instance. |
| * |
| * @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. (Prefer |
| * {@link AccessibilityNodeInfo#setHeading(boolean)}) |
| * @param selected Whether the item is selected. |
| */ |
| public static CollectionItemInfo obtain(int rowIndex, int rowSpan, |
| int columnIndex, int columnSpan, boolean heading, boolean selected) { |
| final CollectionItemInfo info = sPool.acquire(); |
| if (info == null) { |
| return new CollectionItemInfo( |
| rowIndex, rowSpan, columnIndex, columnSpan, heading, selected); |
| } |
| |
| info.mRowIndex = rowIndex; |
| info.mRowSpan = rowSpan; |
| info.mColumnIndex = columnIndex; |
| info.mColumnSpan = columnSpan; |
| info.mHeading = heading; |
| info.mSelected = selected; |
| return info; |
| } |
| |
| private boolean mHeading; |
| private int mColumnIndex; |
| private int mRowIndex; |
| private int mColumnSpan; |
| private int mRowSpan; |
| private boolean mSelected; |
| |
| /** |
| * Creates a new instance. |
| * |
| * @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. |
| */ |
| private CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan, |
| boolean heading, boolean selected) { |
| mRowIndex = rowIndex; |
| mRowSpan = rowSpan; |
| mColumnIndex = columnIndex; |
| mColumnSpan = columnSpan; |
| mHeading = heading; |
| mSelected = selected; |
| } |
| |
| /** |
| * Gets the column index at which the item is located. |
| * |
| * @return The column index. |
| */ |
| public int getColumnIndex() { |
| return mColumnIndex; |
| } |
| |
| /** |
| * Gets the row index at which the item is located. |
| * |
| * @return The row index. |
| */ |
| public int getRowIndex() { |
| return mRowIndex; |
| } |
| |
| /** |
| * Gets the number of columns the item spans. |
| * |
| * @return The column span. |
| */ |
| public int getColumnSpan() { |
| return mColumnSpan; |
| } |
| |
| /** |
| * Gets the number of rows the item spans. |
| * |
| * @return The row span. |
| */ |
| public int getRowSpan() { |
| return mRowSpan; |
| } |
| |
| /** |
| * 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 AccessibilityNodeInfo#isHeading()} |
| */ |
| public boolean isHeading() { |
| return mHeading; |
| } |
| |
| /** |
| * Gets if the collection item is selected. |
| * |
| * @return If the item is selected. |
| */ |
| public boolean isSelected() { |
| return mSelected; |
| } |
| |
| /** |
| * Recycles this instance. |
| */ |
| void recycle() { |
| clear(); |
| sPool.release(this); |
| } |
| |
| private void clear() { |
| mColumnIndex = 0; |
| mColumnSpan = 0; |
| mRowIndex = 0; |
| mRowSpan = 0; |
| mHeading = false; |
| mSelected = false; |
| } |
| } |
| |
| /** |
| * Class with information of touch delegated views and regions from {@link TouchDelegate} for |
| * the {@link AccessibilityNodeInfo}. |
| * |
| * @see AccessibilityNodeInfo#setTouchDelegateInfo(TouchDelegateInfo) |
| */ |
| public static final class TouchDelegateInfo implements Parcelable { |
| private ArrayMap<Region, Long> mTargetMap; |
| // Two ids are initialized lazily in AccessibilityNodeInfo#getTouchDelegateInfo |
| private int mConnectionId; |
| private int mWindowId; |
| |
| /** |
| * Create a new instance of {@link TouchDelegateInfo}. |
| * |
| * @param targetMap A map from regions (in view coordinates) to delegated views. |
| * @throws IllegalArgumentException if targetMap is empty or {@code null} in |
| * Regions or Views. |
| */ |
| public TouchDelegateInfo(@NonNull Map<Region, View> targetMap) { |
| Preconditions.checkArgument(!targetMap.isEmpty() |
| && !targetMap.containsKey(null) && !targetMap.containsValue(null)); |
| mTargetMap = new ArrayMap<>(targetMap.size()); |
| for (final Region region : targetMap.keySet()) { |
| final View view = targetMap.get(region); |
| mTargetMap.put(region, (long) view.getAccessibilityViewId()); |
| } |
| } |
| |
| /** |
| * Create a new instance from target map. |
| * |
| * @param targetMap A map from regions (in view coordinates) to delegated views' |
| * accessibility id. |
| * @param doCopy True if shallow copy targetMap. |
| * @throws IllegalArgumentException if targetMap is empty or {@code null} in |
| * Regions or Views. |
| */ |
| TouchDelegateInfo(@NonNull ArrayMap<Region, Long> targetMap, boolean doCopy) { |
| Preconditions.checkArgument(!targetMap.isEmpty() |
| && !targetMap.containsKey(null) && !targetMap.containsValue(null)); |
| if (doCopy) { |
| mTargetMap = new ArrayMap<>(targetMap.size()); |
| mTargetMap.putAll(targetMap); |
| } else { |
| mTargetMap = targetMap; |
| } |
| } |
| |
| /** |
| * Set the connection ID. |
| * |
| * @param connectionId The connection id. |
| */ |
| private void setConnectionId(int connectionId) { |
| mConnectionId = connectionId; |
| } |
| |
| /** |
| * Set the window ID. |
| * |
| * @param windowId The window id. |
| */ |
| private void setWindowId(int windowId) { |
| mWindowId = windowId; |
| } |
| |
| /** |
| * Returns the number of touch delegate target region. |
| * |
| * @return Number of touch delegate target region. |
| */ |
| public int getRegionCount() { |
| return mTargetMap.size(); |
| } |
| |
| /** |
| * Return the {@link Region} at the given index in the {@link TouchDelegateInfo}. |
| * |
| * @param index The desired index, must be between 0 and {@link #getRegionCount()}-1. |
| * @return Returns the {@link Region} stored at the given index. |
| */ |
| @NonNull |
| public Region getRegionAt(int index) { |
| return mTargetMap.keyAt(index); |
| } |
| |
| /** |
| * Return the target {@link AccessibilityNodeInfo} for the given {@link Region}. |
| * <p> |
| * <strong>Note:</strong> This api can only be called from {@link 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> |
| * |
| * @param region The region retrieved from {@link #getRegionAt(int)}. |
| * @return The target node associates with the given region. |
| */ |
| @Nullable |
| public AccessibilityNodeInfo getTargetForRegion(@NonNull Region region) { |
| return getNodeForAccessibilityId(mConnectionId, mWindowId, mTargetMap.get(region)); |
| } |
| |
| /** |
| * Return the accessibility id of target node. |
| * |
| * @param region The region retrieved from {@link #getRegionAt(int)}. |
| * @return The accessibility id of target node. |
| * |
| * @hide |
| */ |
| @TestApi |
| public long getAccessibilityIdForRegion(@NonNull Region region) { |
| return mTargetMap.get(region); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeInt(mTargetMap.size()); |
| for (int i = 0; i < mTargetMap.size(); i++) { |
| final Region region = mTargetMap.keyAt(i); |
| final Long accessibilityId = mTargetMap.valueAt(i); |
| region.writeToParcel(dest, flags); |
| dest.writeLong(accessibilityId); |
| } |
| } |
| |
| /** |
| * @see android.os.Parcelable.Creator |
| */ |
| public static final @android.annotation.NonNull Parcelable.Creator<TouchDelegateInfo> CREATOR = |
| new Parcelable.Creator<TouchDelegateInfo>() { |
| @Override |
| public TouchDelegateInfo createFromParcel(Parcel parcel) { |
| final int size = parcel.readInt(); |
| if (size == 0) { |
| return null; |
| } |
| final ArrayMap<Region, Long> targetMap = new ArrayMap<>(size); |
| for (int i = 0; i < size; i++) { |
| final Region region = Region.CREATOR.createFromParcel(parcel); |
| final long accessibilityId = parcel.readLong(); |
| targetMap.put(region, accessibilityId); |
| } |
| final TouchDelegateInfo touchDelegateInfo = new TouchDelegateInfo( |
| targetMap, false); |
| return touchDelegateInfo; |
| } |
| |
| @Override |
| public TouchDelegateInfo[] newArray(int size) { |
| return new TouchDelegateInfo[size]; |
| } |
| }; |
| } |
| |
| /** |
| * @see android.os.Parcelable.Creator |
| */ |
| public static final @android.annotation.NonNull Parcelable.Creator<AccessibilityNodeInfo> CREATOR = |
| new Parcelable.Creator<AccessibilityNodeInfo>() { |
| @Override |
| public AccessibilityNodeInfo createFromParcel(Parcel parcel) { |
| AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); |
| info.initFromParcel(parcel); |
| return info; |
| } |
| |
| @Override |
| public AccessibilityNodeInfo[] newArray(int size) { |
| return new AccessibilityNodeInfo[size]; |
| } |
| }; |
| } |