/* * 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 android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.Nullable; import android.graphics.Rect; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.InputType; import android.text.TextUtils; import android.util.ArraySet; import android.util.LongArray; import android.util.Pools.SynchronizedPool; import android.view.View; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * 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 content is * presented as tree of accessibility node info 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. *
** Once an accessibility node info is delivered to an accessibility service it is * made immutable and calling a state mutation method generates an error. *
** 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 familiarizing with the security model. *
*For more information about making applications accessible, read the * Accessibility * developer guide.
*
* Arguments: {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
* {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}
* 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);
*
* Example: Move to the previous character and do not extend selection.
*
* Arguments: {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
* {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}
* 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);
*
* Example: Move to the next character and do not extend selection.
*
* Arguments: {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}
* Bundle arguments = new Bundle();
* arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
* info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
*
* Example:
*
* Arguments: {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}
* Bundle arguments = new Bundle();
* arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
* info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
*
* Example:
*
* Arguments: {@link #ACTION_ARGUMENT_SELECTION_START_INT},
* {@link #ACTION_ARGUMENT_SELECTION_END_INT}
* Bundle arguments = new Bundle();
* arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1);
* arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2);
* info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments);
*
* Example:
*
* null
or empty {@link CharSequence} will clear the text. This action will also put the
* cursor at the end of text.
*
* Arguments: {@link #ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}
* Bundle arguments = new Bundle();
* arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
* "android");
* info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
*
* Example:
*
* Type: int
* Actions: {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
* {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
*
* Type: String
* Actions: {@link #ACTION_NEXT_HTML_ELEMENT},
* {@link #ACTION_PREVIOUS_HTML_ELEMENT}
*
* Type: boolean
* Actions: {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
* {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
*
* Type: int
* Actions: {@link #ACTION_SET_SELECTION}
*
* Type: int
* Actions: {@link #ACTION_SET_SELECTION}
*
* Type: CharSequence
* Actions: {@link #ACTION_SET_TEXT}
*
virtualDescendantId
* by {@link #VIRTUAL_DESCENDANT_ID_SHIFT} and taking
* the bitwise or with the accessibilityViewId
.
*
* @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) {
// We changed the value for undefined node to positive due to wrong
// global id composition (two 32-bin ints into one 64-bit long) but
// the value used for the host node provider view has id -1 so we
// remap it here.
if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
virtualDescendantId = UNDEFINED_ITEM_ID;
}
return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
}
// Housekeeping.
private static final int MAX_POOL_SIZE = 50;
private static final SynchronizedPool* Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param source The info source. */ public void setSource(View source) { setSource(source, UNDEFINED_ITEM_ID); } /** * Sets the source to be a virtual descendant of the givenroot
.
* If virtualDescendantId
is {@link View#NO_ID} the root
* is set as the source.
* * A virtual descendant is an imaginary View that is reported as a part of the view * hierarchy for accessibility purposes. This enables custom views that draw complex * content to report themselves as a tree of virtual views, thus conveying their * logical structure. *
** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param root The root of the virtual subtree. * @param virtualDescendantId The id of the virtual descendant. */ public void setSource(View root, int virtualDescendantId) { enforceNotSealed(); mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED_ITEM_ID; final int rootAccessibilityViewId = (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); } /** * Find the view that has the specified focus type. The search starts from * the view represented by this node info. * * @param focus The focus to find. One of {@link #FOCUS_INPUT} or * {@link #FOCUS_ACCESSIBILITY}. * @return The node info of the focused view or null. * * @see #FOCUS_INPUT * @see #FOCUS_ACCESSIBILITY */ public AccessibilityNodeInfo findFocus(int focus) { enforceSealed(); enforceValidFocusType(focus); if (!canPerformRequestOverConnection(mSourceNodeId)) { return null; } return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId, mSourceNodeId, focus); } /** * Searches for the nearest view in the specified direction that can take * the input focus. * * @param direction The direction. Can be one of: * {@link View#FOCUS_DOWN}, * {@link View#FOCUS_UP}, * {@link View#FOCUS_LEFT}, * {@link View#FOCUS_RIGHT}, * {@link View#FOCUS_FORWARD}, * {@link View#FOCUS_BACKWARD}. * * @return The node info for the view that can take accessibility focus. */ public AccessibilityNodeInfo focusSearch(int direction) { enforceSealed(); enforceValidFocusDirection(direction); if (!canPerformRequestOverConnection(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. ** Note: If this method returns false this info is obsolete * since it represents a view that is no longer in the view tree and should * be recycled. *
* * @param bypassCache Whether to bypass the cache. * @return Whether the refresh succeeded. * * @hide */ public boolean refresh(boolean bypassCache) { enforceSealed(); if (!canPerformRequestOverConnection(mSourceNodeId)) { return false; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); AccessibilityNodeInfo refreshedInfo = client.findAccessibilityNodeInfoByAccessibilityId( mConnectionId, mWindowId, mSourceNodeId, bypassCache, 0); if (refreshedInfo == null) { return false; } init(refreshedInfo); refreshedInfo.recycle(); return true; } /** * Refreshes this info with the latest state of the view it represents. ** Note: If this method returns false this info is obsolete * since it represents a view that is no longer in the view tree and should * be recycled. *
* @return Whether the refresh succeeded. */ public boolean refresh() { return refresh(false); } /** * 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. ** Note: It is a client responsibility to recycle the * received info by calling {@link AccessibilityNodeInfo#recycle()} * to avoid creating of multiple instances. *
* * @param index The child index. * @return The child node. * * @throws IllegalStateException If called outside of an AccessibilityService. * */ public AccessibilityNodeInfo getChild(int index) { enforceSealed(); if (mChildNodeIds == null) { return null; } if (!canPerformRequestOverConnection(mSourceNodeId)) { return null; } final long childId = mChildNodeIds.get(index); AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId, childId, false, FLAG_PREFETCH_DESCENDANTS); } /** * Adds a child. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param child The child. * * @throws IllegalStateException If called from an AccessibilityService. */ public void addChild(View child) { addChildInternal(child, UNDEFINED_ITEM_ID, true); } /** * Unchecked version of {@link #addChild(View)} that does not verify * uniqueness. For framework use only. * * @hide */ public void addChildUnchecked(View child) { addChildInternal(child, UNDEFINED_ITEM_ID, false); } /** * Removes a child. If the child was not previously added to the node, * calling this method has no effect. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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, UNDEFINED_ITEM_ID); } /** * Adds a virtual child which is a descendant of the givenroot
.
* If virtualDescendantId
is {@link View#NO_ID} the root
* is added as a child.
* * A virtual descendant is an imaginary View that is reported as a part of the view * hierarchy for accessibility purposes. This enables custom views that draw complex * content to report them selves as a tree of virtual views, thus conveying their * logical structure. *
* * @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 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 *root
. 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* 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. *
** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param action The action. * * @throws IllegalStateException If called from an AccessibilityService. */ public void addAction(AccessibilityAction action) { enforceNotSealed(); if (action == null) { return; } if (mActions == null) { mActions = new ArrayList* Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param action The action. * * @throws IllegalStateException If called from an AccessibilityService. * @throws IllegalArgumentException If the argument is not one of the standard actions. * * @deprecated This has been deprecated for {@link #addAction(AccessibilityAction)} */ @Deprecated public void addAction(int action) { enforceNotSealed(); if ((action & ACTION_TYPE_MASK) != 0) { throw new IllegalArgumentException("Action is not a combination of the standard " + "actions: " + action); } addLegacyStandardActions(action); } /** * Removes an action that can be performed on the node. If the action was * not already added to the node, calling this method has no effect. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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); } /** * 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 ornull
.
*
* @see #setTraversalBefore(android.view.View)
* @see #setTraversalBefore(android.view.View, int)
*/
public AccessibilityNodeInfo getTraversalBefore() {
enforceSealed();
return getNodeForAccessibilityId(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.
* * Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param view The view providing the preceding node. * * @see #getTraversalBefore() */ public void setTraversalBefore(View view) { setTraversalBefore(view, UNDEFINED_ITEM_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 givenroot
. If
* virtualDescendantId
equals to {@link View#NO_ID} the root is set
* as the successor.
* * A virtual descendant is an imaginary View that is reported as a part of the view * hierarchy for accessibility purposes. This enables custom views that draw complex * content to report them selves as a tree of virtual views, thus conveying their * logical structure. *
** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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 ornull
.
*
* @see #setTraversalAfter(android.view.View)
* @see #setTraversalAfter(android.view.View, int)
*/
public AccessibilityNodeInfo getTraversalAfter() {
enforceSealed();
return getNodeForAccessibilityId(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.
* * Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param view The previous view. * * @see #getTraversalAfter() */ public void setTraversalAfter(View view) { setTraversalAfter(view, UNDEFINED_ITEM_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. IfvirtualDescendantId
equals to {@link View#NO_ID}
* the root is set as the predecessor.
* * A virtual descendant is an imaginary View that is reported as a part of the view * hierarchy for accessibility purposes. This enables custom views that draw complex * content to report them selves as a tree of virtual views, thus conveying their * logical structure. *
** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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); } /** * Sets the maximum text length, or -1 for no limit. ** Typically used to indicate that an editable text field has a limit on * the number of characters entered. *
* Note: Cannot be called from an * {@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. *
* Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param granularities The bit mask with granularities. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setMovementGranularities(int granularities) { enforceNotSealed(); mMovementGranularities = granularities; } /** * Gets the movement granularities for traversing the text of this node. * * @return The bit mask with granularities. */ public int getMovementGranularities() { return mMovementGranularities; } /** * Performs an action on the node. ** Note: An action can be performed only if the request is made * from an {@link android.accessibilityservice.AccessibilityService}. *
* * @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(mSourceNodeId)) { return false; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, action, null); } /** * Performs an action on the node. ** Note: An action can be performed only if the request is made * from an {@link android.accessibilityservice.AccessibilityService}. *
* * @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(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. * ** Note: It is a client responsibility to recycle the * received info by calling {@link AccessibilityNodeInfo#recycle()} * to avoid creating of multiple instances. *
* * @param text The searched text. * @return A list of node info. */ public List* Note: It is a client responsibility to recycle the * received info by calling {@link AccessibilityNodeInfo#recycle()} * to avoid creating of multiple instances. *
** Note: The primary usage of this API is for UI test automation * and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo} * the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} * flag when configuring his {@link android.accessibilityservice.AccessibilityService}. *
* * @param viewId The fully qualified resource name of the view id to find. * @return A list of node info. */ public List* Note: It is a client responsibility to recycle the * received info by calling {@link AccessibilityNodeInfo#recycle()} * to avoid creating of multiple instances. *
* * @return The parent. */ public AccessibilityNodeInfo getParent() { enforceSealed(); return getNodeForAccessibilityId(mParentNodeId); } /** * @return The parent node id. * * @hide */ public long getParentNodeId() { return mParentNodeId; } /** * Sets the parent. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param parent The parent. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setParent(View parent) { setParent(parent, UNDEFINED_ITEM_ID); } /** * Sets the parent to be a virtual descendant of the givenroot
.
* If virtualDescendantId
equals to {@link View#NO_ID} the root
* is set as the parent.
* * A virtual descendant is an imaginary View that is reported as a part of the view * hierarchy for accessibility purposes. This enables custom views that draw complex * content to report them selves as a tree of virtual views, thus conveying their * logical structure. *
** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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 parent coordinates. * * @param outBounds The output node bounds. */ public void getBoundsInParent(Rect outBounds) { outBounds.set(mBoundsInParent.left, mBoundsInParent.top, mBoundsInParent.right, mBoundsInParent.bottom); } /** * Sets the node bounds in parent coordinates. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param bounds The node bounds. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setBoundsInParent(Rect bounds) { enforceNotSealed(); mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom); } /** * Gets the node bounds in screen coordinates. * * @param outBounds The output node bounds. */ public void getBoundsInScreen(Rect outBounds) { outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top, mBoundsInScreen.right, mBoundsInScreen.bottom); } /** * Sets the node bounds in screen coordinates. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param bounds The node bounds. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setBoundsInScreen(Rect bounds) { enforceNotSealed(); mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom); } /** * Gets whether this node is checkable. * * @return True if the node is checkable. */ public boolean isCheckable() { return getBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE); } /** * Sets whether this node is checkable. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param checked True if the node is checked. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setChecked(boolean checked) { setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked); } /** * Gets whether this node is focusable. * * @return True if the node is focusable. */ public boolean isFocusable() { return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE); } /** * Sets whether this node is focusable. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param focusable True if the node is focusable. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setFocusable(boolean focusable) { setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable); } /** * Gets whether this node is focused. * * @return True if the node is focused. */ public boolean isFocused() { return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED); } /** * Sets whether this node is focused. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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); } /** * Sets whether this node is visible to the user. * * @return Whether the node is visible to the user. */ public boolean isVisibleToUser() { return getBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER); } /** * Sets whether this node is visible to the user. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param visibleToUser Whether the node is visible to the user. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setVisibleToUser(boolean visibleToUser) { setBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER, visibleToUser); } /** * Gets whether this node is accessibility focused. * * @return True if the node is accessibility focused. */ public boolean isAccessibilityFocused() { return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED); } /** * Sets whether this node is accessibility focused. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param scrollable True if the node is scrollable, false otherwise. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setScrollable(boolean scrollable) { setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable); } /** * Gets if the node is editable. * * @return True if the node is editable, false otherwise. */ public boolean isEditable() { return getBooleanProperty(BOOLEAN_PROPERTY_EDITABLE); } /** * Sets whether this node is editable. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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 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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
*/ 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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param rangeInfo The range info. */ public void setRangeInfo(RangeInfo rangeInfo) { enforceNotSealed(); mRangeInfo = rangeInfo; } /** * Gets if the content of this node is invalid. For example, * a date is not well-formed. * * @return If the node content is invalid. */ public boolean isContentInvalid() { return getBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID); } /** * Sets if the content of this node is invalid. For example, * a date is not well-formed. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param contentInvalid If the node content is invalid. */ public void setContentInvalid(boolean contentInvalid) { setBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID, contentInvalid); } /** * Gets the node's live region mode. ** A live region is a node that contains information that is important for * the user and when it changes the user should be notified. For example, * in a login screen with a TextView that displays an "incorrect password" * notification, that view should be marked as a live region with mode * {@link View#ACCESSIBILITY_LIVE_REGION_POLITE}. *
* 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. *
* Note: 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. *
* Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param dismissable If the node can be dismissed. */ public void setDismissable(boolean dismissable) { setBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE, dismissable); } /** * Gets the package this node comes from. * * @return The package name. */ public CharSequence getPackageName() { return mPackageName; } /** * Sets the package this node comes from. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. * * @return The text. */ public CharSequence getText() { return mText; } /** * Sets the text of this node. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param text The text. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setText(CharSequence text) { enforceNotSealed(); mText = text; } /** * Sets the error text of this node. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param error The error text. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setError(CharSequence error) { enforceNotSealed(); mError = error; } /** * Gets the error text of this node. * * @return The error text. */ public CharSequence getError() { return mError; } /** * Gets the content description of this node. * * @return The content description. */ public CharSequence getContentDescription() { return mContentDescription; } /** * Sets the content description of this node. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @param contentDescription The content description. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setContentDescription(CharSequence contentDescription) { enforceNotSealed(); mContentDescription = contentDescription; } /** * 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, UNDEFINED_ITEM_ID); } /** * Sets the view for which the view represented by this info serves as a * label for accessibility purposes. IfvirtualDescendantId
* is {@link View#NO_ID} the root is set as the labeled.
* * A virtual descendant is an imaginary View that is reported as a part of the view * hierarchy for accessibility purposes. This enables custom views that draw complex * content to report themselves as a tree of virtual views, thus conveying their * logical structure. *
** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: It is a client responsibility to recycle the * received info by calling {@link AccessibilityNodeInfo#recycle()} * to avoid creating of multiple instances. *
* * @return The labeled info. */ public AccessibilityNodeInfo getLabelFor() { enforceSealed(); return getNodeForAccessibilityId(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, UNDEFINED_ITEM_ID); } /** * Sets the view which serves as the label of the view represented by * this info for accessibility purposes. IfvirtualDescendantId
* is {@link View#NO_ID} the root is set as the label.
* * A virtual descendant is an imaginary View that is reported as a part of the view * hierarchy for accessibility purposes. This enables custom views that draw complex * content to report themselves as a tree of virtual views, thus conveying their * logical structure. *
** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. ** Note: It is a client responsibility to recycle the * received info by calling {@link AccessibilityNodeInfo#recycle()} * to avoid creating of multiple instances. *
* * @return The label. */ public AccessibilityNodeInfo getLabeledBy() { enforceSealed(); return getNodeForAccessibilityId(mLabeledById); } /** * Sets the fully qualified resource name of the source view's id. * ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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. * ** Note: The primary usage of this API is for UI test automation * and in order to report the source view id of an {@link AccessibilityNodeInfo} the * client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} * flag when configuring his {@link android.accessibilityservice.AccessibilityService}. *
* @return The id resource name. */ public String getViewIdResourceName() { return mViewIdResourceName; } /** * Gets the text selection start. * * @return The text selection start if there is selection or -1. */ public int getTextSelectionStart() { return mTextSelectionStart; } /** * Gets the text selection end. * * @return The text selection end if there is selection or -1. */ public int getTextSelectionEnd() { return mTextSelectionEnd; } /** * Sets the text selection start and end. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. *
* * @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}. ** Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an * AccessibilityService. *
* * @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 nevernull
.
* * Note: It is recommended to use the package * name of your application as a prefix for the keys to avoid * collisions which may confuse an accessibility service if the * same key has different meaning when emitted from different * applications. *
* * @return The bundle. */ public Bundle getExtras() { if (mExtras == null) { mExtras = new Bundle(); } return mExtras; } /** * 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; } /** * {@inheritDoc} */ @Override public int describeContents() { return 0; } /** * Gets the id of the source node. * * @return The id. * * @hide */ public long getSourceNodeId() { return mSourceNodeId; } /** * Sets if this instance is sealed. * * @param sealed Whether is sealed. * * @hide */ public void setSealed(boolean sealed) { mSealed = sealed; } /** * Gets if this instance is sealed. * * @return Whether is sealed. * * @hide */ public boolean isSealed() { return mSealed; } /** * Enforces that this instance is sealed. * * @throws IllegalStateException If this instance is not sealed. * * @hide */ protected void enforceSealed() { if (!isSealed()) { throw new IllegalStateException("Cannot perform this " + "action on a not sealed instance."); } } private void enforceValidFocusDirection(int direction) { switch (direction) { case View.FOCUS_DOWN: case View.FOCUS_UP: case View.FOCUS_LEFT: case View.FOCUS_RIGHT: case View.FOCUS_FORWARD: case View.FOCUS_BACKWARD: return; default: throw new IllegalArgumentException("Unknown direction: " + direction); } } private void enforceValidFocusType(int focusType) { switch (focusType) { case FOCUS_INPUT: case FOCUS_ACCESSIBILITY: return; default: throw new IllegalArgumentException("Unknown focus type: " + focusType); } } /** * Enforces that this instance is not sealed. * * @throws IllegalStateException If this instance is sealed. * * @hide */ protected void enforceNotSealed() { if (isSealed()) { throw new IllegalStateException("Cannot perform this " + "action on a sealed instance."); } } /** * Returns a cached instance if such is available otherwise a new one * and sets the source. * * @param source The source view. * @return An instance. * * @see #setSource(View) */ public static AccessibilityNodeInfo obtain(View source) { AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); info.setSource(source); return info; } /** * Returns a cached instance if such is available otherwise a new one * and sets the source. * * @param root The root of the virtual subtree. * @param virtualDescendantId The id of the virtual descendant. * @return An instance. * * @see #setSource(View, int) */ public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) { AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); info.setSource(root, virtualDescendantId); return info; } /** * Returns a cached instance if such is available otherwise a new one. * * @return An instance. */ public static AccessibilityNodeInfo obtain() { AccessibilityNodeInfo info = sPool.acquire(); return (info != null) ? info : new AccessibilityNodeInfo(); } /** * Returns a cached instance if such is available or a new one is * create. The returned instance is initialized from the given *info
.
*
* @param info The other info.
* @return An instance.
*/
public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain();
infoClone.init(info);
return infoClone;
}
/**
* Return an instance back to be reused.
* * Note: You must not touch the object after calling this function. * * @throws IllegalStateException If the info is already recycled. */ public void recycle() { clear(); sPool.release(this); } /** * {@inheritDoc} *
* Note: After the instance is written to a parcel it * is recycled. You must not touch the object after calling this function. *
*/ @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(isSealed() ? 1 : 0); parcel.writeLong(mSourceNodeId); parcel.writeInt(mWindowId); parcel.writeLong(mParentNodeId); parcel.writeLong(mLabelForId); parcel.writeLong(mLabeledById); parcel.writeLong(mTraversalBefore); parcel.writeLong(mTraversalAfter); parcel.writeInt(mConnectionId); 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)); } } parcel.writeInt(mBoundsInParent.top); parcel.writeInt(mBoundsInParent.bottom); parcel.writeInt(mBoundsInParent.left); parcel.writeInt(mBoundsInParent.right); parcel.writeInt(mBoundsInScreen.top); parcel.writeInt(mBoundsInScreen.bottom); parcel.writeInt(mBoundsInScreen.left); parcel.writeInt(mBoundsInScreen.right); if (mActions != null && !mActions.isEmpty()) { final int actionCount = mActions.size(); parcel.writeInt(actionCount); int defaultLegacyStandardActions = 0; for (int i = 0; i < actionCount; i++) { AccessibilityAction action = mActions.get(i); if (isDefaultLegacyStandardAction(action)) { defaultLegacyStandardActions |= action.getId(); } } parcel.writeInt(defaultLegacyStandardActions); for (int i = 0; i < actionCount; i++) { AccessibilityAction action = mActions.get(i); if (!isDefaultLegacyStandardAction(action)) { parcel.writeInt(action.getId()); parcel.writeCharSequence(action.getLabel()); } } } else { parcel.writeInt(0); } parcel.writeInt(mMaxTextLength); parcel.writeInt(mMovementGranularities); parcel.writeInt(mBooleanProperties); parcel.writeCharSequence(mPackageName); parcel.writeCharSequence(mClassName); parcel.writeCharSequence(mText); parcel.writeCharSequence(mError); parcel.writeCharSequence(mContentDescription); parcel.writeString(mViewIdResourceName); parcel.writeInt(mTextSelectionStart); parcel.writeInt(mTextSelectionEnd); parcel.writeInt(mInputType); parcel.writeInt(mLiveRegion); if (mExtras != null) { parcel.writeInt(1); parcel.writeBundle(mExtras); } else { parcel.writeInt(0); } if (mRangeInfo != null) { parcel.writeInt(1); parcel.writeInt(mRangeInfo.getType()); parcel.writeFloat(mRangeInfo.getMin()); parcel.writeFloat(mRangeInfo.getMax()); parcel.writeFloat(mRangeInfo.getCurrent()); } else { parcel.writeInt(0); } if (mCollectionInfo != null) { parcel.writeInt(1); parcel.writeInt(mCollectionInfo.getRowCount()); parcel.writeInt(mCollectionInfo.getColumnCount()); parcel.writeInt(mCollectionInfo.isHierarchical() ? 1 : 0); parcel.writeInt(mCollectionInfo.getSelectionMode()); } else { parcel.writeInt(0); } if (mCollectionItemInfo != null) { parcel.writeInt(1); parcel.writeInt(mCollectionItemInfo.getColumnIndex()); parcel.writeInt(mCollectionItemInfo.getColumnSpan()); parcel.writeInt(mCollectionItemInfo.getRowIndex()); parcel.writeInt(mCollectionItemInfo.getRowSpan()); parcel.writeInt(mCollectionItemInfo.isHeading() ? 1 : 0); parcel.writeInt(mCollectionItemInfo.isSelected() ? 1 : 0); } else { parcel.writeInt(0); } // Since instances of this class are fetched via synchronous i.e. blocking // calls in IPCs we always recycle as soon as the instance is marshaled. recycle(); } /** * Initializes this instance from another one. * * @param other The other instance. */ private void init(AccessibilityNodeInfo other) { mSealed = other.mSealed; mSourceNodeId = other.mSourceNodeId; mParentNodeId = other.mParentNodeId; mLabelForId = other.mLabelForId; mLabeledById = other.mLabeledById; mTraversalBefore = other.mTraversalBefore; mTraversalAfter = other.mTraversalAfter; mWindowId = other.mWindowId; mConnectionId = other.mConnectionId; mBoundsInParent.set(other.mBoundsInParent); mBoundsInScreen.set(other.mBoundsInScreen); mPackageName = other.mPackageName; mClassName = other.mClassName; mText = other.mText; mError = other.mError; mContentDescription = other.mContentDescription; mViewIdResourceName = other.mViewIdResourceName; final ArrayList* There are three categories of actions: *
* Arguments:
* {@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}
* 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);
*
* Example: Move to the previous character and do not extend selection.
*
* Arguments:
* {@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}
* 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);
*
* Example: Move to the next character and do not extend selection.
*
* Arguments:
* {@link AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING
* AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING}
* Bundle arguments = new Bundle();
* arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
* info.performAction(AccessibilityAction.ACTION_NEXT_HTML_ELEMENT.getId(), arguments);
*
* Example:
*
* Arguments:
* {@link AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING
* AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING}
* Bundle arguments = new Bundle();
* arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
* info.performAction(AccessibilityAction.ACTION_PREVIOUS_HTML_ELEMENT.getId(), arguments);
*
* Example:
*
* Arguments:
* {@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}
* 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);
*
* Example:
*
null
or empty {@link CharSequence} will clear the text. This
* action will also put the cursor at the end of text.
*
* Arguments:
* {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE
* AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}
* Bundle arguments = new Bundle();
* arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
* "android");
* info.performAction(AccessibilityAction.ACTION_SET_TEXT.getId(), arguments);
*
* Example:
*
* AccessibilityAction action = new AccessibilityAction( * AccessibilityAction.ACTION_ACTION_CLICK, getLocalizedLabel()); * node.addAction(action); ** * @param actionId The id for this action. This should either be one of the * standard actions or a specific action for your app. In that case it is * required to use a resource identifier. * @param label The label for the new AccessibilityAction. */ public AccessibilityAction(int actionId, @Nullable CharSequence label) { if ((actionId & ACTION_TYPE_MASK) == 0 && Integer.bitCount(actionId) != 1) { throw new IllegalArgumentException("Invalid standard action id"); } mActionId = actionId; mLabel = label; } /** * Gets the id for this action. * * @return The action id. */ public int getId() { return mActionId; } /** * Gets the label for this action. Its purpose is to describe the * action to user. * * @return The label. */ public CharSequence getLabel() { return mLabel; } @Override public int hashCode() { return mActionId; } @Override public boolean equals(Object other) { if (other == null) { return false; } if (other == this) { return true; } if (getClass() != other.getClass()) { return false; } return mActionId == ((AccessibilityAction)other).mActionId; } @Override public String toString() { return "AccessibilityAction: " + getActionSymbolicName(mActionId) + " - " + mLabel; } } /** * Class with information if a node is a range. Use * {@link RangeInfo#obtain(int, float, float, float)} to get an instance. */ public static final class RangeInfo { private static final int MAX_POOL_SIZE = 10; /** Range type: integer. */ public static final int RANGE_TYPE_INT = 0; /** Range type: float. */ public static final int RANGE_TYPE_FLOAT = 1; /** Range type: percent with values from zero to one.*/ public static final int RANGE_TYPE_PERCENT = 2; private static final SynchronizedPool
* 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. *
*/ public static final class CollectionInfo { /** Selection mode where items are not selectable. */ public static final int SELECTION_MODE_NONE = 0; /** Selection mode where a single item may be selected. */ public static final int SELECTION_MODE_SINGLE = 1; /** Selection mode where multiple items may be selected. */ public static final int SELECTION_MODE_MULTIPLE = 2; private static final int MAX_POOL_SIZE = 20; private static final SynchronizedPool* 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. *
*/ public static final class CollectionItemInfo { private static final int MAX_POOL_SIZE = 20; private static final SynchronizedPool