Fields Summary |
---|
public static final int | INVALID_IDVirtual node identifier value for invalid nodes. |
private static final String | DEFAULT_CLASS_NAMEDefault class name used for virtual views. |
private static final android.graphics.Rect | INVALID_PARENT_BOUNDSDefault bounds used to determine if the client didn't set any. |
private android.graphics.Rect | mTempScreenRect |
private android.graphics.Rect | mTempParentRect |
private int[] | mTempGlobalRect |
private android.graphics.Rect | mTempVisibleRectLazily-created temporary data structure used to compute visibility. |
private android.util.IntArray | mTempArrayLazily-created temporary data structure used to obtain child IDs. |
private final android.view.accessibility.AccessibilityManager | mManagerSystem accessibility manager, used to check state and send events. |
private final android.view.View | mViewView whose internal structure is exposed through this helper. |
private final android.content.Context | mContextContext of the host view. |
private ExploreByTouchNodeProvider | mNodeProviderNode provider that handles creating nodes and performing actions. |
private int | mFocusedVirtualViewIdVirtual view id for the currently focused logical item. |
private int | mHoveredVirtualViewIdVirtual view id for the currently hovered logical item. |
Methods Summary |
---|
private boolean | clearAccessibilityFocus(int virtualViewId)Attempts to clear accessibility focus from a virtual view.
if (isAccessibilityFocused(virtualViewId)) {
mFocusedVirtualViewId = INVALID_ID;
mView.invalidate();
sendEventForVirtualView(virtualViewId,
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
return true;
}
return false;
|
private android.view.accessibility.AccessibilityEvent | createEvent(int virtualViewId, int eventType)Constructs and returns an {@link AccessibilityEvent} for the specified
virtual view id, which includes the host view ({@link View#NO_ID}).
switch (virtualViewId) {
case View.NO_ID:
return createEventForHost(eventType);
default:
return createEventForChild(virtualViewId, eventType);
}
|
private android.view.accessibility.AccessibilityEvent | createEventForChild(int virtualViewId, int eventType)Constructs and returns an {@link AccessibilityEvent} populated with
information about the specified item.
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
event.setEnabled(true);
event.setClassName(DEFAULT_CLASS_NAME);
// Allow the client to populate the event.
onPopulateEventForVirtualView(virtualViewId, event);
// Make sure the developer is following the rules.
if (event.getText().isEmpty() && (event.getContentDescription() == null)) {
throw new RuntimeException("Callbacks must add text or a content description in "
+ "populateEventForVirtualViewId()");
}
// Don't allow the client to override these properties.
event.setPackageName(mView.getContext().getPackageName());
event.setSource(mView, virtualViewId);
return event;
|
private android.view.accessibility.AccessibilityEvent | createEventForHost(int eventType)Constructs and returns an {@link AccessibilityEvent} for the host node.
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
onInitializeAccessibilityEvent(mView, event);
return event;
|
private android.view.accessibility.AccessibilityNodeInfo | createNode(int virtualViewId)Constructs and returns an {@link android.view.accessibility.AccessibilityNodeInfo} for the
specified virtual view id, which includes the host view
({@link View#NO_ID}).
switch (virtualViewId) {
case View.NO_ID:
return createNodeForHost();
default:
return createNodeForChild(virtualViewId);
}
|
private android.view.accessibility.AccessibilityNodeInfo | createNodeForChild(int virtualViewId)Constructs and returns an {@link AccessibilityNodeInfo} for the
specified item. Automatically manages accessibility focus actions.
Allows the implementing class to specify most node properties, but
overrides the following:
- {@link AccessibilityNodeInfo#setPackageName}
- {@link AccessibilityNodeInfo#setClassName}
- {@link AccessibilityNodeInfo#setParent(View)}
- {@link AccessibilityNodeInfo#setSource(View, int)}
- {@link AccessibilityNodeInfo#setVisibleToUser}
- {@link AccessibilityNodeInfo#setBoundsInScreen(Rect)}
Uses the bounds of the parent view and the parent-relative bounding
rectangle specified by
{@link AccessibilityNodeInfo#getBoundsInParent} to automatically
update the following properties:
- {@link AccessibilityNodeInfo#setVisibleToUser}
- {@link AccessibilityNodeInfo#setBoundsInParent}
ensureTempRects();
final Rect tempParentRect = mTempParentRect;
final int[] tempGlobalRect = mTempGlobalRect;
final Rect tempScreenRect = mTempScreenRect;
final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain();
// Ensure the client has good defaults.
node.setEnabled(true);
node.setClassName(DEFAULT_CLASS_NAME);
node.setBoundsInParent(INVALID_PARENT_BOUNDS);
// Allow the client to populate the node.
onPopulateNodeForVirtualView(virtualViewId, node);
// Make sure the developer is following the rules.
if ((node.getText() == null) && (node.getContentDescription() == null)) {
throw new RuntimeException("Callbacks must add text or a content description in "
+ "populateNodeForVirtualViewId()");
}
node.getBoundsInParent(tempParentRect);
if (tempParentRect.equals(INVALID_PARENT_BOUNDS)) {
throw new RuntimeException("Callbacks must set parent bounds in "
+ "populateNodeForVirtualViewId()");
}
final int actions = node.getActions();
if ((actions & AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) != 0) {
throw new RuntimeException("Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in "
+ "populateNodeForVirtualViewId()");
}
if ((actions & AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS) != 0) {
throw new RuntimeException("Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in "
+ "populateNodeForVirtualViewId()");
}
// Don't allow the client to override these properties.
node.setPackageName(mView.getContext().getPackageName());
node.setSource(mView, virtualViewId);
node.setParent(mView);
// Manage internal accessibility focus state.
if (mFocusedVirtualViewId == virtualViewId) {
node.setAccessibilityFocused(true);
node.addAction(AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
} else {
node.setAccessibilityFocused(false);
node.addAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS);
}
// Set the visibility based on the parent bound.
if (intersectVisibleToUser(tempParentRect)) {
node.setVisibleToUser(true);
node.setBoundsInParent(tempParentRect);
}
// Calculate screen-relative bound.
mView.getLocationOnScreen(tempGlobalRect);
final int offsetX = tempGlobalRect[0];
final int offsetY = tempGlobalRect[1];
tempScreenRect.set(tempParentRect);
tempScreenRect.offset(offsetX, offsetY);
node.setBoundsInScreen(tempScreenRect);
return node;
|
private android.view.accessibility.AccessibilityNodeInfo | createNodeForHost()Constructs and returns an {@link AccessibilityNodeInfo} for the
host view populated with its virtual descendants.
final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(mView);
onInitializeAccessibilityNodeInfo(mView, node);
// Add the virtual descendants.
if (mTempArray == null) {
mTempArray = new IntArray();
} else {
mTempArray.clear();
}
final IntArray virtualViewIds = mTempArray;
getVisibleVirtualViews(virtualViewIds);
final int N = virtualViewIds.size();
for (int i = 0; i < N; i++) {
node.addChild(mView, virtualViewIds.get(i));
}
return node;
|
public boolean | dispatchHoverEvent(android.view.MotionEvent event)Dispatches hover {@link android.view.MotionEvent}s to the virtual view hierarchy when
the Explore by Touch feature is enabled.
This method should be called by overriding
{@link View#dispatchHoverEvent}:
@Override
public boolean dispatchHoverEvent(MotionEvent event) {
if (mHelper.dispatchHoverEvent(this, event) {
return true;
}
return super.dispatchHoverEvent(event);
}
if (!mManager.isEnabled() || !mManager.isTouchExplorationEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_HOVER_ENTER:
final int virtualViewId = getVirtualViewAt(event.getX(), event.getY());
updateHoveredVirtualView(virtualViewId);
return (virtualViewId != INVALID_ID);
case MotionEvent.ACTION_HOVER_EXIT:
if (mFocusedVirtualViewId != INVALID_ID) {
updateHoveredVirtualView(INVALID_ID);
return true;
}
return false;
default:
return false;
}
|
private void | ensureTempRects()
mTempGlobalRect = new int[2];
mTempParentRect = new Rect();
mTempScreenRect = new Rect();
|
public android.view.accessibility.AccessibilityNodeProvider | getAccessibilityNodeProvider(android.view.View host)Returns the {@link android.view.accessibility.AccessibilityNodeProvider} for this helper.
if (mNodeProvider == null) {
mNodeProvider = new ExploreByTouchNodeProvider();
}
return mNodeProvider;
|
public int | getFocusedVirtualView()Returns the virtual view id for the currently focused item,
return mFocusedVirtualViewId;
|
protected abstract int | getVirtualViewAt(float x, float y)Provides a mapping between view-relative coordinates and logical
items.
|
protected abstract void | getVisibleVirtualViews(android.util.IntArray virtualViewIds)Populates a list with the view's visible items. The ordering of items
within {@code virtualViewIds} specifies order of accessibility focus
traversal.
|
private boolean | intersectVisibleToUser(android.graphics.Rect localRect)Computes whether the specified {@link Rect} intersects with the visible
portion of its parent {@link View}. Modifies {@code localRect} to contain
only the visible portion.
// Missing or empty bounds mean this view is not visible.
if ((localRect == null) || localRect.isEmpty()) {
return false;
}
// Attached to invisible window means this view is not visible.
if (mView.getWindowVisibility() != View.VISIBLE) {
return false;
}
// An invisible predecessor means that this view is not visible.
ViewParent viewParent = mView.getParent();
while (viewParent instanceof View) {
final View view = (View) viewParent;
if ((view.getAlpha() <= 0) || (view.getVisibility() != View.VISIBLE)) {
return false;
}
viewParent = view.getParent();
}
// A null parent implies the view is not visible.
if (viewParent == null) {
return false;
}
// If no portion of the parent is visible, this view is not visible.
if (mTempVisibleRect == null) {
mTempVisibleRect = new Rect();
}
final Rect tempVisibleRect = mTempVisibleRect;
if (!mView.getLocalVisibleRect(tempVisibleRect)) {
return false;
}
// Check if the view intersects the visible portion of the parent.
return localRect.intersect(tempVisibleRect);
|
public void | invalidateRoot()Notifies the accessibility framework that the properties of the parent
view have changed.
You must call this method after adding or removing items from the
parent view.
invalidateVirtualView(View.NO_ID);
|
public void | invalidateVirtualView(int virtualViewId)Notifies the accessibility framework that the properties of a particular
item have changed.
You must call this method after changing any of the properties set
in {@link #onPopulateNodeForVirtualView}.
sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
|
private boolean | isAccessibilityFocused(int virtualViewId)Returns whether this virtual view is accessibility focused.
return (mFocusedVirtualViewId == virtualViewId);
|
private boolean | manageFocusForChild(int virtualViewId, int action)
switch (action) {
case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
return requestAccessibilityFocus(virtualViewId);
case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
return clearAccessibilityFocus(virtualViewId);
default:
return false;
}
|
protected abstract boolean | onPerformActionForVirtualView(int virtualViewId, int action, android.os.Bundle arguments)Performs the specified accessibility action on the item associated
with the virtual view identifier. See
{@link AccessibilityNodeInfo#performAction(int, Bundle)} for
more information.
Implementations must handle any actions added manually in
{@link #onPopulateNodeForVirtualView}.
The helper class automatically handles focus management resulting
from {@link AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS}
and
{@link AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
actions.
|
protected abstract void | onPopulateEventForVirtualView(int virtualViewId, android.view.accessibility.AccessibilityEvent event)Populates an {@link AccessibilityEvent} with information about the
specified item.
Implementations must populate the following required fields:
- event text, see {@link AccessibilityEvent#getText} or
{@link AccessibilityEvent#setContentDescription}
The helper class automatically populates the following fields with
default values, but implementations may optionally override them:
- item class name, set to android.view.View, see
{@link AccessibilityEvent#setClassName}
The following required fields are automatically populated by the
helper class and may not be overridden:
- package name, set to the package of the host view's
{@link Context}, see {@link AccessibilityEvent#setPackageName}
- event source, set to the host view and virtual view identifier,
see {@link AccessibilityRecord#setSource(View, int)}
|
protected abstract void | onPopulateNodeForVirtualView(int virtualViewId, android.view.accessibility.AccessibilityNodeInfo node)Populates an {@link AccessibilityNodeInfo} with information
about the specified item.
Implementations must populate the following required fields:
- event text, see {@link AccessibilityNodeInfo#setText} or
{@link AccessibilityNodeInfo#setContentDescription}
- bounds in parent coordinates, see
{@link AccessibilityNodeInfo#setBoundsInParent}
The helper class automatically populates the following fields with
default values, but implementations may optionally override them:
- enabled state, set to true, see
{@link AccessibilityNodeInfo#setEnabled}
- item class name, identical to the class name set by
{@link #onPopulateEventForVirtualView}, see
{@link AccessibilityNodeInfo#setClassName}
The following required fields are automatically populated by the
helper class and may not be overridden:
- package name, identical to the package name set by
{@link #onPopulateEventForVirtualView}, see
{@link AccessibilityNodeInfo#setPackageName}
- node source, identical to the event source set in
{@link #onPopulateEventForVirtualView}, see
{@link AccessibilityNodeInfo#setSource(View, int)}
- parent view, set to the host view, see
{@link AccessibilityNodeInfo#setParent(View)}
- visibility, computed based on parent-relative bounds, see
{@link AccessibilityNodeInfo#setVisibleToUser}
- accessibility focus, computed based on internal helper state, see
{@link AccessibilityNodeInfo#setAccessibilityFocused}
- bounds in screen coordinates, computed based on host view bounds,
see {@link AccessibilityNodeInfo#setBoundsInScreen}
Additionally, the helper class automatically handles accessibility
focus management by adding the appropriate
{@link AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS} or
{@link AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
action. Implementations must never manually add these actions.
The helper class also automatically modifies parent- and
screen-relative bounds to reflect the portion of the item visible
within its parent.
|
private boolean | performAction(int virtualViewId, int action, android.os.Bundle arguments)
switch (virtualViewId) {
case View.NO_ID:
return performActionForHost(action, arguments);
default:
return performActionForChild(virtualViewId, action, arguments);
}
|
private boolean | performActionForChild(int virtualViewId, int action, android.os.Bundle arguments)
switch (action) {
case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
return manageFocusForChild(virtualViewId, action);
default:
return onPerformActionForVirtualView(virtualViewId, action, arguments);
}
|
private boolean | performActionForHost(int action, android.os.Bundle arguments)
return performAccessibilityAction(mView, action, arguments);
|
private boolean | requestAccessibilityFocus(int virtualViewId)Attempts to give accessibility focus to a virtual view.
A virtual view will not actually take focus if
{@link AccessibilityManager#isEnabled()} returns false,
{@link AccessibilityManager#isTouchExplorationEnabled()} returns false,
or the view already has accessibility focus.
final AccessibilityManager accessibilityManager =
(AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
if (!mManager.isEnabled()
|| !accessibilityManager.isTouchExplorationEnabled()) {
return false;
}
// TODO: Check virtual view visibility.
if (!isAccessibilityFocused(virtualViewId)) {
mFocusedVirtualViewId = virtualViewId;
// TODO: Only invalidate virtual view bounds.
mView.invalidate();
sendEventForVirtualView(virtualViewId,
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
return true;
}
return false;
|
public boolean | sendEventForVirtualView(int virtualViewId, int eventType)Populates an event of the specified type with information about an item
and attempts to send it up through the view hierarchy.
You should call this method after performing a user action that normally
fires an accessibility event, such as clicking on an item.
public void performItemClick(T item) {
...
sendEventForVirtualViewId(item.id, AccessibilityEvent.TYPE_VIEW_CLICKED);
}
if ((virtualViewId == INVALID_ID) || !mManager.isEnabled()) {
return false;
}
final ViewParent parent = mView.getParent();
if (parent == null) {
return false;
}
final AccessibilityEvent event = createEvent(virtualViewId, eventType);
return parent.requestSendAccessibilityEvent(mView, event);
|
private void | updateHoveredVirtualView(int virtualViewId)Sets the currently hovered item, sending hover accessibility events as
necessary to maintain the correct state.
if (mHoveredVirtualViewId == virtualViewId) {
return;
}
final int previousVirtualViewId = mHoveredVirtualViewId;
mHoveredVirtualViewId = virtualViewId;
// Stay consistent with framework behavior by sending ENTER/EXIT pairs
// in reverse order. This is accurate as of API 18.
sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
sendEventForVirtualView(previousVirtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
|