7162 lines
273 KiB
Java
7162 lines
273 KiB
Java
/*
|
|
* 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.FlaggedApi;
|
|
import android.annotation.IntDef;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.SuppressLint;
|
|
import android.annotation.TestApi;
|
|
import android.compat.annotation.UnsupportedAppUsage;
|
|
import android.content.ClipData;
|
|
import android.graphics.Rect;
|
|
import android.graphics.Region;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.os.IBinder;
|
|
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.AccessibilityReplacementSpan;
|
|
import android.text.style.AccessibilityURLSpan;
|
|
import android.text.style.ClickableSpan;
|
|
import android.text.style.ReplacementSpan;
|
|
import android.text.style.URLSpan;
|
|
import android.util.ArrayMap;
|
|
import android.util.ArraySet;
|
|
import android.util.Log;
|
|
import android.util.LongArray;
|
|
import android.util.Size;
|
|
import android.util.TypedValue;
|
|
import android.view.SurfaceView;
|
|
import android.view.TouchDelegate;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.ViewRootImpl;
|
|
import android.widget.TextView;
|
|
|
|
import com.android.internal.R;
|
|
import com.android.internal.util.CollectionUtils;
|
|
import com.android.internal.util.Preconditions;
|
|
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.time.Duration;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
|
|
/**
|
|
* 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. See
|
|
* {@link #setQueryFromAppProcessEnabled} if you would like to inspect the
|
|
* node tree from the app process for testing or debugging tools.
|
|
* </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>
|
|
* <aside class="note">
|
|
* <b>Note:</b> Use a {@link androidx.core.view.accessibility.AccessibilityNodeInfoCompat}
|
|
* wrapper instead of this class for backwards-compatibility. </aside>
|
|
*
|
|
* @see android.accessibilityservice.AccessibilityService
|
|
* @see AccessibilityEvent
|
|
* @see AccessibilityManager
|
|
*/
|
|
public class AccessibilityNodeInfo implements Parcelable {
|
|
|
|
private static final String TAG = "AccessibilityNodeInfo";
|
|
|
|
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG) && Build.IS_DEBUGGABLE;
|
|
|
|
/** @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 int LEASHED_ITEM_ID = Integer.MAX_VALUE - 2;
|
|
|
|
/** @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 long LEASHED_NODE_ID = makeNodeId(LEASHED_ITEM_ID,
|
|
AccessibilityNodeProvider.HOST_VIEW_ID);
|
|
|
|
// Prefetch flags.
|
|
|
|
/**
|
|
* Prefetching strategy that prefetches the ancestors of the requested node.
|
|
* <p> Ancestors will be prefetched before siblings and descendants.
|
|
*
|
|
* @see #getChild(int, int)
|
|
* @see #getParent(int)
|
|
* @see AccessibilityWindowInfo#getRoot(int)
|
|
* @see AccessibilityService#getRootInActiveWindow(int)
|
|
* @see AccessibilityEvent#getSource(int)
|
|
*/
|
|
public static final int FLAG_PREFETCH_ANCESTORS = 1 /* << 0 */;
|
|
|
|
/**
|
|
* Prefetching strategy that prefetches the siblings of the requested node.
|
|
* <p> To avoid disconnected trees, this flag will also prefetch the parent. Siblings will be
|
|
* prefetched before descendants.
|
|
*
|
|
* <p> See {@link #FLAG_PREFETCH_ANCESTORS} for information on where these flags can be used.
|
|
*/
|
|
public static final int FLAG_PREFETCH_SIBLINGS = 1 << 1;
|
|
|
|
/**
|
|
* Prefetching strategy that prefetches the descendants in a hybrid depth first and breadth
|
|
* first approach.
|
|
* <p> The children of the root node is prefetched before recursing on the children. This
|
|
* must not be combined with {@link #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST} or
|
|
* {@link #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST} or this will trigger an
|
|
* IllegalArgumentException.
|
|
*
|
|
* <p> See {@link #FLAG_PREFETCH_ANCESTORS} for information on where these flags can be used.
|
|
*/
|
|
public static final int FLAG_PREFETCH_DESCENDANTS_HYBRID = 1 << 2;
|
|
|
|
/**
|
|
* Prefetching strategy that prefetches the descendants of the requested node depth-first.
|
|
* <p> This must not be combined with {@link #FLAG_PREFETCH_DESCENDANTS_HYBRID} or
|
|
* {@link #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST} or this will trigger an
|
|
* IllegalArgumentException.
|
|
*
|
|
* <p> See {@link #FLAG_PREFETCH_ANCESTORS} for information on where these flags can be used.
|
|
*/
|
|
public static final int FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST = 1 << 3;
|
|
|
|
/**
|
|
* Prefetching strategy that prefetches the descendants of the requested node breadth-first.
|
|
* <p> This must not be combined with {@link #FLAG_PREFETCH_DESCENDANTS_HYBRID} or
|
|
* {@link #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST} or this will trigger an
|
|
* IllegalArgumentException.
|
|
*
|
|
* <p> See {@link #FLAG_PREFETCH_ANCESTORS} for information on where these flags can be used.
|
|
*/
|
|
public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST = 1 << 4;
|
|
|
|
/**
|
|
* Prefetching flag that specifies prefetching should not be interrupted by a request to
|
|
* retrieve a node or perform an action on a node.
|
|
*
|
|
* <p> See {@link #FLAG_PREFETCH_ANCESTORS} for information on where these flags can be used.
|
|
*/
|
|
public static final int FLAG_PREFETCH_UNINTERRUPTIBLE = 1 << 5;
|
|
|
|
/**
|
|
* Mask for {@link PrefetchingStrategy} all types.
|
|
*
|
|
* @see #FLAG_PREFETCH_ANCESTORS
|
|
* @see #FLAG_PREFETCH_SIBLINGS
|
|
* @see #FLAG_PREFETCH_DESCENDANTS_HYBRID
|
|
* @see #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST
|
|
* @see #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST
|
|
* @see #FLAG_PREFETCH_UNINTERRUPTIBLE
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int FLAG_PREFETCH_MASK = 0x0000003F;
|
|
|
|
/**
|
|
* Mask for {@link PrefetchingStrategy} that includes only descendants-related strategies.
|
|
*
|
|
* @see #FLAG_PREFETCH_DESCENDANTS_HYBRID
|
|
* @see #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST
|
|
* @see #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int FLAG_PREFETCH_DESCENDANTS_MASK = 0x0000001C;
|
|
|
|
/**
|
|
* Maximum batch size of prefetched nodes for a request.
|
|
*/
|
|
@SuppressLint("MinMaxConstant")
|
|
public static final int MAX_NUMBER_OF_PREFETCHED_NODES = 50;
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(flag = true, prefix = { "FLAG_PREFETCH" }, value = {
|
|
FLAG_PREFETCH_ANCESTORS,
|
|
FLAG_PREFETCH_SIBLINGS,
|
|
FLAG_PREFETCH_DESCENDANTS_HYBRID,
|
|
FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST,
|
|
FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST,
|
|
FLAG_PREFETCH_UNINTERRUPTIBLE
|
|
})
|
|
public @interface PrefetchingStrategy {}
|
|
|
|
// Service flags.
|
|
|
|
/**
|
|
* @see AccessibilityServiceInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
|
|
* @hide
|
|
*/
|
|
public static final int FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS = 1 << 7;
|
|
|
|
/**
|
|
* @see AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS
|
|
* @hide
|
|
*/
|
|
public static final int FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS = 1 << 8;
|
|
|
|
/**
|
|
* @see AccessibilityServiceInfo#isAccessibilityTool()
|
|
* @hide
|
|
*/
|
|
public static final int FLAG_SERVICE_IS_ACCESSIBILITY_TOOL = 1 << 9;
|
|
|
|
/**
|
|
* Mask for all types of additional view data exposed to services.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int FLAG_REPORT_MASK =
|
|
FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS
|
|
| FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS
|
|
| FLAG_SERVICE_IS_ACCESSIBILITY_TOOL;
|
|
|
|
// Actions.
|
|
|
|
/**
|
|
* Action that gives input focus to the node.
|
|
* See {@link AccessibilityAction#ACTION_FOCUS}
|
|
*/
|
|
public static final int ACTION_FOCUS = 1 /* << 0 */;
|
|
|
|
/**
|
|
* Action that clears input focus of the node.
|
|
* See {@link AccessibilityAction#ACTION_CLEAR_FOCUS}
|
|
*/
|
|
public static final int ACTION_CLEAR_FOCUS = 1 << 1;
|
|
|
|
/**
|
|
* Action that selects the node.
|
|
* @see AccessibilityAction#ACTION_SELECT
|
|
*/
|
|
public static final int ACTION_SELECT = 1 << 2;
|
|
|
|
/**
|
|
* Action that deselects the node.
|
|
*/
|
|
public static final int ACTION_CLEAR_SELECTION = 1 << 3;
|
|
|
|
/**
|
|
* Action that clicks on the node info.
|
|
*
|
|
* @see AccessibilityAction#ACTION_CLICK
|
|
*/
|
|
public static final int ACTION_CLICK = 1 << 4;
|
|
|
|
/**
|
|
* Action that long clicks on the node.
|
|
*
|
|
* <p>It does not support coordinate information for anchoring.</p>
|
|
* @see AccessibilityAction#ACTION_LONG_CLICK
|
|
*/
|
|
public static final int ACTION_LONG_CLICK = 1 << 5;
|
|
|
|
/**
|
|
* Action that gives accessibility focus to the node.
|
|
* See {@link AccessibilityAction#ACTION_ACCESSIBILITY_FOCUS}
|
|
*/
|
|
public static final int ACTION_ACCESSIBILITY_FOCUS = 1 << 6;
|
|
|
|
/**
|
|
* Action that clears accessibility focus of the node.
|
|
* See {@link AccessibilityAction#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
|
|
*/
|
|
public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 1 << 7;
|
|
|
|
/**
|
|
* 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 = 1 << 8;
|
|
|
|
/**
|
|
* 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 = 1 << 9;
|
|
|
|
/**
|
|
* 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 = 1 << 10;
|
|
|
|
/**
|
|
* 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 = 1 << 11;
|
|
|
|
/**
|
|
* Action to scroll the node content forward.
|
|
* @see AccessibilityAction#ACTION_SCROLL_FORWARD
|
|
*/
|
|
public static final int ACTION_SCROLL_FORWARD = 1 << 12;
|
|
|
|
/**
|
|
* Action to scroll the node content backward.
|
|
* @see AccessibilityAction#ACTION_SCROLL_BACKWARD
|
|
*/
|
|
public static final int ACTION_SCROLL_BACKWARD = 1 << 13;
|
|
|
|
/**
|
|
* Action to copy the current selection to the clipboard.
|
|
*/
|
|
public static final int ACTION_COPY = 1 << 14;
|
|
|
|
/**
|
|
* Action to paste the current clipboard content.
|
|
*/
|
|
public static final int ACTION_PASTE = 1 << 15;
|
|
|
|
/**
|
|
* Action to cut the current selection and place it to the clipboard.
|
|
*/
|
|
public static final int ACTION_CUT = 1 << 16;
|
|
|
|
/**
|
|
* Action to set the selection. Performing this action with no arguments
|
|
* clears the selection.
|
|
*
|
|
* @see AccessibilityAction#ACTION_SET_SELECTION
|
|
* @see #ACTION_ARGUMENT_SELECTION_START_INT
|
|
* @see #ACTION_ARGUMENT_SELECTION_END_INT
|
|
*/
|
|
public static final int ACTION_SET_SELECTION = 1 << 17;
|
|
|
|
/**
|
|
* Action to expand an expandable node.
|
|
*/
|
|
public static final int ACTION_EXPAND = 1 << 18;
|
|
|
|
/**
|
|
* Action to collapse an expandable node.
|
|
*/
|
|
public static final int ACTION_COLLAPSE = 1 << 19;
|
|
|
|
/**
|
|
* Action to dismiss a dismissable node.
|
|
*/
|
|
public static final int ACTION_DISMISS = 1 << 20;
|
|
|
|
/**
|
|
* 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.
|
|
* @see AccessibilityAction#ACTION_SET_TEXT
|
|
*/
|
|
public static final int ACTION_SET_TEXT = 1 << 21;
|
|
|
|
/** @hide */
|
|
public static final int LAST_LEGACY_STANDARD_ACTION = ACTION_SET_TEXT;
|
|
|
|
/**
|
|
* Mask to verify if a given value is a combination of the existing ACTION_ constants.
|
|
*
|
|
* The smallest possible action is 1, and the largest is 1 << 21, or {@link ACTION_SET_TEXT}. A
|
|
* node can have any combination of actions present, so a node's max action int is:
|
|
*
|
|
* 0000 0000 0011 1111 1111 1111 1111 1111
|
|
*
|
|
* Therefore, if an action has any of the following bits flipped, it will be invalid:
|
|
*
|
|
* 1111 1111 11-- ---- ---- ---- ---- ----
|
|
*
|
|
* This can be represented in hexadecimal as 0xFFC00000.
|
|
*
|
|
* @see AccessibilityNodeInfo#addAction(int)
|
|
*/
|
|
private static final int INVALID_ACTIONS_MASK = 0xFFC00000;
|
|
|
|
// 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";
|
|
|
|
/**
|
|
* Argument to represent the duration in milliseconds to press and hold a node.
|
|
* <p>
|
|
* <strong>Type:</strong> int<br>
|
|
* <strong>Actions:</strong>
|
|
* <ul>
|
|
* <li>{@link AccessibilityAction#ACTION_PRESS_AND_HOLD}</li>
|
|
* </ul>
|
|
*
|
|
* @see AccessibilityAction#ACTION_PRESS_AND_HOLD
|
|
*/
|
|
public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT =
|
|
"android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
|
|
|
|
/**
|
|
* <p>Argument to represent the direction when using
|
|
* {@link AccessibilityAction#ACTION_SCROLL_IN_DIRECTION}.</p>
|
|
*
|
|
* <p>
|
|
* The value of this argument can be one of:
|
|
* <ul>
|
|
* <li>{@link View#FOCUS_DOWN}</li>
|
|
* <li>{@link View#FOCUS_UP}</li>
|
|
* <li>{@link View#FOCUS_LEFT}</li>
|
|
* <li>{@link View#FOCUS_RIGHT}</li>
|
|
* <li>{@link View#FOCUS_FORWARD}</li>
|
|
* <li>{@link View#FOCUS_BACKWARD}</li>
|
|
* </ul>
|
|
* </p>
|
|
*/
|
|
public static final String ACTION_ARGUMENT_DIRECTION_INT =
|
|
"android.view.accessibility.action.ARGUMENT_DIRECTION_INT";
|
|
|
|
/**
|
|
* <p>Argument to represent the scroll amount as a percent of the visible area of a node, with
|
|
* 1.0F as the default. Values smaller than 1.0F represent a partial scroll of the node, and
|
|
* values larger than 1.0F represent a scroll that extends beyond the currently visible node
|
|
* Rect. Setting this to {@link Float#POSITIVE_INFINITY} or to another "too large" value should
|
|
* scroll to the end of the node. Negative values should not be used with this argument.
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* This argument should be used with the following scroll actions:
|
|
* <ul>
|
|
* <li>{@link AccessibilityAction#ACTION_SCROLL_FORWARD}</li>
|
|
* <li>{@link AccessibilityAction#ACTION_SCROLL_BACKWARD}</li>
|
|
* <li>{@link AccessibilityAction#ACTION_SCROLL_UP}</li>
|
|
* <li>{@link AccessibilityAction#ACTION_SCROLL_DOWN}</li>
|
|
* <li>{@link AccessibilityAction#ACTION_SCROLL_LEFT}</li>
|
|
* <li>{@link AccessibilityAction#ACTION_SCROLL_RIGHT}</li>
|
|
* </ul>
|
|
* </p>
|
|
* <p>
|
|
* Example: if a view representing a list of items implements
|
|
* {@link AccessibilityAction#ACTION_SCROLL_FORWARD} to scroll forward by an entire screen
|
|
* (one "page"), then passing a value of .25F via this argument should scroll that view
|
|
* only by 1/4th of a screen. Passing a value of 1.50F via this argument should scroll the
|
|
* view by 1 1/2 screens or to end of the node if the node doesn't extend to 1 1/2 screens.
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* This argument should not be used with the following scroll actions, which don't cleanly
|
|
* conform to granular scroll semantics:
|
|
* <ul>
|
|
* <li>{@link AccessibilityAction#ACTION_SCROLL_IN_DIRECTION}</li>
|
|
* <li>{@link AccessibilityAction#ACTION_SCROLL_TO_POSITION}</li>
|
|
* </ul>
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* Views that support this argument should set
|
|
* {@link #setGranularScrollingSupported(boolean)} to true. Clients should use
|
|
* {@link #isGranularScrollingSupported()} to check if granular scrolling is supported.
|
|
* </p>
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_GRANULAR_SCROLLING)
|
|
public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT =
|
|
"android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
|
|
|
|
// Focus types.
|
|
|
|
/**
|
|
* The input focus.
|
|
*/
|
|
public static final int FOCUS_INPUT = 1;
|
|
|
|
/**
|
|
* The accessibility focus.
|
|
*/
|
|
public static final int FOCUS_ACCESSIBILITY = 2;
|
|
|
|
// Movement granularities.
|
|
|
|
/**
|
|
* Movement granularity bit for traversing the text of a node by character.
|
|
*/
|
|
public static final int MOVEMENT_GRANULARITY_CHARACTER = 1 /* << 0 */;
|
|
|
|
/**
|
|
* Movement granularity bit for traversing the text of a node by word.
|
|
*/
|
|
public static final int MOVEMENT_GRANULARITY_WORD = 1 << 1;
|
|
|
|
/**
|
|
* Movement granularity bit for traversing the text of a node by line.
|
|
*/
|
|
public static final int MOVEMENT_GRANULARITY_LINE = 1 << 2;
|
|
|
|
/**
|
|
* Movement granularity bit for traversing the text of a node by paragraph.
|
|
*/
|
|
public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 1 << 3;
|
|
|
|
/**
|
|
* Movement granularity bit for traversing the text of a node by page.
|
|
*/
|
|
public static final int MOVEMENT_GRANULARITY_PAGE = 1 << 4;
|
|
|
|
// Extra data arguments.
|
|
|
|
/**
|
|
* 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 and no larger than {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH}.
|
|
*
|
|
* @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";
|
|
|
|
/**
|
|
* The maximum allowed length of the requested text location data.
|
|
*/
|
|
public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000;
|
|
|
|
/**
|
|
* Key used to request extra data for the rendering information.
|
|
* The key requests that a {@link AccessibilityNodeInfo.ExtraRenderingInfo} be added to this
|
|
* info. This request is made with {@link #refreshWithExtraData(String, Bundle)} without
|
|
* argument.
|
|
* <p>
|
|
* The data can be retrieved from the {@link ExtraRenderingInfo} returned by
|
|
* {@link #getExtraRenderingInfo()} using {@link ExtraRenderingInfo#getLayoutSize},
|
|
* {@link ExtraRenderingInfo#getTextSizeInPx()} and
|
|
* {@link ExtraRenderingInfo#getTextSizeUnit()}. For layout params, it is supported by both
|
|
* {@link TextView} and {@link ViewGroup}. For text size and unit, it is only supported by
|
|
* {@link TextView}.
|
|
*
|
|
* @see #refreshWithExtraData(String, Bundle)
|
|
*/
|
|
public static final String EXTRA_DATA_RENDERING_INFO_KEY =
|
|
"android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
|
|
|
|
/** @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 = 1 /* << 0 */;
|
|
|
|
private static final int BOOLEAN_PROPERTY_CHECKED = 1 << 1;
|
|
|
|
private static final int BOOLEAN_PROPERTY_FOCUSABLE = 1 << 2;
|
|
|
|
private static final int BOOLEAN_PROPERTY_FOCUSED = 1 << 3;
|
|
|
|
private static final int BOOLEAN_PROPERTY_SELECTED = 1 << 4;
|
|
|
|
private static final int BOOLEAN_PROPERTY_CLICKABLE = 1 << 5;
|
|
|
|
private static final int BOOLEAN_PROPERTY_LONG_CLICKABLE = 1 << 6;
|
|
|
|
private static final int BOOLEAN_PROPERTY_ENABLED = 1 << 7;
|
|
|
|
private static final int BOOLEAN_PROPERTY_PASSWORD = 1 << 8;
|
|
|
|
private static final int BOOLEAN_PROPERTY_SCROLLABLE = 1 << 9;
|
|
|
|
private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 1 << 10;
|
|
|
|
private static final int BOOLEAN_PROPERTY_VISIBLE_TO_USER = 1 << 11;
|
|
|
|
private static final int BOOLEAN_PROPERTY_EDITABLE = 1 << 12;
|
|
|
|
private static final int BOOLEAN_PROPERTY_OPENS_POPUP = 1 << 13;
|
|
|
|
private static final int BOOLEAN_PROPERTY_DISMISSABLE = 1 << 14;
|
|
|
|
private static final int BOOLEAN_PROPERTY_MULTI_LINE = 1 << 15;
|
|
|
|
private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 1 << 16;
|
|
|
|
private static final int BOOLEAN_PROPERTY_CONTEXT_CLICKABLE = 1 << 17;
|
|
|
|
private static final int BOOLEAN_PROPERTY_IMPORTANCE = 1 << 18;
|
|
|
|
private static final int BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE = 1 << 19;
|
|
|
|
private static final int BOOLEAN_PROPERTY_IS_SHOWING_HINT = 1 << 20;
|
|
|
|
private static final int BOOLEAN_PROPERTY_IS_HEADING = 1 << 21;
|
|
|
|
private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 1 << 22;
|
|
|
|
private static final int BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE = 1 << 23;
|
|
|
|
private static final int BOOLEAN_PROPERTY_REQUEST_INITIAL_ACCESSIBILITY_FOCUS = 1 << 24;
|
|
|
|
private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_DATA_SENSITIVE = 1 << 25;
|
|
|
|
private static final int BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING = 1 << 26;
|
|
|
|
/**
|
|
* 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;
|
|
|
|
/**
|
|
* 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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
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;
|
|
}
|
|
|
|
private static final AccessibilityNodeInfo DEFAULT = new AccessibilityNodeInfo();
|
|
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
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 long mMinDurationBetweenContentChanges = 0;
|
|
|
|
private int mBooleanProperties;
|
|
private final Rect mBoundsInParent = new Rect();
|
|
private final Rect mBoundsInScreen = new Rect();
|
|
private final Rect mBoundsInWindow = 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 mStateDescription;
|
|
private CharSequence mContentDescription;
|
|
private CharSequence mTooltipText;
|
|
private String mViewIdResourceName;
|
|
private String mUniqueId;
|
|
private CharSequence mContainerTitle;
|
|
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;
|
|
|
|
private ExtraRenderingInfo mExtraRenderingInfo;
|
|
|
|
private IBinder mLeashedChild;
|
|
private IBinder mLeashedParent;
|
|
private long mLeashedParentNodeId = UNDEFINED_NODE_ID;
|
|
|
|
/**
|
|
* Creates a new {@link AccessibilityNodeInfo}.
|
|
*/
|
|
public AccessibilityNodeInfo() {
|
|
}
|
|
|
|
/**
|
|
* Creates a new {@link AccessibilityNodeInfo} with the given <code>source</code>.
|
|
*
|
|
* @param source The source view.
|
|
*/
|
|
public AccessibilityNodeInfo(@NonNull View source) {
|
|
setSource(source);
|
|
}
|
|
|
|
/**
|
|
* Creates a new {@link AccessibilityNodeInfo} with the given <code>source</code>.
|
|
*
|
|
* @param root The root of the virtual subtree.
|
|
* @param virtualDescendantId The id of the virtual descendant.
|
|
*/
|
|
public AccessibilityNodeInfo(@NonNull View root, int virtualDescendantId) {
|
|
setSource(root, virtualDescendantId);
|
|
}
|
|
|
|
/**
|
|
* Copy constructor. Creates a new {@link AccessibilityNodeInfo}, and this new instance is
|
|
* initialized from the given <code>info</code>.
|
|
*
|
|
* @param info The other info.
|
|
*/
|
|
public AccessibilityNodeInfo(@NonNull 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.
|
|
*
|
|
* <p>
|
|
* <strong>Note:</strong> If this view hierarchy has a {@link SurfaceView} embedding another
|
|
* view hierarchy via {@link SurfaceView#setChildSurfacePackage}, there is a limitation that
|
|
* this API won't be able to find the node for the view on the embedded view hierarchy. It's
|
|
* because views don't know about the embedded hierarchies. Instead, you could traverse all
|
|
* the children to find the node. Or, use {@link AccessibilityService#findFocus(int)} for
|
|
* {@link #FOCUS_ACCESSIBILITY} only since it has no such limitation.
|
|
* </p>
|
|
*
|
|
* @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.
|
|
*
|
|
* <p>
|
|
* <strong>Note:</strong> If this view hierarchy has a {@link SurfaceView} embedding another
|
|
* view hierarchy via {@link SurfaceView#setChildSurfacePackage}, there is a limitation that
|
|
* this API won't be able to find the node for the view in the specified direction on the
|
|
* embedded view hierarchy. It's because views don't know about the embedded hierarchies.
|
|
* Instead, you could traverse all the children to find the node.
|
|
* </p>
|
|
*
|
|
* @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.
|
|
*
|
|
* @param bypassCache Whether to bypass the cache.
|
|
* @return Whether the refresh succeeded.
|
|
*
|
|
* @hide
|
|
*/
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
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;
|
|
}
|
|
init(refreshedInfo);
|
|
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).
|
|
*/
|
|
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_RENDERING_INFO_KEY},
|
|
* {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY},
|
|
* {@link #getAvailableExtraData()} and {@link #getExtraRenderingInfo()}.
|
|
* @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).
|
|
*/
|
|
public boolean refreshWithExtraData(String extraDataKey, Bundle args) {
|
|
// limits the text location length to make sure the rectangle array allocation avoids
|
|
// the binder transaction failure and OOM crash.
|
|
if (args.getInt(EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH, -1)
|
|
> EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH) {
|
|
args.putInt(EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH,
|
|
EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH);
|
|
}
|
|
|
|
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.
|
|
*
|
|
* @param index The child index.
|
|
* @return The child node.
|
|
*
|
|
* @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
|
|
* calling {@link #setQueryFromAppProcessEnabled}.
|
|
*/
|
|
public AccessibilityNodeInfo getChild(int index) {
|
|
return getChild(index, FLAG_PREFETCH_DESCENDANTS_HYBRID);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the child at given index.
|
|
*
|
|
* <p>
|
|
* See {@link #getParent(int)} for a description of prefetching.
|
|
* @param index The child index.
|
|
* @param prefetchingStrategy the prefetching strategy.
|
|
* @return The child node.
|
|
*
|
|
* @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
|
|
* calling {@link #setQueryFromAppProcessEnabled}.
|
|
*
|
|
*/
|
|
@Nullable
|
|
public AccessibilityNodeInfo getChild(int index, @PrefetchingStrategy int prefetchingStrategy) {
|
|
enforceSealed();
|
|
if (mChildNodeIds == null) {
|
|
return null;
|
|
}
|
|
if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) {
|
|
return null;
|
|
}
|
|
final long childId = mChildNodeIds.get(index);
|
|
final AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
|
|
if (mLeashedChild != null && childId == LEASHED_NODE_ID) {
|
|
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mLeashedChild,
|
|
ROOT_NODE_ID, false, prefetchingStrategy, null);
|
|
}
|
|
|
|
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
|
|
childId, false, prefetchingStrategy, 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);
|
|
}
|
|
|
|
/**
|
|
* Adds a view root from leashed content as a child. This method is used to embedded another
|
|
* view hierarchy.
|
|
* <p>
|
|
* <strong>Note:</strong> Only one leashed child is permitted.
|
|
* </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.
|
|
* Note that a view cannot be made its own child.
|
|
* </p>
|
|
*
|
|
* @param token The token to which a view root is added.
|
|
*
|
|
* @throws IllegalStateException If called from an AccessibilityService.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public void addChild(@NonNull IBinder token) {
|
|
enforceNotSealed();
|
|
if (token == null) {
|
|
return;
|
|
}
|
|
if (mChildNodeIds == null) {
|
|
mChildNodeIds = new LongArray();
|
|
}
|
|
|
|
mLeashedChild = token;
|
|
// Checking uniqueness.
|
|
// Since only one leashed child is permitted, skip adding ID if the ID already exists.
|
|
if (mChildNodeIds.indexOf(LEASHED_NODE_ID) >= 0) {
|
|
return;
|
|
}
|
|
mChildNodeIds.add(LEASHED_NODE_ID);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* Removes a leashed 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 token The token of the leashed child
|
|
* @return true if the child was present
|
|
*
|
|
* @throws IllegalStateException If called from an AccessibilityService.
|
|
* @hide
|
|
*/
|
|
public boolean removeChild(IBinder token) {
|
|
enforceNotSealed();
|
|
if (mChildNodeIds == null || mLeashedChild == null) {
|
|
return false;
|
|
}
|
|
if (!mLeashedChild.equals(token)) {
|
|
return false;
|
|
}
|
|
final int index = mChildNodeIds.indexOf(LEASHED_NODE_ID);
|
|
mLeashedChild = null;
|
|
if (index < 0) {
|
|
return false;
|
|
}
|
|
mChildNodeIds.remove(index);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 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>
|
|
* Use {@link androidx.core.view.ViewCompat#addAccessibilityAction(View, CharSequence,
|
|
* AccessibilityViewCommand)} to register an action directly on the view.
|
|
* <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 & INVALID_ACTIONS_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_RENDERING_INFO_KEY
|
|
* @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;
|
|
}
|
|
|
|
/**
|
|
* Sets the minimum time duration between two content change events, which is used in throttling
|
|
* content change events in accessibility services.
|
|
*
|
|
* <p>
|
|
* Example: An app can set MinMillisBetweenContentChanges as 1 min for a view which sends
|
|
* content change events to accessibility services one event per second.
|
|
* Accessibility service will throttle those content change events and only handle one event
|
|
* per minute for that view.
|
|
* </p>
|
|
* <p>
|
|
* Example UI elements that frequently update and may benefit from a duration are progress bars,
|
|
* timers, and stopwatches.
|
|
* </p>
|
|
*
|
|
* @see AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
|
|
* @see AccessibilityEvent#getContentChangeTypes
|
|
* @param duration the minimum duration between content change events.
|
|
* Negative duration would be treated as zero.
|
|
*/
|
|
public void setMinDurationBetweenContentChanges(@NonNull Duration duration) {
|
|
enforceNotSealed();
|
|
mMinDurationBetweenContentChanges = duration.toMillis();
|
|
}
|
|
|
|
/**
|
|
* Gets the minimum time duration between two content change events.
|
|
*/
|
|
@NonNull
|
|
public Duration getMinDurationBetweenContentChanges() {
|
|
return Duration.ofMillis(mMinDurationBetweenContentChanges);
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
Bundle arguments = null;
|
|
if (mExtras != null) {
|
|
arguments = mExtras;
|
|
}
|
|
return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
|
|
action, arguments);
|
|
}
|
|
|
|
/**
|
|
* 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> If this view hierarchy has a {@link SurfaceView} embedding another
|
|
* view hierarchy via {@link SurfaceView#setChildSurfacePackage}, there is a limitation that
|
|
* this API won't be able to find the node for the view on the embedded view hierarchy. It's
|
|
* because views don't know about the embedded hierarchies. Instead, you could traverse all
|
|
* the children to find the node.
|
|
* </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> 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 the {@link android.accessibilityservice.AccessibilityService}.
|
|
* </p>
|
|
* <p>
|
|
* <strong>Note:</strong> If this view hierarchy has a {@link SurfaceView} embedding another
|
|
* view hierarchy via {@link SurfaceView#setChildSurfacePackage}, there is a limitation that
|
|
* this API won't be able to find the node for the view on the embedded view hierarchy. It's
|
|
* because views don't know about the embedded hierarchies. Instead, you could traverse all
|
|
* the children to find the node.
|
|
* </p>
|
|
*
|
|
* @param viewId The fully qualified resource name of the view id to find.
|
|
* @return A list of node info.
|
|
*/
|
|
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(@NonNull String viewId) {
|
|
enforceSealed();
|
|
if (viewId == null) {
|
|
Log.e(TAG, "returns empty list due to null viewId.");
|
|
return Collections.emptyList();
|
|
}
|
|
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.
|
|
*
|
|
* @return The parent.
|
|
*
|
|
* @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
|
|
* calling {@link #setQueryFromAppProcessEnabled}.
|
|
*/
|
|
public AccessibilityNodeInfo getParent() {
|
|
enforceSealed();
|
|
if (mLeashedParent != null && mLeashedParentNodeId != UNDEFINED_NODE_ID) {
|
|
return getNodeForAccessibilityId(mConnectionId, mLeashedParent, mLeashedParentNodeId,
|
|
FLAG_PREFETCH_ANCESTORS | FLAG_PREFETCH_SIBLINGS);
|
|
}
|
|
return getNodeForAccessibilityId(mConnectionId, mWindowId, mParentNodeId);
|
|
}
|
|
|
|
/**
|
|
* Gets the parent.
|
|
*
|
|
* <p>
|
|
* Use {@code prefetchingStrategy} to determine the types of
|
|
* nodes prefetched from the app if the requested node is not in the cache and must be retrieved
|
|
* by the app. The default strategy for {@link #getParent()} is a combination of ancestor and
|
|
* sibling strategies. The app will prefetch until all nodes fulfilling the strategies are
|
|
* fetched, another node request is sent, or the maximum prefetch batch size of
|
|
* {@link #MAX_NUMBER_OF_PREFETCHED_NODES} nodes is reached. To prevent interruption by another
|
|
* request and to force prefetching of the max batch size, use
|
|
* {@link AccessibilityNodeInfo#FLAG_PREFETCH_UNINTERRUPTIBLE}.
|
|
* </p>
|
|
*
|
|
* @param prefetchingStrategy the prefetching strategy.
|
|
* @return The parent.
|
|
*
|
|
* @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
|
|
* calling {@link #setQueryFromAppProcessEnabled}.
|
|
*
|
|
* @see #FLAG_PREFETCH_ANCESTORS
|
|
* @see #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST
|
|
* @see #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST
|
|
* @see #FLAG_PREFETCH_DESCENDANTS_HYBRID
|
|
* @see #FLAG_PREFETCH_SIBLINGS
|
|
* @see #FLAG_PREFETCH_UNINTERRUPTIBLE
|
|
*/
|
|
@Nullable
|
|
public AccessibilityNodeInfo getParent(@PrefetchingStrategy int prefetchingStrategy) {
|
|
enforceSealed();
|
|
if (mLeashedParent != null && mLeashedParentNodeId != UNDEFINED_NODE_ID) {
|
|
return getNodeForAccessibilityId(mConnectionId, mLeashedParent, mLeashedParentNodeId,
|
|
prefetchingStrategy);
|
|
}
|
|
return getNodeForAccessibilityId(mConnectionId, mWindowId, mParentNodeId,
|
|
prefetchingStrategy);
|
|
}
|
|
|
|
/**
|
|
* @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.
|
|
* <p>
|
|
* When magnification is enabled, the bounds in parent are also scaled up by magnification
|
|
* scale. For example, it returns Rect(20, 20, 200, 200) for original bounds
|
|
* Rect(10, 10, 100, 100), when the magnification scale is 2.
|
|
* <p/>
|
|
*
|
|
* @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.
|
|
* <p>
|
|
* When magnification is enabled, the bounds in screen are scaled up by magnification scale
|
|
* and the positions are also adjusted according to the offset of magnification viewport.
|
|
* For example, it returns Rect(-180, -180, 0, 0) for original bounds Rect(10, 10, 100, 100),
|
|
* when the magnification scale is 2 and offsets for X and Y are both 200.
|
|
* <p/>
|
|
*
|
|
* @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 the node bounds in window coordinates.
|
|
* <p>
|
|
* When magnification is enabled, the bounds in window are scaled up by magnification scale
|
|
* and the positions are also adjusted according to the offset of magnification viewport.
|
|
* For example, it returns Rect(-180, -180, 0, 0) for original bounds Rect(10, 10, 100, 100),
|
|
* when the magnification scale is 2 and offsets for X and Y are both 200.
|
|
* <p/>
|
|
*
|
|
* @param outBounds The output node bounds.
|
|
*/
|
|
public void getBoundsInWindow(@NonNull Rect outBounds) {
|
|
outBounds.set(mBoundsInWindow.left, mBoundsInWindow.top,
|
|
mBoundsInWindow.right, mBoundsInWindow.bottom);
|
|
}
|
|
|
|
/**
|
|
* Returns the actual rect containing the node bounds in window coordinates.
|
|
*
|
|
* @hide Not safe to expose outside the framework.
|
|
*/
|
|
@NonNull
|
|
public Rect getBoundsInWindow() {
|
|
return mBoundsInWindow;
|
|
}
|
|
|
|
/**
|
|
* Sets the node bounds in window coordinates.
|
|
* <p>
|
|
* <strong>Note:</strong> Cannot be called from an
|
|
* {@link android.accessibilityservice.AccessibilityService}.
|
|
* This class is made immutable before being delivered to an AccessibilityService.
|
|
* </p>
|
|
*
|
|
* @param bounds The node bounds.
|
|
*
|
|
* @throws IllegalStateException If called from an AccessibilityService.
|
|
*/
|
|
public void setBoundsInWindow(@NonNull Rect bounds) {
|
|
enforceNotSealed();
|
|
mBoundsInWindow.set(bounds);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* <p>In the View system, this typically maps to {@link View#isFocusable()}.
|
|
* @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>
|
|
* <p>To mark a node as explicitly focusable for a screen reader, consider using
|
|
* {@link #setScreenReaderFocusable(boolean)} instead.
|
|
*
|
|
* @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.
|
|
*
|
|
* <p>This is distinct from {@link #isAccessibilityFocused()}, which is used by screen readers.
|
|
* See {@link AccessibilityAction#ACTION_ACCESSIBILITY_FOCUS} for details.
|
|
*
|
|
* @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.
|
|
* <p>
|
|
* Between {@link Build.VERSION_CODES#JELLY_BEAN API 16} and
|
|
* {@link Build.VERSION_CODES#Q API 29}, this method may incorrectly return false when
|
|
* magnification is enabled. On other versions, a node is considered visible even if it is not
|
|
* on the screen because magnification is active.
|
|
* </p>
|
|
*
|
|
* @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.
|
|
*
|
|
* <p>This is distinct from {@link #isFocused()}, which is used to track system focus.
|
|
* See {@link #ACTION_ACCESSIBILITY_FOCUS} for details.
|
|
* @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>
|
|
* <p>The UI element updating this property should send an event of
|
|
* {@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}
|
|
* or {@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED} if its
|
|
* accessibility-focused state changes.
|
|
* @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 supports granular scrolling.
|
|
*
|
|
* @return True if all scroll actions that could support
|
|
* {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT} have done so, false otherwise.
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_GRANULAR_SCROLLING)
|
|
public boolean isGranularScrollingSupported() {
|
|
return getBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING);
|
|
}
|
|
|
|
/**
|
|
* Sets if the node supports granular scrolling. This should be set to true if all scroll
|
|
* actions which could support {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT} have done so.
|
|
* <p>
|
|
* <strong>Note:</strong> Cannot be called from an
|
|
* {@link android.accessibilityservice.AccessibilityService}.
|
|
* This class is made immutable before being delivered to an AccessibilityService.
|
|
* </p>
|
|
*
|
|
* @param granularScrollingSupported True if the node supports granular scrolling, false
|
|
* otherwise.
|
|
*
|
|
* @throws IllegalStateException If called from an AccessibilityService.
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_GRANULAR_SCROLLING)
|
|
public void setGranularScrollingSupported(boolean granularScrollingSupported) {
|
|
setBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING,
|
|
granularScrollingSupported);
|
|
}
|
|
|
|
/**
|
|
* Gets if the node has selectable text.
|
|
*
|
|
* <p>
|
|
* Services should use {@link #ACTION_SET_SELECTION} for selection. Editable text nodes must
|
|
* also be selectable. But not all UIs will populate this field, so services should consider
|
|
* 'isTextSelectable | isEditable' to ensure they don't miss nodes with selectable text.
|
|
* </p>
|
|
*
|
|
* @see #isEditable
|
|
* @return True if the node has selectable text.
|
|
*/
|
|
public boolean isTextSelectable() {
|
|
return getBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE);
|
|
}
|
|
|
|
/**
|
|
* Sets if the node has selectable text.
|
|
* <p>
|
|
* <strong>Note:</strong> Cannot be called from an
|
|
* {@link android.accessibilityservice.AccessibilityService}.
|
|
* This class is made immutable before being delivered to an AccessibilityService.
|
|
* </p>
|
|
*
|
|
* @param selectableText True if the node has selectable text, false otherwise.
|
|
*
|
|
* @throws IllegalStateException If called from an AccessibilityService.
|
|
*/
|
|
public void setTextSelectable(boolean selectableText) {
|
|
setBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE, selectableText);
|
|
}
|
|
|
|
/**
|
|
* Gets whether the node has {@link #setRequestInitialAccessibilityFocus}.
|
|
*
|
|
* @return True if the node has requested initial accessibility focus.
|
|
*/
|
|
public boolean hasRequestInitialAccessibilityFocus() {
|
|
return getBooleanProperty(BOOLEAN_PROPERTY_REQUEST_INITIAL_ACCESSIBILITY_FOCUS);
|
|
}
|
|
|
|
/**
|
|
* Sets whether the node has requested initial accessibility focus.
|
|
*
|
|
* <p>
|
|
* If the node {@link #hasRequestInitialAccessibilityFocus}, this node would be one of
|
|
* candidates to be accessibility focused when the window appears.
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* <strong>Note:</strong> Cannot be called from an
|
|
* {@link android.accessibilityservice.AccessibilityService}.
|
|
* This class is made immutable before being delivered to an AccessibilityService.
|
|
* </p>
|
|
*
|
|
* @param requestInitialAccessibilityFocus True if the node requests to receive initial
|
|
* accessibility focus.
|
|
* @throws IllegalStateException If called from an AccessibilityService.
|
|
*/
|
|
public void setRequestInitialAccessibilityFocus(boolean requestInitialAccessibilityFocus) {
|
|
setBooleanProperty(BOOLEAN_PROPERTY_REQUEST_INITIAL_ACCESSIBILITY_FOCUS,
|
|
requestInitialAccessibilityFocus);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* Gets if the node's accessibility data is considered sensitive.
|
|
*
|
|
* @return True if the node's data is considered sensitive, false otherwise.
|
|
* @see View#isAccessibilityDataSensitive()
|
|
*/
|
|
public boolean isAccessibilityDataSensitive() {
|
|
return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_DATA_SENSITIVE);
|
|
}
|
|
|
|
/**
|
|
* Sets whether this node's accessibility data is considered sensitive.
|
|
*
|
|
* <p>
|
|
* <strong>Note:</strong> Cannot be called from an {@link AccessibilityService}.
|
|
* This class is made immutable before being delivered to an AccessibilityService.
|
|
* </p>
|
|
*
|
|
* @param accessibilityDataSensitive True if the node's accessibility data is considered
|
|
* sensitive.
|
|
* @throws IllegalStateException If called from an AccessibilityService.
|
|
* @see View#setAccessibilityDataSensitive
|
|
*/
|
|
public void setAccessibilityDataSensitive(boolean accessibilityDataSensitive) {
|
|
setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_DATA_SENSITIVE,
|
|
accessibilityDataSensitive);
|
|
}
|
|
|
|
/**
|
|
* 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 the {@link ExtraRenderingInfo extra rendering info} if the node is meant to be
|
|
* refreshed with extra data to examine rendering related accessibility issues.
|
|
*
|
|
* @return The {@link ExtraRenderingInfo extra rendering info}.
|
|
*
|
|
* @see #EXTRA_DATA_RENDERING_INFO_KEY
|
|
* @see #refreshWithExtraData(String, Bundle)
|
|
*/
|
|
@Nullable
|
|
public ExtraRenderingInfo getExtraRenderingInfo() {
|
|
return mExtraRenderingInfo;
|
|
}
|
|
|
|
/**
|
|
* Sets the extra rendering info, <code>extraRenderingInfo<code/>, if the node is meant to be
|
|
* refreshed with extra data.
|
|
* <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 extraRenderingInfo The {@link ExtraRenderingInfo extra rendering info}.
|
|
* @hide
|
|
*/
|
|
public void setExtraRenderingInfo(@NonNull ExtraRenderingInfo extraRenderingInfo) {
|
|
enforceNotSealed();
|
|
mExtraRenderingInfo = extraRenderingInfo;
|
|
}
|
|
|
|
/**
|
|
* 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,
|
|
* a Snackbar that displays a confirmation notification 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>
|
|
* <p>This can be used to
|
|
* <a href="{@docRoot}guide/topics/ui/accessibility/principles#content-groups">group related
|
|
* content.</a>
|
|
* </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;
|
|
if (text instanceof Spanned) {
|
|
CharSequence tmpText = text;
|
|
tmpText = replaceClickableSpan(tmpText);
|
|
tmpText = replaceReplacementSpan(tmpText);
|
|
mText = tmpText;
|
|
return;
|
|
}
|
|
mText = (text == null) ? null : text.subSequence(0, text.length());
|
|
}
|
|
|
|
/**
|
|
* Replaces any ClickableSpan in the given {@code text} with placeholders.
|
|
*
|
|
* @param text The text.
|
|
*
|
|
* @return The spannable with ClickableSpan replacement.
|
|
*/
|
|
private CharSequence replaceClickableSpan(CharSequence text) {
|
|
ClickableSpan[] clickableSpans =
|
|
((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
|
|
Spannable spannable = new SpannableStringBuilder(text);
|
|
if (clickableSpans.length == 0) {
|
|
return text;
|
|
}
|
|
for (int i = 0; i < clickableSpans.length; i++) {
|
|
ClickableSpan span = clickableSpans[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);
|
|
if (spanToReplaceStart < 0) {
|
|
continue;
|
|
}
|
|
spannable.removeSpan(span);
|
|
ClickableSpan replacementSpan = (span instanceof URLSpan)
|
|
? new AccessibilityURLSpan((URLSpan) span)
|
|
: new AccessibilityClickableSpan(span.getId());
|
|
spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd,
|
|
spanToReplaceFlags);
|
|
}
|
|
return spannable;
|
|
}
|
|
|
|
/**
|
|
* Replaces any ReplacementSpan in the given {@code text} if the object has content description.
|
|
*
|
|
* @param text The text.
|
|
*
|
|
* @return The spannable with ReplacementSpan replacement.
|
|
*/
|
|
private CharSequence replaceReplacementSpan(CharSequence text) {
|
|
ReplacementSpan[] replacementSpans =
|
|
((Spanned) text).getSpans(0, text.length(), ReplacementSpan.class);
|
|
SpannableStringBuilder spannable = new SpannableStringBuilder(text);
|
|
if (replacementSpans.length == 0) {
|
|
return text;
|
|
}
|
|
for (int i = 0; i < replacementSpans.length; i++) {
|
|
ReplacementSpan span = replacementSpans[i];
|
|
CharSequence replacementText = span.getContentDescription();
|
|
if (span instanceof AccessibilityReplacementSpan) {
|
|
// We've already done enough
|
|
break;
|
|
}
|
|
if (replacementText == null) {
|
|
continue;
|
|
}
|
|
int spanToReplaceStart = spannable.getSpanStart(span);
|
|
int spanToReplaceEnd = spannable.getSpanEnd(span);
|
|
int spanToReplaceFlags = spannable.getSpanFlags(span);
|
|
if (spanToReplaceStart < 0) {
|
|
continue;
|
|
}
|
|
spannable.removeSpan(span);
|
|
ReplacementSpan replacementSpan = new AccessibilityReplacementSpan(replacementText);
|
|
spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd,
|
|
spanToReplaceFlags);
|
|
}
|
|
return spannable;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* Get the state description of this node.
|
|
*
|
|
* @return the state description
|
|
*/
|
|
public @Nullable CharSequence getStateDescription() {
|
|
return mStateDescription;
|
|
}
|
|
|
|
/**
|
|
* Gets the content description of this node.
|
|
*
|
|
* @return The content description.
|
|
*/
|
|
public CharSequence getContentDescription() {
|
|
return mContentDescription;
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets the state description of this node.
|
|
* <p>
|
|
* <strong>Note:</strong> Cannot be called from an
|
|
* {@link android.accessibilityservice.AccessibilityService}.
|
|
* This class is made immutable before being delivered to an AccessibilityService.
|
|
* </p>
|
|
*
|
|
* @param stateDescription the state description of this node.
|
|
*
|
|
* @throws IllegalStateException If called from an AccessibilityService.
|
|
*/
|
|
public void setStateDescription(@Nullable CharSequence stateDescription) {
|
|
enforceNotSealed();
|
|
mStateDescription = (stateDescription == null) ? null
|
|
: stateDescription.subSequence(0, stateDescription.length());
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @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.
|
|
*
|
|
* @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 the {@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
|
|
@TestApi
|
|
public long getSourceNodeId() {
|
|
return mSourceNodeId;
|
|
}
|
|
|
|
/**
|
|
* Sets the unique id to act as a key to identify the node. If the node instance is replaced
|
|
* after refreshing the layout, calling this API to assign the same unique id to the new
|
|
* alike node can help accessibility service to identify it.
|
|
*
|
|
* @param uniqueId The unique id that is associated with a visible node on the screen
|
|
*/
|
|
public void setUniqueId(@Nullable String uniqueId) {
|
|
enforceNotSealed();
|
|
mUniqueId = uniqueId;
|
|
}
|
|
|
|
/**
|
|
* Gets the unique id of the node.
|
|
*
|
|
* @return The unique id
|
|
*/
|
|
@Nullable
|
|
public String getUniqueId() {
|
|
return mUniqueId;
|
|
}
|
|
|
|
/**
|
|
* Sets the container title for app-developer-defined container which can be any type of
|
|
* ViewGroup or layout.
|
|
* Container title will be used to group together related controls, similar to HTML fieldset.
|
|
* Or container title may identify a large piece of the UI that is visibly grouped together,
|
|
* such as a toolbar or a card, etc.
|
|
* <p>
|
|
* Container title helps to assist in navigation across containers and other groups.
|
|
* For example, a screen reader may use this to determine where to put accessibility focus.
|
|
* </p>
|
|
* <p>
|
|
* Container title is different from pane title{@link #setPaneTitle} which indicates that the
|
|
* node represents a window or activity.
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* Example: An app can set container titles on several non-modal menus, containing TextViews
|
|
* or ImageButtons that have content descriptions, text, etc. Screen readers can quickly
|
|
* switch accessibility focus among menus instead of child views. Other accessibility-services
|
|
* can easily find the menu.
|
|
* </p>
|
|
*
|
|
* @param containerTitle The container title that is associated with a ViewGroup/Layout on the
|
|
* screen.
|
|
*/
|
|
public void setContainerTitle(@Nullable CharSequence containerTitle) {
|
|
enforceNotSealed();
|
|
mContainerTitle = (containerTitle == null) ? null
|
|
: containerTitle.subSequence(0, containerTitle.length());
|
|
}
|
|
|
|
/**
|
|
* Returns the container title.
|
|
*
|
|
* @see #setContainerTitle
|
|
*/
|
|
@Nullable
|
|
public CharSequence getContainerTitle() {
|
|
return mContainerTitle;
|
|
}
|
|
|
|
/**
|
|
* Sets the token and node id of the leashed parent.
|
|
*
|
|
* @param token The token.
|
|
* @param viewId The accessibility view id.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public void setLeashedParent(@Nullable IBinder token, int viewId) {
|
|
enforceNotSealed();
|
|
mLeashedParent = token;
|
|
mLeashedParentNodeId = makeNodeId(viewId, AccessibilityNodeProvider.HOST_VIEW_ID);
|
|
}
|
|
|
|
/**
|
|
* Gets the token of the leashed parent.
|
|
*
|
|
* @return The token.
|
|
* @hide
|
|
*/
|
|
public @Nullable IBinder getLeashedParent() {
|
|
return mLeashedParent;
|
|
}
|
|
|
|
/**
|
|
* Gets the node id of the leashed parent.
|
|
*
|
|
* @return The accessibility node id.
|
|
* @hide
|
|
*/
|
|
public long getLeashedParentNodeId() {
|
|
return mLeashedParentNodeId;
|
|
}
|
|
|
|
/**
|
|
* Connects this node to the View's root so that operations on this node can query the entire
|
|
* {@link AccessibilityNodeInfo} tree and perform accessibility actions on nodes.
|
|
*
|
|
* <p>
|
|
* Testing or debugging tools should create this {@link AccessibilityNodeInfo} node using
|
|
* {@link View#createAccessibilityNodeInfo()} or {@link AccessibilityNodeProvider} and call this
|
|
* method, then navigate and interact with the node tree by calling methods on the node.
|
|
* Calling this method more than once on the same node is a no-op. After calling this method,
|
|
* all nodes linked to this node (children, ancestors, etc.) are also queryable.
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* Here "query" refers to the following node operations:
|
|
* <li>check properties of this node (example: {@link #isScrollable()})</li>
|
|
* <li>find and query children (example: {@link #getChild(int)})</li>
|
|
* <li>find and query the parent (example: {@link #getParent()})</li>
|
|
* <li>find focus (examples: {@link #findFocus(int)}, {@link #focusSearch(int)})</li>
|
|
* <li>find and query other nodes (example: {@link #findAccessibilityNodeInfosByText(String)},
|
|
* {@link #findAccessibilityNodeInfosByViewId(String)})</li>
|
|
* <li>perform actions (example: {@link #performAction(int)})</li>
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* This is intended for short-lived inspections from testing or debugging tools in the app
|
|
* process, as operations on this node tree will only succeed as long as the associated
|
|
* view hierarchy remains attached to a window. {@link AccessibilityNodeInfo} objects can
|
|
* quickly become out of sync with their corresponding {@link View} objects; if you wish to
|
|
* inspect a changed or different view hierarchy then create a new node from any view in that
|
|
* hierarchy and call this method on that new node, instead of disabling & re-enabling the
|
|
* connection on the previous node.
|
|
* </p>
|
|
*
|
|
* @param view The view that generated this node, or any view in the same view-root hierarchy.
|
|
* @param enabled Whether to enable (true) or disable (false) querying from the app process.
|
|
* @throws IllegalStateException If called from an {@link AccessibilityService}, or if provided
|
|
* a {@link View} that is not attached to a window.
|
|
*/
|
|
public void setQueryFromAppProcessEnabled(@NonNull View view, boolean enabled) {
|
|
enforceNotSealed();
|
|
|
|
if (!enabled) {
|
|
setConnectionId(UNDEFINED_CONNECTION_ID);
|
|
return;
|
|
}
|
|
|
|
if (mConnectionId != UNDEFINED_CONNECTION_ID) {
|
|
return;
|
|
}
|
|
|
|
ViewRootImpl viewRootImpl = view.getViewRootImpl();
|
|
if (viewRootImpl == null) {
|
|
throw new IllegalStateException(
|
|
"Cannot link a node to a view that is not attached to a window.");
|
|
}
|
|
setConnectionId(viewRootImpl.getDirectAccessibilityConnectionId());
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
private static boolean usingDirectConnection(int connectionId) {
|
|
return AccessibilityInteractionClient.getConnection(
|
|
connectionId) instanceof DirectAccessibilityConnection;
|
|
}
|
|
|
|
/**
|
|
* Enforces that this instance is sealed, unless using a {@link DirectAccessibilityConnection}
|
|
* which allows queries while the node is not sealed.
|
|
*
|
|
* @throws IllegalStateException If this instance is not sealed.
|
|
*
|
|
* @hide
|
|
*/
|
|
protected void enforceSealed() {
|
|
if (!usingDirectConnection(mConnectionId) && !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.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Create a new instance using the
|
|
* constructor {@link #AccessibilityNodeInfo(View)} instead.
|
|
* @param source The source view.
|
|
* @return An instance.
|
|
*
|
|
* @see #setSource(View)
|
|
*/
|
|
@Deprecated
|
|
public static AccessibilityNodeInfo obtain(View source) {
|
|
return new AccessibilityNodeInfo(source);
|
|
}
|
|
|
|
/**
|
|
* Returns a cached instance if such is available otherwise a new one
|
|
* and sets the source.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Create a new instance using the
|
|
* constructor {@link #AccessibilityNodeInfo(View, int)} instead.
|
|
*
|
|
* @param root The root of the virtual subtree.
|
|
* @param virtualDescendantId The id of the virtual descendant.
|
|
* @return An instance.
|
|
*
|
|
* @see #setSource(View, int)
|
|
*/
|
|
@Deprecated
|
|
public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
|
|
return new AccessibilityNodeInfo(root, virtualDescendantId);
|
|
}
|
|
|
|
/**
|
|
* Instantiates a new AccessibilityNodeInfo.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Create a new instance using the
|
|
* constructor {@link #AccessibilityNodeInfo()} instead.
|
|
* @return An instance.
|
|
*/
|
|
@Deprecated
|
|
public static AccessibilityNodeInfo obtain() {
|
|
return new AccessibilityNodeInfo();
|
|
}
|
|
|
|
/**
|
|
* Instantiates a new AccessibilityNodeInfo initialized from the given
|
|
* <code>info</code>.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Create a new instance using the
|
|
* constructor {@link #AccessibilityNodeInfo(AccessibilityNodeInfo)} instead.
|
|
* @param info The other info.
|
|
* @return An instance.
|
|
*/
|
|
@Deprecated
|
|
public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
|
|
return new AccessibilityNodeInfo(info);
|
|
}
|
|
|
|
/**
|
|
* Would previously return an instance back to be reused.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Calling this function now will have
|
|
* no effect.
|
|
*/
|
|
@Deprecated
|
|
public void recycle() {}
|
|
|
|
/**
|
|
* {@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.
|
|
}
|
|
|
|
/** @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 (mMinDurationBetweenContentChanges
|
|
!= DEFAULT.mMinDurationBetweenContentChanges) {
|
|
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(mBoundsInWindow, DEFAULT.mBoundsInWindow)) {
|
|
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(mStateDescription, DEFAULT.mStateDescription)) {
|
|
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(mContainerTitle, DEFAULT.mContainerTitle)) {
|
|
nonDefaultFields |= bitAt(fieldIndex);
|
|
}
|
|
fieldIndex++;
|
|
if (!Objects.equals(mViewIdResourceName, DEFAULT.mViewIdResourceName)) {
|
|
nonDefaultFields |= bitAt(fieldIndex);
|
|
}
|
|
fieldIndex++;
|
|
if (!Objects.equals(mUniqueId, DEFAULT.mUniqueId)) {
|
|
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);
|
|
}
|
|
fieldIndex++;
|
|
if (!Objects.equals(mExtraRenderingInfo, DEFAULT.mExtraRenderingInfo)) {
|
|
nonDefaultFields |= bitAt(fieldIndex);
|
|
}
|
|
fieldIndex++;
|
|
if (mLeashedChild != DEFAULT.mLeashedChild) {
|
|
nonDefaultFields |= bitAt(fieldIndex);
|
|
}
|
|
fieldIndex++;
|
|
if (mLeashedParent != DEFAULT.mLeashedParent) {
|
|
nonDefaultFields |= bitAt(fieldIndex);
|
|
}
|
|
fieldIndex++;
|
|
if (mLeashedParentNodeId != DEFAULT.mLeashedParentNodeId) {
|
|
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.writeLong(mMinDurationBetweenContentChanges);
|
|
}
|
|
|
|
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++)) {
|
|
parcel.writeInt(mBoundsInWindow.top);
|
|
parcel.writeInt(mBoundsInWindow.bottom);
|
|
parcel.writeInt(mBoundsInWindow.left);
|
|
parcel.writeInt(mBoundsInWindow.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)) {
|
|
action.writeToParcel(parcel, flags);
|
|
}
|
|
}
|
|
} 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(mStateDescription);
|
|
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.writeCharSequence(mContainerTitle);
|
|
|
|
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeString(mViewIdResourceName);
|
|
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeString(mUniqueId);
|
|
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());
|
|
parcel.writeInt(mCollectionInfo.getItemCount());
|
|
parcel.writeInt(mCollectionInfo.getImportantForAccessibilityItemCount());
|
|
}
|
|
|
|
if (isBitSet(nonDefaultFields, fieldIndex++)) {
|
|
parcel.writeString(mCollectionItemInfo.getRowTitle());
|
|
parcel.writeInt(mCollectionItemInfo.getRowIndex());
|
|
parcel.writeInt(mCollectionItemInfo.getRowSpan());
|
|
parcel.writeString(mCollectionItemInfo.getColumnTitle());
|
|
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 (isBitSet(nonDefaultFields, fieldIndex++)) {
|
|
parcel.writeValue(mExtraRenderingInfo.getLayoutSize());
|
|
parcel.writeFloat(mExtraRenderingInfo.getTextSizeInPx());
|
|
parcel.writeInt(mExtraRenderingInfo.getTextSizeUnit());
|
|
}
|
|
|
|
if (isBitSet(nonDefaultFields, fieldIndex++)) {
|
|
parcel.writeStrongBinder(mLeashedChild);
|
|
}
|
|
if (isBitSet(nonDefaultFields, fieldIndex++)) {
|
|
parcel.writeStrongBinder(mLeashedParent);
|
|
}
|
|
if (isBitSet(nonDefaultFields, fieldIndex++)) {
|
|
parcel.writeLong(mLeashedParentNodeId);
|
|
}
|
|
|
|
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;
|
|
mMinDurationBetweenContentChanges = other.mMinDurationBetweenContentChanges;
|
|
mWindowId = other.mWindowId;
|
|
mConnectionId = other.mConnectionId;
|
|
mUniqueId = other.mUniqueId;
|
|
mBoundsInParent.set(other.mBoundsInParent);
|
|
mBoundsInScreen.set(other.mBoundsInScreen);
|
|
mBoundsInWindow.set(other.mBoundsInWindow);
|
|
mPackageName = other.mPackageName;
|
|
mClassName = other.mClassName;
|
|
mText = other.mText;
|
|
mOriginalText = other.mOriginalText;
|
|
mHintText = other.mHintText;
|
|
mError = other.mError;
|
|
mStateDescription = other.mStateDescription;
|
|
mContentDescription = other.mContentDescription;
|
|
mPaneTitle = other.mPaneTitle;
|
|
mTooltipText = other.mTooltipText;
|
|
mContainerTitle = other.mContainerTitle;
|
|
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;
|
|
|
|
initCopyInfos(other);
|
|
|
|
final TouchDelegateInfo otherInfo = other.mTouchDelegateInfo;
|
|
mTouchDelegateInfo = (otherInfo != null)
|
|
? new TouchDelegateInfo(otherInfo.mTargetMap, true) : null;
|
|
|
|
mLeashedChild = other.mLeashedChild;
|
|
mLeashedParent = other.mLeashedParent;
|
|
mLeashedParentNodeId = other.mLeashedParentNodeId;
|
|
}
|
|
|
|
private void initCopyInfos(AccessibilityNodeInfo other) {
|
|
RangeInfo ri = other.mRangeInfo;
|
|
mRangeInfo = (ri == null) ? null
|
|
: new RangeInfo(ri.mType, ri.mMin, ri.mMax, ri.mCurrent);
|
|
CollectionInfo ci = other.mCollectionInfo;
|
|
mCollectionInfo = (ci == null) ? null
|
|
: new CollectionInfo(ci.mRowCount, ci.mColumnCount,
|
|
ci.mHierarchical, ci.mSelectionMode, ci.mItemCount,
|
|
ci.mImportantForAccessibilityItemCount);
|
|
CollectionItemInfo cii = other.mCollectionItemInfo;
|
|
CollectionItemInfo.Builder builder = new CollectionItemInfo.Builder();
|
|
mCollectionItemInfo = (cii == null) ? null
|
|
: builder.setRowTitle(cii.mRowTitle).setRowIndex(cii.mRowIndex).setRowSpan(
|
|
cii.mRowSpan).setColumnTitle(cii.mColumnTitle).setColumnIndex(
|
|
cii.mColumnIndex).setColumnSpan(cii.mColumnSpan).setHeading(
|
|
cii.mHeading).setSelected(cii.mSelected).build();
|
|
ExtraRenderingInfo ti = other.mExtraRenderingInfo;
|
|
mExtraRenderingInfo = (ti == null) ? null
|
|
: new ExtraRenderingInfo(ti);
|
|
}
|
|
|
|
/**
|
|
* 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++)) {
|
|
mMinDurationBetweenContentChanges = 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++)) {
|
|
mBoundsInWindow.top = parcel.readInt();
|
|
mBoundsInWindow.bottom = parcel.readInt();
|
|
mBoundsInWindow.left = parcel.readInt();
|
|
mBoundsInWindow.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 =
|
|
AccessibilityAction.CREATOR.createFromParcel(parcel);
|
|
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++)) mStateDescription = 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++)) mContainerTitle = parcel.readCharSequence();
|
|
if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString();
|
|
if (isBitSet(nonDefaultFields, fieldIndex++)) mUniqueId = 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;
|
|
|
|
mRangeInfo = isBitSet(nonDefaultFields, fieldIndex++)
|
|
? new RangeInfo(
|
|
parcel.readInt(),
|
|
parcel.readFloat(),
|
|
parcel.readFloat(),
|
|
parcel.readFloat())
|
|
: null;
|
|
|
|
mCollectionInfo = isBitSet(nonDefaultFields, fieldIndex++)
|
|
? new CollectionInfo(
|
|
parcel.readInt(),
|
|
parcel.readInt(),
|
|
parcel.readInt() == 1,
|
|
parcel.readInt(),
|
|
parcel.readInt(),
|
|
parcel.readInt())
|
|
: null;
|
|
|
|
mCollectionItemInfo = isBitSet(nonDefaultFields, fieldIndex++)
|
|
? new CollectionItemInfo(
|
|
parcel.readString(),
|
|
parcel.readInt(),
|
|
parcel.readInt(),
|
|
parcel.readString(),
|
|
parcel.readInt(),
|
|
parcel.readInt(),
|
|
parcel.readInt() == 1,
|
|
parcel.readInt() == 1)
|
|
: null;
|
|
|
|
if (isBitSet(nonDefaultFields, fieldIndex++)) {
|
|
mTouchDelegateInfo = TouchDelegateInfo.CREATOR.createFromParcel(parcel);
|
|
}
|
|
|
|
if (isBitSet(nonDefaultFields, fieldIndex++)) {
|
|
mExtraRenderingInfo = new ExtraRenderingInfo(null);
|
|
mExtraRenderingInfo.mLayoutSize = (Size) parcel.readValue(null);
|
|
mExtraRenderingInfo.mTextSizeInPx = parcel.readFloat();
|
|
mExtraRenderingInfo.mTextSizeUnit = parcel.readInt();
|
|
}
|
|
|
|
if (isBitSet(nonDefaultFields, fieldIndex++)) {
|
|
mLeashedChild = parcel.readStrongBinder();
|
|
}
|
|
if (isBitSet(nonDefaultFields, fieldIndex++)) {
|
|
mLeashedParent = parcel.readStrongBinder();
|
|
}
|
|
if (isBitSet(nonDefaultFields, fieldIndex++)) {
|
|
mLeashedParentNodeId = parcel.readLong();
|
|
}
|
|
|
|
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";
|
|
case R.id.accessibilityActionPressAndHold:
|
|
return "ACTION_PRESS_AND_HOLD";
|
|
case R.id.accessibilityActionImeEnter:
|
|
return "ACTION_IME_ENTER";
|
|
case R.id.accessibilityActionDragStart:
|
|
return "ACTION_DRAG";
|
|
case R.id.accessibilityActionDragCancel:
|
|
return "ACTION_CANCEL_DRAG";
|
|
case R.id.accessibilityActionDragDrop:
|
|
return "ACTION_DROP";
|
|
default: {
|
|
if (action == R.id.accessibilityActionShowTextSuggestions) {
|
|
return "ACTION_SHOW_TEXT_SUGGESTIONS";
|
|
}
|
|
if (action == R.id.accessibilityActionScrollInDirection) {
|
|
return "ACTION_SCROLL_IN_DIRECTION";
|
|
}
|
|
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) {
|
|
final boolean hasWindowId = windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
|
|
return ((usingDirectConnection(connectionId) || hasWindowId)
|
|
&& (getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID)
|
|
&& (connectionId != UNDEFINED_CONNECTION_ID));
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(@Nullable 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: 0x").append(Long.toHexString(mSourceNodeId));
|
|
builder.append("; windowId: 0x").append(Long.toHexString(mWindowId));
|
|
builder.append("; accessibilityViewId: 0x")
|
|
.append(Long.toHexString(getAccessibilityViewId(mSourceNodeId)));
|
|
builder.append("; virtualDescendantId: 0x")
|
|
.append(Long.toHexString(getVirtualDescendantId(mSourceNodeId)));
|
|
builder.append("; mParentNodeId: 0x").append(Long.toHexString(mParentNodeId));
|
|
builder.append("; traversalBefore: 0x").append(Long.toHexString(mTraversalBefore));
|
|
builder.append("; traversalAfter: 0x").append(Long.toHexString(mTraversalAfter));
|
|
builder.append("; minDurationBetweenContentChanges: ")
|
|
.append(mMinDurationBetweenContentChanges);
|
|
|
|
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("0x").append(Long.toHexString(childIds.get(i)));
|
|
if (i < count - 1) {
|
|
builder.append(", ");
|
|
}
|
|
}
|
|
}
|
|
builder.append("]");
|
|
}
|
|
|
|
builder.append("; boundsInParent: ").append(mBoundsInParent);
|
|
builder.append("; boundsInScreen: ").append(mBoundsInScreen);
|
|
builder.append("; boundsInWindow: ").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("; stateDescription: ").append(mStateDescription);
|
|
builder.append("; contentDescription: ").append(mContentDescription);
|
|
builder.append("; tooltipText: ").append(mTooltipText);
|
|
builder.append("; containerTitle: ").append(mContainerTitle);
|
|
builder.append("; viewIdResName: ").append(mViewIdResourceName);
|
|
builder.append("; uniqueId: ").append(mUniqueId);
|
|
|
|
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("; granularScrollingSupported: ").append(isGranularScrollingSupported());
|
|
builder.append("; importantForAccessibility: ").append(isImportantForAccessibility());
|
|
builder.append("; visible: ").append(isVisibleToUser());
|
|
builder.append("; actions: ").append(mActions);
|
|
builder.append("; isTextSelectable: ").append(isTextSelectable());
|
|
|
|
return builder.toString();
|
|
}
|
|
|
|
private static AccessibilityNodeInfo getNodeForAccessibilityId(int connectionId,
|
|
int windowId, long accessibilityId) {
|
|
return getNodeForAccessibilityId(connectionId, windowId, accessibilityId,
|
|
FLAG_PREFETCH_ANCESTORS
|
|
| FLAG_PREFETCH_DESCENDANTS_HYBRID | FLAG_PREFETCH_SIBLINGS);
|
|
}
|
|
|
|
private static AccessibilityNodeInfo getNodeForAccessibilityId(int connectionId,
|
|
int windowId, long accessibilityId, @PrefetchingStrategy int prefetchingStrategy) {
|
|
if (!canPerformRequestOverConnection(connectionId, windowId, accessibilityId)) {
|
|
return null;
|
|
}
|
|
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
|
|
return client.findAccessibilityNodeInfoByAccessibilityId(connectionId,
|
|
windowId, accessibilityId, false, prefetchingStrategy, null);
|
|
}
|
|
|
|
private static AccessibilityNodeInfo getNodeForAccessibilityId(int connectionId,
|
|
IBinder leashToken, long accessibilityId) {
|
|
return getNodeForAccessibilityId(connectionId, leashToken, accessibilityId,
|
|
FLAG_PREFETCH_ANCESTORS
|
|
| FLAG_PREFETCH_DESCENDANTS_HYBRID | FLAG_PREFETCH_SIBLINGS);
|
|
}
|
|
|
|
private static AccessibilityNodeInfo getNodeForAccessibilityId(int connectionId,
|
|
IBinder leashToken, long accessibilityId,
|
|
@PrefetchingStrategy int prefetchingStrategy) {
|
|
if (!((leashToken != null)
|
|
&& (getAccessibilityViewId(accessibilityId) != UNDEFINED_ITEM_ID)
|
|
&& (connectionId != UNDEFINED_CONNECTION_ID))) {
|
|
return null;
|
|
}
|
|
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
|
|
return client.findAccessibilityNodeInfoByAccessibilityId(connectionId,
|
|
leashToken, accessibilityId, false, prefetchingStrategy, 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>Overridden 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>
|
|
* <aside class="note">
|
|
* <b>Note:</b> 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. </aside>
|
|
* </p>
|
|
* <p>
|
|
* <aside class="note">
|
|
* <b>Note:</b> Use {@link androidx.core.view.ViewCompat#addAccessibilityAction(View,
|
|
* CharSequence, AccessibilityViewCommand)} to register an action directly on the view. </aside>
|
|
* </p>
|
|
*/
|
|
public static final class AccessibilityAction implements Parcelable {
|
|
|
|
/** @hide */
|
|
public static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<>();
|
|
|
|
/**
|
|
* Action that gives input focus to the node.
|
|
* <p>The focus request send an event of {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
|
|
* if successful. In the View system, this is handled by {@link View#requestFocus}.
|
|
*
|
|
* <p>The node that is focused should return {@code true} for
|
|
* {@link AccessibilityNodeInfo#isFocused()}.
|
|
*
|
|
* See {@link #ACTION_ACCESSIBILITY_FOCUS} for the difference between system and
|
|
* accessibility focus.
|
|
*/
|
|
public static final AccessibilityAction ACTION_FOCUS =
|
|
new AccessibilityAction(AccessibilityNodeInfo.ACTION_FOCUS);
|
|
|
|
/**
|
|
* Action that clears input focus of the node.
|
|
* <p>The node that is cleared should return {@code false} for
|
|
* {@link AccessibilityNodeInfo#isFocused)}.
|
|
*/
|
|
public static final AccessibilityAction ACTION_CLEAR_FOCUS =
|
|
new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS);
|
|
|
|
/**
|
|
* Action that selects the node.
|
|
* The view the implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_SELECTED} event.
|
|
* @see AccessibilityAction#ACTION_CLEAR_SELECTION
|
|
*/
|
|
public static final AccessibilityAction ACTION_SELECT =
|
|
new AccessibilityAction(AccessibilityNodeInfo.ACTION_SELECT);
|
|
|
|
/**
|
|
* Action that deselects the node.
|
|
* @see AccessibilityAction#ACTION_SELECT
|
|
*/
|
|
public static final AccessibilityAction ACTION_CLEAR_SELECTION =
|
|
new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION);
|
|
|
|
/**
|
|
* Action that clicks on the node info.
|
|
*
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_CLICKED} event. In the View system,
|
|
* the default handling of this action when performed by a service is to call
|
|
* {@link View#performClick()}, and setting a
|
|
* {@link View#setOnClickListener(View.OnClickListener)} automatically adds this action.
|
|
*
|
|
* <p>{@link #isClickable()} should return true if this action is available.
|
|
*/
|
|
public static final AccessibilityAction ACTION_CLICK =
|
|
new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK);
|
|
|
|
/**
|
|
* Action that long clicks on the node.
|
|
*
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED} event. In the View system,
|
|
* the default handling of this action when performed by a service is to call
|
|
* {@link View#performLongClick()}, and setting a
|
|
* {@link View#setOnLongClickListener(View.OnLongClickListener)} automatically adds this
|
|
* action.
|
|
*
|
|
* <p>{@link #isLongClickable()} should return true if this action is available.
|
|
*/
|
|
public static final AccessibilityAction ACTION_LONG_CLICK =
|
|
new AccessibilityAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
|
|
|
|
/**
|
|
* Action that gives accessibility focus to the node.
|
|
*
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED} event
|
|
* if successful. The node that is focused should return {@code true} for
|
|
* {@link AccessibilityNodeInfo#isAccessibilityFocused()}.
|
|
*
|
|
* <p>This is intended to be used by screen readers to assist with user navigation. Apps
|
|
* changing focus can confuse screen readers, so the resulting behavior can vary by device
|
|
* and screen reader version.
|
|
* <p>This is distinct from {@link #ACTION_FOCUS}, which refers to system focus. System
|
|
* focus is typically used to convey targets for keyboard navigation.
|
|
*/
|
|
public static final AccessibilityAction ACTION_ACCESSIBILITY_FOCUS =
|
|
new AccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
|
|
|
|
/**
|
|
* Action that clears accessibility focus of the node.
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED} event if successful. The
|
|
* node that is cleared should return {@code false} for
|
|
* {@link AccessibilityNodeInfo#isAccessibilityFocused()}.
|
|
*/
|
|
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);
|
|
|
|
// TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
|
|
/**
|
|
* Action to scroll the node content forward.
|
|
*
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. Depending on the orientation,
|
|
* this element should also add the relevant directional scroll actions of
|
|
* {@link #ACTION_SCROLL_LEFT}, {@link #ACTION_SCROLL_RIGHT},
|
|
* {@link #ACTION_SCROLL_UP}, and {@link #ACTION_SCROLL_DOWN}. If the scrolling brings
|
|
* the next or previous element into view as the center element, such as in a ViewPager2,
|
|
* use {@link #ACTION_PAGE_DOWN} and the other page actions instead of the directional
|
|
* actions.
|
|
* <p>Example: a scrolling UI of vertical orientation with a forward
|
|
* scroll action should also add the scroll down action:
|
|
* <pre class="prettyprint"><code>
|
|
* onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
|
* super.onInitializeAccessibilityNodeInfo(info);
|
|
* if (canScrollForward) {
|
|
* info.addAction(ACTION_SCROLL_FORWARD);
|
|
* info.addAction(ACTION_SCROLL_DOWN);
|
|
* }
|
|
* }
|
|
* performAccessibilityAction(int action, Bundle bundle) {
|
|
* if (action == ACTION_SCROLL_FORWARD || action == ACTION_SCROLL_DOWN) {
|
|
* scrollForward();
|
|
* }
|
|
* }
|
|
* scrollForward() {
|
|
* ...
|
|
* if (mAccessibilityManager.isEnabled()) {
|
|
* event = new AccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
|
|
* event.setScrollDeltaX(dx);
|
|
* event.setScrollDeltaY(dy);
|
|
* event.setMaxScrollX(maxDx);
|
|
* event.setMaxScrollY(maxDY);
|
|
* sendAccessibilityEventUnchecked(event);
|
|
* }
|
|
* }
|
|
* </code>
|
|
* </pre></p>
|
|
*/
|
|
public static final AccessibilityAction ACTION_SCROLL_FORWARD =
|
|
new AccessibilityAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
|
|
|
|
// TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
|
|
/**
|
|
* Action to scroll the node content backward.
|
|
*
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. Depending on the orientation,
|
|
* this element should also add the relevant directional scroll actions of
|
|
* {@link #ACTION_SCROLL_LEFT}, {@link #ACTION_SCROLL_RIGHT},
|
|
* {@link #ACTION_SCROLL_UP}, and {@link #ACTION_SCROLL_DOWN}. If the scrolling brings
|
|
* the next or previous element into view as the center element, such as in a ViewPager2,
|
|
* use {@link #ACTION_PAGE_DOWN} and the other page actions instead of the directional
|
|
* actions.
|
|
* <p> Example: a scrolling UI of horizontal orientation with a backward
|
|
* scroll action should also add the scroll left/right action (LTR/RTL):
|
|
* <pre class="prettyprint"><code>
|
|
* onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
|
* super.onInitializeAccessibilityNodeInfo(info);
|
|
* if (canScrollBackward) {
|
|
* info.addAction(ACTION_SCROLL_FORWARD);
|
|
* if (leftToRight) {
|
|
* info.addAction(ACTION_SCROLL_LEFT);
|
|
* } else {
|
|
* info.addAction(ACTION_SCROLL_RIGHT);
|
|
* }
|
|
* }
|
|
* }
|
|
* performAccessibilityAction(int action, Bundle bundle) {
|
|
* if (action == ACTION_SCROLL_BACKWARD) {
|
|
* scrollBackward();
|
|
* } else if (action == ACTION_SCROLL_LEFT) {
|
|
* if (!isRTL()){
|
|
* scrollBackward();
|
|
* }
|
|
* } else if (action == ACTION_SCROLL_RIGHT) {
|
|
* if (isRTL()){
|
|
* scrollBackward();
|
|
* }
|
|
* }
|
|
* }
|
|
* scrollBackward() {
|
|
* ...
|
|
* if (mAccessibilityManager.isEnabled()) {
|
|
* event = new AccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
|
|
* event.setScrollDeltaX(dx);
|
|
* event.setScrollDeltaY(dy);
|
|
* event.setMaxScrollX(maxDx);
|
|
* event.setMaxScrollY(maxDY);
|
|
* sendAccessibilityEventUnchecked(event);
|
|
* }
|
|
* }
|
|
* </code>
|
|
* </pre></p>
|
|
*/
|
|
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>
|
|
* <p> If this is a text selection, the UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED} event if its selection is
|
|
* updated. This element should also return {@code true} for
|
|
* {@link AccessibilityNodeInfo#isTextSelectable()}.
|
|
* @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>
|
|
* <p> The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED} event if its text is updated.
|
|
* This element should also return {@code true} for
|
|
* {@link AccessibilityNodeInfo#isEditable()}.
|
|
*/
|
|
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.
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
|
|
*
|
|
* @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>
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
|
|
*
|
|
* @see AccessibilityNodeInfo#getCollectionInfo()
|
|
*/
|
|
public static final AccessibilityAction ACTION_SCROLL_TO_POSITION =
|
|
new AccessibilityAction(R.id.accessibilityActionScrollToPosition);
|
|
|
|
/**
|
|
* Action that brings fully on screen the next node in the specified direction.
|
|
*
|
|
* <p>
|
|
* This should include wrapping around to the next/previous row, column, etc. in a
|
|
* collection if one is available. If there is no node in that direction, the action
|
|
* should fail and return false.
|
|
* </p>
|
|
* <p>
|
|
* This action should be used instead of
|
|
* {@link AccessibilityAction#ACTION_SCROLL_TO_POSITION} when a widget does not have
|
|
* clear row and column semantics or if a directional search is needed to find a node in
|
|
* a complex ViewGroup where individual nodes may span multiple rows or columns. The
|
|
* implementing widget must send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_TARGETED_BY_SCROLL} accessibility event with the
|
|
* scroll target as the source. An accessibility service can listen for this event,
|
|
* inspect its source, and use the result when determining where to place accessibility
|
|
* focus.
|
|
* <p>
|
|
* <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_DIRECTION_INT}. This is a
|
|
* required argument.<br>
|
|
* </p>
|
|
*/
|
|
@NonNull public static final AccessibilityAction ACTION_SCROLL_IN_DIRECTION =
|
|
new AccessibilityAction(R.id.accessibilityActionScrollInDirection);
|
|
|
|
// TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
|
|
/**
|
|
* Action to scroll the node content up.
|
|
*
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
|
|
*/
|
|
public static final AccessibilityAction ACTION_SCROLL_UP =
|
|
new AccessibilityAction(R.id.accessibilityActionScrollUp);
|
|
|
|
// TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
|
|
/**
|
|
* Action to scroll the node content left.
|
|
*
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
|
|
*/
|
|
public static final AccessibilityAction ACTION_SCROLL_LEFT =
|
|
new AccessibilityAction(R.id.accessibilityActionScrollLeft);
|
|
|
|
// TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
|
|
/**
|
|
* Action to scroll the node content down.
|
|
*
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
|
|
*/
|
|
public static final AccessibilityAction ACTION_SCROLL_DOWN =
|
|
new AccessibilityAction(R.id.accessibilityActionScrollDown);
|
|
|
|
// TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
|
|
/**
|
|
* Action to scroll the node content right.
|
|
*
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
|
|
*/
|
|
public static final AccessibilityAction ACTION_SCROLL_RIGHT =
|
|
new AccessibilityAction(R.id.accessibilityActionScrollRight);
|
|
|
|
/**
|
|
* Action to move to the page above.
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
|
|
*/
|
|
public static final AccessibilityAction ACTION_PAGE_UP =
|
|
new AccessibilityAction(R.id.accessibilityActionPageUp);
|
|
|
|
/**
|
|
* Action to move to the page below.
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
|
|
*/
|
|
public static final AccessibilityAction ACTION_PAGE_DOWN =
|
|
new AccessibilityAction(R.id.accessibilityActionPageDown);
|
|
|
|
/**
|
|
* Action to move to the page left.
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
|
|
*/
|
|
public static final AccessibilityAction ACTION_PAGE_LEFT =
|
|
new AccessibilityAction(R.id.accessibilityActionPageLeft);
|
|
|
|
/**
|
|
* Action to move to the page right.
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
|
|
*/
|
|
public static final AccessibilityAction ACTION_PAGE_RIGHT =
|
|
new AccessibilityAction(R.id.accessibilityActionPageRight);
|
|
|
|
/**
|
|
* Action that context clicks the node.
|
|
*
|
|
* <p>The UI element that implements this should send a
|
|
* {@link AccessibilityEvent#TYPE_VIEW_CONTEXT_CLICKED} event. In the View system,
|
|
* the default handling of this action when performed by a service is to call
|
|
* {@link View#performContextClick()}, and setting a
|
|
* {@link View#setOnContextClickListener(View.OnContextClickListener)} automatically adds
|
|
* this action.
|
|
*
|
|
* <p>A context click usually occurs from a mouse pointer right-click or a stylus button
|
|
* press.
|
|
*
|
|
* <p>{@link #isContextClickable()} should return true if this action is available.
|
|
*/
|
|
public static final 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);
|
|
|
|
/**
|
|
* Action that presses and holds a node.
|
|
* <p>
|
|
* This action is for nodes that have distinct behavior that depends on how long a press is
|
|
* held. Nodes having a single action for long press should use {@link #ACTION_LONG_CLICK}
|
|
* instead of this action, and nodes should not expose both actions.
|
|
* <p>
|
|
* When calling {@code performAction(ACTION_PRESS_AND_HOLD, bundle}, use
|
|
* {@link #ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT} to specify how long the
|
|
* node is pressed. The first time an accessibility service performs ACTION_PRES_AND_HOLD
|
|
* on a node, it must specify 0 as ACTION_ARGUMENT_PRESS_AND_HOLD, so the application is
|
|
* notified that the held state has started. To ensure reasonable behavior, the values
|
|
* must be increased incrementally and may not exceed 10,000. UIs requested
|
|
* to hold for times outside of this range should ignore the action.
|
|
* <p>
|
|
* The total time the element is held could be specified by an accessibility user up-front,
|
|
* or may depend on what happens on the UI as the user continues to request the hold.
|
|
* <p>
|
|
* <strong>Note:</strong> The time between dispatching the action and it arriving in the
|
|
* UI process is not guaranteed. It is possible on a busy system for the time to expire
|
|
* unexpectedly. For the case of holding down a key for a repeating action, a delayed
|
|
* arrival should be benign. Please do not use this sort of action in cases where such
|
|
* delays will lead to unexpected UI behavior.
|
|
* <p>
|
|
*/
|
|
@NonNull public static final AccessibilityAction ACTION_PRESS_AND_HOLD =
|
|
new AccessibilityAction(R.id.accessibilityActionPressAndHold);
|
|
|
|
/**
|
|
* Action to send an ime actionId which is from
|
|
* {@link android.view.inputmethod.EditorInfo#actionId}. This ime actionId sets by
|
|
* {@link TextView#setImeActionLabel(CharSequence, int)}, or it would be
|
|
* {@link android.view.inputmethod.EditorInfo#IME_ACTION_UNSPECIFIED} if no specific
|
|
* actionId has set. A node should expose this action only for views that are currently
|
|
* with input focus and editable.
|
|
*/
|
|
@NonNull public static final AccessibilityAction ACTION_IME_ENTER =
|
|
new AccessibilityAction(R.id.accessibilityActionImeEnter);
|
|
|
|
/**
|
|
* Action to start a drag.
|
|
* <p>
|
|
* This action initiates a drag & drop within the system. The source's dragged content is
|
|
* prepared before the drag begins. In View, this action should prepare the arguments to
|
|
* {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)} and then
|
|
* call {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)} with
|
|
* {@link View#DRAG_FLAG_ACCESSIBILITY_ACTION}. The equivalent should be performed for other
|
|
* UI toolkits.
|
|
* </p>
|
|
*
|
|
* @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_STARTED
|
|
*/
|
|
@NonNull public static final AccessibilityAction ACTION_DRAG_START =
|
|
new AccessibilityAction(R.id.accessibilityActionDragStart);
|
|
|
|
/**
|
|
* Action to trigger a drop of the content being dragged.
|
|
* <p>
|
|
* This action is added to potential drop targets if the source started a drag with
|
|
* {@link #ACTION_DRAG_START}. In View, these targets are Views that accepted
|
|
* {@link android.view.DragEvent#ACTION_DRAG_STARTED} and have an
|
|
* {@link View.OnDragListener}, and the drop occurs at the center location of the View's
|
|
* window bounds.
|
|
* </p>
|
|
*
|
|
* @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_DROPPED
|
|
*/
|
|
@NonNull public static final AccessibilityAction ACTION_DRAG_DROP =
|
|
new AccessibilityAction(R.id.accessibilityActionDragDrop);
|
|
|
|
/**
|
|
* Action to cancel a drag.
|
|
* <p>
|
|
* This action is added to the source that started a drag with {@link #ACTION_DRAG_START}.
|
|
* </p>
|
|
*
|
|
* @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_CANCELLED
|
|
*/
|
|
@NonNull public static final AccessibilityAction ACTION_DRAG_CANCEL =
|
|
new AccessibilityAction(R.id.accessibilityActionDragCancel);
|
|
|
|
/**
|
|
* Action to show suggestions for editable text.
|
|
*/
|
|
@NonNull public static final AccessibilityAction ACTION_SHOW_TEXT_SUGGESTIONS =
|
|
new AccessibilityAction(R.id.accessibilityActionShowTextSuggestions);
|
|
|
|
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) {
|
|
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(@Nullable 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;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Write data into a parcel.
|
|
*/
|
|
public void writeToParcel(@NonNull Parcel out, int flags) {
|
|
out.writeInt(mActionId);
|
|
out.writeCharSequence(mLabel);
|
|
}
|
|
|
|
public static final @NonNull Parcelable.Creator<AccessibilityAction> CREATOR =
|
|
new Parcelable.Creator<AccessibilityAction>() {
|
|
public AccessibilityAction createFromParcel(Parcel in) {
|
|
return new AccessibilityAction(in);
|
|
}
|
|
|
|
public AccessibilityAction[] newArray(int size) {
|
|
return new AccessibilityAction[size];
|
|
}
|
|
};
|
|
|
|
private AccessibilityAction(Parcel in) {
|
|
mActionId = in.readInt();
|
|
mLabel = in.readCharSequence();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Class with information if a node is a range.
|
|
*/
|
|
public static final class RangeInfo {
|
|
|
|
/** 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 hundred. */
|
|
public static final int RANGE_TYPE_PERCENT = 2;
|
|
|
|
private int mType;
|
|
private float mMin;
|
|
private float mMax;
|
|
private float mCurrent;
|
|
/**
|
|
* Instantiates a new RangeInfo.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Create a new instance using the
|
|
* constructor {@link AccessibilityNodeInfo.RangeInfo#RangeInfo(int, float, float,
|
|
* float)} instead.
|
|
*
|
|
* @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.
|
|
*/
|
|
@Deprecated
|
|
public static RangeInfo obtain(int type, float min, float max, float current) {
|
|
return new RangeInfo(type, min, max, current);
|
|
}
|
|
|
|
/**
|
|
* Creates a new range.
|
|
*
|
|
* @param type The type of the range.
|
|
* @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no
|
|
* minimum.
|
|
* @param max The maximum value. Use {@code Float.POSITIVE_INFINITY} if the range has no
|
|
* maximum.
|
|
* @param current The current value.
|
|
*/
|
|
public 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.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Calling this function now will have
|
|
* no effect.
|
|
*/
|
|
@Deprecated
|
|
void recycle() {}
|
|
|
|
private void clear() {
|
|
mType = 0;
|
|
mMin = 0;
|
|
mMax = 0;
|
|
mCurrent = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Class with information if a node is a collection.
|
|
* <p>
|
|
* A collection of items has rows and columns and may be hierarchical.
|
|
* For example, a horizontal list is a collection with one column, as
|
|
* many rows as the list items, and is not hierarchical; A table is a
|
|
* collection with several rows, several columns, and is not hierarchical;
|
|
* A vertical tree is a hierarchical collection with one column and
|
|
* as many rows as the first level children.
|
|
* </p>
|
|
*/
|
|
public static 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;
|
|
|
|
/**
|
|
* Constant to denote a missing collection count.
|
|
*
|
|
* This should be used for {@code mItemCount} and
|
|
* {@code mImportantForAccessibilityItemCount} when values for those fields are not known.
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
|
|
public static final int UNDEFINED = -1;
|
|
|
|
private int mRowCount;
|
|
private int mColumnCount;
|
|
private boolean mHierarchical;
|
|
private int mSelectionMode;
|
|
private int mItemCount;
|
|
private int mImportantForAccessibilityItemCount;
|
|
|
|
/**
|
|
* Instantiates a CollectionInfo that is a clone of another one.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Create a new instance using the
|
|
* constructor {@link
|
|
* AccessibilityNodeInfo.CollectionInfo#CollectionInfo} instead.
|
|
*
|
|
* @param other The instance to clone.
|
|
* @hide
|
|
*/
|
|
public static CollectionInfo obtain(CollectionInfo other) {
|
|
return new CollectionInfo(other.mRowCount, other.mColumnCount, other.mHierarchical,
|
|
other.mSelectionMode, other.mItemCount,
|
|
other.mImportantForAccessibilityItemCount);
|
|
}
|
|
|
|
/**
|
|
* Obtains a pooled instance.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Create a new instance using the
|
|
* constructor {@link
|
|
* AccessibilityNodeInfo.CollectionInfo#CollectionInfo(int, int,
|
|
* boolean)} instead.
|
|
*
|
|
* @param rowCount The number of rows, or -1 if count is unknown.
|
|
* @param columnCount The number of columns, or -1 if count is unknown.
|
|
* @param hierarchical Whether the collection is hierarchical.
|
|
*/
|
|
public static CollectionInfo obtain(int rowCount, int columnCount,
|
|
boolean hierarchical) {
|
|
return new CollectionInfo(rowCount, columnCount, hierarchical, SELECTION_MODE_NONE);
|
|
}
|
|
|
|
/**
|
|
* Obtains a pooled instance.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Create a new instance using the
|
|
* constructor {@link
|
|
* AccessibilityNodeInfo.CollectionInfo#CollectionInfo(int, int,
|
|
* boolean, int)} instead.
|
|
*
|
|
* @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) {
|
|
return new CollectionInfo(rowCount, columnCount, hierarchical, selectionMode);
|
|
}
|
|
|
|
/**
|
|
* Creates a new instance.
|
|
*
|
|
* @param rowCount The number of rows.
|
|
* @param columnCount The number of columns.
|
|
* @param hierarchical Whether the collection is hierarchical.
|
|
*/
|
|
public CollectionInfo(int rowCount, int columnCount, boolean hierarchical) {
|
|
this(rowCount, columnCount, hierarchical, SELECTION_MODE_NONE);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
public CollectionInfo(int rowCount, int columnCount, boolean hierarchical,
|
|
int selectionMode) {
|
|
mRowCount = rowCount;
|
|
mColumnCount = columnCount;
|
|
mHierarchical = hierarchical;
|
|
mSelectionMode = selectionMode;
|
|
mItemCount = UNDEFINED;
|
|
mImportantForAccessibilityItemCount = UNDEFINED;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
* @param itemCount The collection's item count, which includes items that are unimportant
|
|
* for accessibility. When ViewGroups map cleanly to both row and column
|
|
* semantics, clients should populate the row and column counts and
|
|
* optionally populate this field. In all other cases, clients should
|
|
* populate this field so that accessibility services can use it to relay
|
|
* the collection size to users. This should be set to {@code UNDEFINED} if
|
|
* the item count is not known.
|
|
* @param importantForAccessibilityItemCount The count of the collection's views considered
|
|
* important for accessibility.
|
|
* @hide
|
|
*/
|
|
public CollectionInfo(int rowCount, int columnCount, boolean hierarchical,
|
|
int selectionMode, int itemCount, int importantForAccessibilityItemCount) {
|
|
mRowCount = rowCount;
|
|
mColumnCount = columnCount;
|
|
mHierarchical = hierarchical;
|
|
mSelectionMode = selectionMode;
|
|
mItemCount = itemCount;
|
|
mImportantForAccessibilityItemCount = importantForAccessibilityItemCount;
|
|
}
|
|
|
|
/**
|
|
* Gets the number of rows.
|
|
*
|
|
* @return The row count, or -1 if count is unknown.
|
|
*/
|
|
public int getRowCount() {
|
|
return mRowCount;
|
|
}
|
|
|
|
/**
|
|
* Gets the number of columns.
|
|
*
|
|
* @return The column count, or -1 if count is unknown.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Gets the number of items in the collection.
|
|
*
|
|
* @return The count of items, which may be {@code UNDEFINED} if the count is not known.
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
|
|
public int getItemCount() {
|
|
return mItemCount;
|
|
}
|
|
|
|
/**
|
|
* Gets the number of items in the collection considered important for accessibility.
|
|
*
|
|
* @return The count of items important for accessibility, which may be {@code UNDEFINED}
|
|
* if the count is not known.
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
|
|
public int getImportantForAccessibilityItemCount() {
|
|
return mImportantForAccessibilityItemCount;
|
|
}
|
|
|
|
/**
|
|
* Previously would recycle this instance.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Calling this function now will have
|
|
* no effect.
|
|
*/
|
|
@Deprecated
|
|
void recycle() {}
|
|
|
|
private void clear() {
|
|
mRowCount = 0;
|
|
mColumnCount = 0;
|
|
mHierarchical = false;
|
|
mSelectionMode = SELECTION_MODE_NONE;
|
|
mItemCount = UNDEFINED;
|
|
mImportantForAccessibilityItemCount = UNDEFINED;
|
|
}
|
|
|
|
/**
|
|
* The builder for CollectionInfo.
|
|
*/
|
|
|
|
@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
|
|
public static final class Builder {
|
|
private int mRowCount = 0;
|
|
private int mColumnCount = 0;
|
|
private boolean mHierarchical = false;
|
|
private int mSelectionMode;
|
|
private int mItemCount = UNDEFINED;
|
|
private int mImportantForAccessibilityItemCount = UNDEFINED;
|
|
|
|
/**
|
|
* Creates a new Builder.
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
|
|
public Builder() {
|
|
}
|
|
|
|
/**
|
|
* Sets the row count.
|
|
* @param rowCount The number of rows in the collection.
|
|
* @return This builder.
|
|
*/
|
|
@NonNull
|
|
@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
|
|
public CollectionInfo.Builder setRowCount(int rowCount) {
|
|
mRowCount = rowCount;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the column count.
|
|
* @param columnCount The number of columns in the collection.
|
|
* @return This builder.
|
|
*/
|
|
@NonNull
|
|
@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
|
|
public CollectionInfo.Builder setColumnCount(int columnCount) {
|
|
mColumnCount = columnCount;
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets whether the collection is hierarchical.
|
|
* @param hierarchical Whether the collection is hierarchical.
|
|
* @return This builder.
|
|
*/
|
|
@NonNull
|
|
@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
|
|
public CollectionInfo.Builder setHierarchical(boolean hierarchical) {
|
|
mHierarchical = hierarchical;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the selection mode.
|
|
* @param selectionMode The selection mode.
|
|
* @return This builder.
|
|
*/
|
|
@NonNull
|
|
@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
|
|
public CollectionInfo.Builder setSelectionMode(int selectionMode) {
|
|
mSelectionMode = selectionMode;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the number of items in the collection. Can be optionally set for ViewGroups with
|
|
* clear row and column semantics; should be set for all other clients.
|
|
*
|
|
* @param itemCount The number of items in the collection. This should be set to
|
|
* {@code UNDEFINED} if the item count is not known.
|
|
* @return This builder.
|
|
*/
|
|
@NonNull
|
|
@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
|
|
public CollectionInfo.Builder setItemCount(int itemCount) {
|
|
mItemCount = itemCount;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the number of views considered important for accessibility.
|
|
* @param importantForAccessibilityItemCount The number of items important for
|
|
* accessibility.
|
|
* @return This builder.
|
|
*/
|
|
@NonNull
|
|
@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
|
|
public CollectionInfo.Builder setImportantForAccessibilityItemCount(
|
|
int importantForAccessibilityItemCount) {
|
|
mImportantForAccessibilityItemCount = importantForAccessibilityItemCount;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Creates a new {@link CollectionInfo} instance.
|
|
*/
|
|
@NonNull
|
|
@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS)
|
|
public CollectionInfo build() {
|
|
CollectionInfo collectionInfo = new CollectionInfo(mRowCount, mColumnCount,
|
|
mHierarchical);
|
|
collectionInfo.mSelectionMode = mSelectionMode;
|
|
collectionInfo.mItemCount = mItemCount;
|
|
collectionInfo.mImportantForAccessibilityItemCount =
|
|
mImportantForAccessibilityItemCount;
|
|
return collectionInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Class with information if a node is a collection item.
|
|
* <p>
|
|
* A collection item is contained in a collection, it starts at
|
|
* a given row and column in the collection, and spans one or
|
|
* more rows and columns. For example, a header of two related
|
|
* table columns starts at the first row and the first column,
|
|
* spans one row and two columns.
|
|
* </p>
|
|
*/
|
|
public static final class CollectionItemInfo {
|
|
/**
|
|
* Instantiates a CollectionItemInfo that is a clone of another one.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Create a new instance using the
|
|
* constructor {@link
|
|
* AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo}
|
|
* instead.
|
|
*
|
|
* @param other The instance to clone.
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
public static CollectionItemInfo obtain(CollectionItemInfo other) {
|
|
return new CollectionItemInfo(other.mRowTitle, other.mRowIndex, other.mRowSpan,
|
|
other.mColumnTitle, other.mColumnIndex, other.mColumnSpan, other.mHeading,
|
|
other.mSelected);
|
|
}
|
|
|
|
/**
|
|
* Instantiates a new CollectionItemInfo.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Create a new instance using the
|
|
* constructor {@link
|
|
* AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo(int,
|
|
* int, int, int, boolean)} instead.
|
|
* @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)}).
|
|
*/
|
|
@Deprecated
|
|
public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
|
|
int columnIndex, int columnSpan, boolean heading) {
|
|
return new CollectionItemInfo(rowIndex, rowSpan, columnIndex, columnSpan, heading,
|
|
false);
|
|
}
|
|
|
|
/**
|
|
* Instantiates a new CollectionItemInfo.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Create a new instance using the
|
|
* constructor {@link
|
|
* AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo(int,
|
|
* int, int, int, boolean)} instead.
|
|
* @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.
|
|
*/
|
|
@Deprecated
|
|
public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
|
|
int columnIndex, int columnSpan, boolean heading, boolean selected) {
|
|
return new CollectionItemInfo(rowIndex, rowSpan, columnIndex, columnSpan, heading,
|
|
selected);
|
|
}
|
|
|
|
/**
|
|
* Instantiates a new CollectionItemInfo.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Creates a new instance using the
|
|
* constructor {@link
|
|
* AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo(int,
|
|
* int, int, int, boolean, boolean)} instead.
|
|
*
|
|
* @param rowTitle The row title at which the item is located.
|
|
* @param rowIndex The row index at which the item is located.
|
|
* @param rowSpan The number of rows the item spans.
|
|
* @param columnTitle The column title at which the item is located.
|
|
* @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.
|
|
* @removed
|
|
*/
|
|
@Deprecated
|
|
@NonNull
|
|
public static CollectionItemInfo obtain(@Nullable String rowTitle, int rowIndex,
|
|
int rowSpan, @Nullable String columnTitle, int columnIndex, int columnSpan,
|
|
boolean heading, boolean selected) {
|
|
return new CollectionItemInfo(rowTitle, rowIndex, rowSpan, columnTitle, columnIndex,
|
|
columnSpan, heading, selected);
|
|
}
|
|
|
|
private boolean mHeading;
|
|
private int mColumnIndex;
|
|
private int mRowIndex;
|
|
private int mColumnSpan;
|
|
private int mRowSpan;
|
|
private boolean mSelected;
|
|
private String mRowTitle;
|
|
private String mColumnTitle;
|
|
|
|
private CollectionItemInfo() {
|
|
/* do nothing */
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
public CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan,
|
|
boolean heading) {
|
|
this(rowIndex, rowSpan, columnIndex, columnSpan, heading, false);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
* @param selected Whether the item is selected.
|
|
*/
|
|
public CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan,
|
|
boolean heading, boolean selected) {
|
|
this(null, rowIndex, rowSpan, null, columnIndex, columnSpan,
|
|
heading, selected);
|
|
}
|
|
|
|
/**
|
|
* Creates a new instance.
|
|
*
|
|
* @param rowTitle The row title at which the item is located.
|
|
* @param rowIndex The row index at which the item is located.
|
|
* @param rowSpan The number of rows the item spans.
|
|
* @param columnTitle The column title at which the item is located.
|
|
* @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.
|
|
* @param selected Whether the item is selected.
|
|
* @hide
|
|
*/
|
|
public CollectionItemInfo(@Nullable String rowTitle, int rowIndex, int rowSpan,
|
|
@Nullable String columnTitle, int columnIndex, int columnSpan, boolean heading,
|
|
boolean selected) {
|
|
mRowIndex = rowIndex;
|
|
mRowSpan = rowSpan;
|
|
mColumnIndex = columnIndex;
|
|
mColumnSpan = columnSpan;
|
|
mHeading = heading;
|
|
mSelected = selected;
|
|
mRowTitle = rowTitle;
|
|
mColumnTitle = columnTitle;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* Gets the row title at which the item is located.
|
|
*
|
|
* @return The row title.
|
|
*/
|
|
@Nullable
|
|
public String getRowTitle() {
|
|
return mRowTitle;
|
|
}
|
|
|
|
/**
|
|
* Gets the column title at which the item is located.
|
|
*
|
|
* @return The column title.
|
|
*/
|
|
@Nullable
|
|
public String getColumnTitle() {
|
|
return mColumnTitle;
|
|
}
|
|
|
|
/**
|
|
* Recycles this instance.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Calling this function now will have
|
|
* no effect.
|
|
*/
|
|
@Deprecated
|
|
void recycle() {}
|
|
|
|
private void clear() {
|
|
mColumnIndex = 0;
|
|
mColumnSpan = 0;
|
|
mRowIndex = 0;
|
|
mRowSpan = 0;
|
|
mHeading = false;
|
|
mSelected = false;
|
|
mRowTitle = null;
|
|
mColumnTitle = null;
|
|
}
|
|
|
|
/**
|
|
* Builder for creating {@link CollectionItemInfo} objects.
|
|
*/
|
|
public static final class Builder {
|
|
private boolean mHeading;
|
|
private int mColumnIndex;
|
|
private int mRowIndex;
|
|
private int mColumnSpan;
|
|
private int mRowSpan;
|
|
private boolean mSelected;
|
|
private String mRowTitle;
|
|
private String mColumnTitle;
|
|
|
|
/**
|
|
* Creates a new Builder.
|
|
*/
|
|
public Builder() {
|
|
}
|
|
|
|
/**
|
|
* Sets the collection item is a heading.
|
|
*
|
|
* @param heading The heading state
|
|
* @return This builder
|
|
*/
|
|
@NonNull
|
|
public CollectionItemInfo.Builder setHeading(boolean heading) {
|
|
mHeading = heading;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the column index at which the item is located.
|
|
*
|
|
* @param columnIndex The column index
|
|
* @return This builder
|
|
*/
|
|
@NonNull
|
|
public CollectionItemInfo.Builder setColumnIndex(int columnIndex) {
|
|
mColumnIndex = columnIndex;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the row index at which the item is located.
|
|
*
|
|
* @param rowIndex The row index
|
|
* @return This builder
|
|
*/
|
|
@NonNull
|
|
public CollectionItemInfo.Builder setRowIndex(int rowIndex) {
|
|
mRowIndex = rowIndex;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the number of columns the item spans.
|
|
*
|
|
* @param columnSpan The number of columns spans
|
|
* @return This builder
|
|
*/
|
|
@NonNull
|
|
public CollectionItemInfo.Builder setColumnSpan(int columnSpan) {
|
|
mColumnSpan = columnSpan;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the number of rows the item spans.
|
|
*
|
|
* @param rowSpan The number of rows spans
|
|
* @return This builder
|
|
*/
|
|
@NonNull
|
|
public CollectionItemInfo.Builder setRowSpan(int rowSpan) {
|
|
mRowSpan = rowSpan;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the collection item is selected.
|
|
*
|
|
* @param selected The number of rows spans
|
|
* @return This builder
|
|
*/
|
|
@NonNull
|
|
public CollectionItemInfo.Builder setSelected(boolean selected) {
|
|
mSelected = selected;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the row title at which the item is located.
|
|
*
|
|
* @param rowTitle The row title
|
|
* @return This builder
|
|
*/
|
|
@NonNull
|
|
public CollectionItemInfo.Builder setRowTitle(@Nullable String rowTitle) {
|
|
mRowTitle = rowTitle;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the column title at which the item is located.
|
|
*
|
|
* @param columnTitle The column title
|
|
* @return This builder
|
|
*/
|
|
@NonNull
|
|
public CollectionItemInfo.Builder setColumnTitle(@Nullable String columnTitle) {
|
|
mColumnTitle = columnTitle;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Builds and returns a {@link CollectionItemInfo}.
|
|
*/
|
|
@NonNull
|
|
public CollectionItemInfo build() {
|
|
CollectionItemInfo collectionItemInfo = new CollectionItemInfo();
|
|
collectionItemInfo.mHeading = mHeading;
|
|
collectionItemInfo.mColumnIndex = mColumnIndex;
|
|
collectionItemInfo.mRowIndex = mRowIndex;
|
|
collectionItemInfo.mColumnSpan = mColumnSpan;
|
|
collectionItemInfo.mRowSpan = mRowSpan;
|
|
collectionItemInfo.mSelected = mSelected;
|
|
collectionItemInfo.mRowTitle = mRowTitle;
|
|
collectionItemInfo.mColumnTitle = mColumnTitle;
|
|
|
|
return collectionItemInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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>
|
|
*
|
|
* @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 @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];
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Class with information of a view useful to evaluate accessibility needs. Developers can
|
|
* refresh the node with the key {@link #EXTRA_DATA_RENDERING_INFO_KEY} to fetch the text size
|
|
* and unit if it is {@link TextView} and the height and the width of layout params from
|
|
* {@link ViewGroup} or {@link TextView}.
|
|
*
|
|
* @see #EXTRA_DATA_RENDERING_INFO_KEY
|
|
* @see #refreshWithExtraData(String, Bundle)
|
|
*/
|
|
public static final class ExtraRenderingInfo {
|
|
private static final int UNDEFINED_VALUE = -1;
|
|
|
|
private Size mLayoutSize;
|
|
private float mTextSizeInPx = UNDEFINED_VALUE;
|
|
private int mTextSizeUnit = UNDEFINED_VALUE;
|
|
|
|
/**
|
|
* Instantiates an ExtraRenderingInfo, by copying an existing one.
|
|
*
|
|
* @hide
|
|
* @deprecated Object pooling has been discontinued. Create a new instance using the
|
|
* constructor {@link #ExtraRenderingInfo(ExtraRenderingInfo)} instead.
|
|
*/
|
|
@Deprecated
|
|
@NonNull
|
|
public static ExtraRenderingInfo obtain() {
|
|
return new ExtraRenderingInfo(null);
|
|
}
|
|
|
|
/**
|
|
* Instantiates an ExtraRenderingInfo, by copying an existing one.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Create a new instance using the
|
|
* constructor {@link #ExtraRenderingInfo(ExtraRenderingInfo)} instead.
|
|
* @param other
|
|
*/
|
|
@Deprecated
|
|
private static ExtraRenderingInfo obtain(ExtraRenderingInfo other) {
|
|
return new ExtraRenderingInfo(other);
|
|
}
|
|
|
|
/**
|
|
* Creates a new rendering info of a view, and this new instance is initialized from
|
|
* the given <code>other</code>.
|
|
*
|
|
* @param other The instance to clone.
|
|
*/
|
|
private ExtraRenderingInfo(@Nullable ExtraRenderingInfo other) {
|
|
if (other != null) {
|
|
mLayoutSize = other.mLayoutSize;
|
|
mTextSizeInPx = other.mTextSizeInPx;
|
|
mTextSizeUnit = other.mTextSizeUnit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the size object containing the height and the width of
|
|
* {@link android.view.ViewGroup.LayoutParams} if the node is a {@link ViewGroup} or
|
|
* a {@link TextView}, or null otherwise. Useful for some accessibility services to
|
|
* understand whether the text is scalable and fits the view or not.
|
|
*
|
|
* @return a {@link Size} stores layout height and layout width of the view, or null
|
|
* otherwise. And the size value may be in pixels,
|
|
* {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT},
|
|
* or {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
|
|
*/
|
|
public @Nullable Size getLayoutSize() {
|
|
return mLayoutSize;
|
|
}
|
|
|
|
/**
|
|
* Sets layout width and layout height of the view.
|
|
*
|
|
* @param width The layout width.
|
|
* @param height The layout height.
|
|
* @hide
|
|
*/
|
|
public void setLayoutSize(int width, int height) {
|
|
mLayoutSize = new Size(width, height);
|
|
}
|
|
|
|
/**
|
|
* Gets the text size if the node is a {@link TextView}, or -1 otherwise. Useful for some
|
|
* accessibility services to understand whether the text is scalable and fits the view or
|
|
* not.
|
|
*
|
|
* @return the text size of a {@code TextView}, or -1 otherwise.
|
|
*/
|
|
public float getTextSizeInPx() {
|
|
return mTextSizeInPx;
|
|
}
|
|
|
|
/**
|
|
* Sets text size of the view.
|
|
*
|
|
* @param textSizeInPx The text size in pixels.
|
|
* @hide
|
|
*/
|
|
public void setTextSizeInPx(float textSizeInPx) {
|
|
mTextSizeInPx = textSizeInPx;
|
|
}
|
|
|
|
/**
|
|
* Gets the text size unit if the node is a {@link TextView}, or -1 otherwise.
|
|
* Text size returned from {@link #getTextSizeInPx} in raw pixels may scale by factors and
|
|
* convert from other units. Useful for some accessibility services to understand whether
|
|
* the text is scalable and fits the view or not.
|
|
*
|
|
* @return the text size unit which type is {@link TypedValue#TYPE_DIMENSION} of a
|
|
* {@code TextView}, or -1 otherwise.
|
|
*
|
|
* @see TypedValue#TYPE_DIMENSION
|
|
*/
|
|
public int getTextSizeUnit() {
|
|
return mTextSizeUnit;
|
|
}
|
|
|
|
/**
|
|
* Sets text size unit of the view.
|
|
*
|
|
* @param textSizeUnit The text size unit.
|
|
* @hide
|
|
*/
|
|
public void setTextSizeUnit(int textSizeUnit) {
|
|
mTextSizeUnit = textSizeUnit;
|
|
}
|
|
|
|
/**
|
|
* Previously would recycle this instance.
|
|
*
|
|
* @deprecated Object pooling has been discontinued. Calling this function now will have
|
|
* no effect.
|
|
*/
|
|
@Deprecated
|
|
void recycle() {}
|
|
|
|
private void clear() {
|
|
mLayoutSize = null;
|
|
mTextSizeInPx = UNDEFINED_VALUE;
|
|
mTextSizeUnit = UNDEFINED_VALUE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see android.os.Parcelable.Creator
|
|
*/
|
|
public static final @NonNull Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
|
|
new Parcelable.Creator<AccessibilityNodeInfo>() {
|
|
@Override
|
|
public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
|
|
AccessibilityNodeInfo info = new AccessibilityNodeInfo();
|
|
info.initFromParcel(parcel);
|
|
return info;
|
|
}
|
|
|
|
@Override
|
|
public AccessibilityNodeInfo[] newArray(int size) {
|
|
return new AccessibilityNodeInfo[size];
|
|
}
|
|
};
|
|
}
|