summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorSvetoslav <svetoslavganov@google.com>2014-10-09 18:40:56 -0700
committerSvetoslav <svetoslavganov@google.com>2014-10-30 16:55:53 -0700
commit6c70290ff0b20329c8f173d5c3423eb83ddc46f1 (patch)
tree87a40e17c6d5224de8370f74dd13d79a350048f5 /core
parentc1c86151b033a2c866c10970553b567dfb359ba9 (diff)
downloadframeworks_base-6c70290ff0b20329c8f173d5c3423eb83ddc46f1.zip
frameworks_base-6c70290ff0b20329c8f173d5c3423eb83ddc46f1.tar.gz
frameworks_base-6c70290ff0b20329c8f173d5c3423eb83ddc46f1.tar.bz2
Adding APIs to enable apps to influence accessibility traversal.
Accessibility focus is a token that can be put on any view and is entirely controlled by accessibility services which decide whether to put accessibility focus on a given view and what the traversal strategy is. Currently accessibility service use the view tree structure for traversal which does not always reflect the logical order and the user experience is sub-optimal. In such cases apps should be able to influence how they are traversed for accessibility. This change adds the notion of an accessibilty traversal order. If a view has next in the traversal, an accessibility service has to traverse the content of this view before traversing the content next one. This change will enable accessibility service to move away from using the view tree and use spatial location of the content while app developers can also influence the treversal order. bug:17319923 Change-Id: Ib67ae4577f30b2c96575c71538dc1afdea08bfd3
Diffstat (limited to 'core')
-rw-r--r--core/java/android/view/View.java134
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java168
-rw-r--r--core/java/android/widget/RemoteViews.java20
-rw-r--r--core/res/res/values/attrs.xml12
-rw-r--r--core/res/res/values/public.xml2
5 files changed, 315 insertions, 21 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e4f95a4..1d09696 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3109,6 +3109,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private MatchLabelForPredicate mMatchLabelForPredicate;
/**
+ * Specifies a view before which this one is visited in accessibility traversal.
+ */
+ private int mAccessibilityTraversalBeforeId = NO_ID;
+
+ /**
+ * Specifies a view after which this one is visited in accessibility traversal.
+ */
+ private int mAccessibilityTraversalAfterId = NO_ID;
+
+ /**
* Predicate for matching a view by its id.
*/
private MatchIdPredicate mMatchIdPredicate;
@@ -3888,6 +3898,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
case com.android.internal.R.styleable.View_contentDescription:
setContentDescription(a.getString(attr));
break;
+ case com.android.internal.R.styleable.View_accessibilityTraversalBefore:
+ setAccessibilityTraversalBefore(a.getResourceId(attr, NO_ID));
+ break;
+ case com.android.internal.R.styleable.View_accessibilityTraversalAfter:
+ setAccessibilityTraversalAfter(a.getResourceId(attr, NO_ID));
+ break;
case com.android.internal.R.styleable.View_labelFor:
setLabelFor(a.getResourceId(attr, NO_ID));
break;
@@ -5611,6 +5627,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (rootView == null) {
rootView = this;
}
+
View label = rootView.findLabelForView(this, mID);
if (label != null) {
info.setLabeledBy(label);
@@ -5639,6 +5656,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
+ if (mAccessibilityTraversalBeforeId != View.NO_ID) {
+ View rootView = getRootView();
+ if (rootView == null) {
+ rootView = this;
+ }
+ View next = rootView.findViewInsideOutShouldExist(this,
+ mAccessibilityTraversalBeforeId);
+ if (next != null) {
+ info.setTraversalBefore(next);
+ }
+ }
+
+ if (mAccessibilityTraversalAfterId != View.NO_ID) {
+ View rootView = getRootView();
+ if (rootView == null) {
+ rootView = this;
+ }
+ View next = rootView.findViewInsideOutShouldExist(this,
+ mAccessibilityTraversalAfterId);
+ if (next != null) {
+ info.setTraversalAfter(next);
+ }
+ }
+
info.setVisibleToUser(isVisibleToUser());
info.setPackageName(mContext.getPackageName());
@@ -6043,6 +6084,94 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Sets the id of a view before which this one is visited in accessibility traversal.
+ * A screen-reader must visit the content of this view before the content of the one
+ * it precedes. For example, if view B is set to be before view A, then a screen-reader
+ * will traverse the entire content of B before traversing the entire content of A,
+ * regardles of what traversal strategy it is using.
+ * <p>
+ * Views that do not have specified before/after relationships are traversed in order
+ * determined by the screen-reader.
+ * </p>
+ * <p>
+ * Setting that this view is before a view that is not important for accessibility
+ * or if this view is not important for accessibility will have no effect as the
+ * screen-reader is not aware of unimportant views.
+ * </p>
+ *
+ * @param beforeId The id of a view this one precedes in accessibility traversal.
+ *
+ * @attr ref android.R.styleable#View_accessibilityTraversalBefore
+ *
+ * @see #setImportantForAccessibility(int)
+ */
+ @RemotableViewMethod
+ public void setAccessibilityTraversalBefore(int beforeId) {
+ if (mAccessibilityTraversalBeforeId == beforeId) {
+ return;
+ }
+ mAccessibilityTraversalBeforeId = beforeId;
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ }
+
+ /**
+ * Gets the id of a view before which this one is visited in accessibility traversal.
+ *
+ * @return The id of a view this one precedes in accessibility traversal if
+ * specified, otherwise {@link #NO_ID}.
+ *
+ * @see #setAccessibilityTraversalBefore(int)
+ */
+ public int getAccessibilityTraversalBefore() {
+ return mAccessibilityTraversalBeforeId;
+ }
+
+ /**
+ * Sets the id of a view after which this one is visited in accessibility traversal.
+ * A screen-reader must visit the content of the other view before the content of this
+ * one. For example, if view B is set to be after view A, then a screen-reader
+ * will traverse the entire content of A before traversing the entire content of B,
+ * regardles of what traversal strategy it is using.
+ * <p>
+ * Views that do not have specified before/after relationships are traversed in order
+ * determined by the screen-reader.
+ * </p>
+ * <p>
+ * Setting that this view is after a view that is not important for accessibility
+ * or if this view is not important for accessibility will have no effect as the
+ * screen-reader is not aware of unimportant views.
+ * </p>
+ *
+ * @param afterId The id of a view this one succedees in accessibility traversal.
+ *
+ * @attr ref android.R.styleable#View_accessibilityTraversalAfter
+ *
+ * @see #setImportantForAccessibility(int)
+ */
+ @RemotableViewMethod
+ public void setAccessibilityTraversalAfter(int afterId) {
+ if (mAccessibilityTraversalAfterId == afterId) {
+ return;
+ }
+ mAccessibilityTraversalAfterId = afterId;
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ }
+
+ /**
+ * Gets the id of a view after which this one is visited in accessibility traversal.
+ *
+ * @return The id of a view this one succeedes in accessibility traversal if
+ * specified, otherwise {@link #NO_ID}.
+ *
+ * @see #setAccessibilityTraversalAfter(int)
+ */
+ public int getAccessibilityTraversalAfter() {
+ return mAccessibilityTraversalAfterId;
+ }
+
+ /**
* Gets the id of a view for which this view serves as a label for
* accessibility purposes.
*
@@ -6061,11 +6190,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
@RemotableViewMethod
public void setLabelFor(int id) {
+ if (mLabelForId == id) {
+ return;
+ }
mLabelForId = id;
if (mLabelForId != View.NO_ID
&& mID == View.NO_ID) {
mID = generateViewId();
}
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 3987fbc..b5afdf7 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -547,6 +547,8 @@ public class AccessibilityNodeInfo implements Parcelable {
private long mParentNodeId = ROOT_NODE_ID;
private long mLabelForId = ROOT_NODE_ID;
private long mLabeledById = ROOT_NODE_ID;
+ private long mTraversalBefore = ROOT_NODE_ID;
+ private long mTraversalAfter = ROOT_NODE_ID;
private int mBooleanProperties;
private final Rect mBoundsInParent = new Rect();
@@ -1046,6 +1048,126 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * 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(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, 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 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(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, 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. 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);
+ }
+
+ /**
* Sets the maximum text length, or -1 for no limit.
* <p>
* Typically used to indicate that an editable text field has a limit on
@@ -1229,13 +1351,7 @@ public class AccessibilityNodeInfo implements Parcelable {
*/
public AccessibilityNodeInfo getParent() {
enforceSealed();
- if (!canPerformRequestOverConnection(mParentNodeId)) {
- return null;
- }
- AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
- return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- mWindowId, mParentNodeId, false, FLAG_PREFETCH_PREDECESSORS
- | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ return getNodeForAccessibilityId(mParentNodeId);
}
/**
@@ -2055,13 +2171,7 @@ public class AccessibilityNodeInfo implements Parcelable {
*/
public AccessibilityNodeInfo getLabelFor() {
enforceSealed();
- if (!canPerformRequestOverConnection(mLabelForId)) {
- return null;
- }
- AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
- return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- mWindowId, mLabelForId, false, FLAG_PREFETCH_PREDECESSORS
- | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ return getNodeForAccessibilityId(mLabelForId);
}
/**
@@ -2113,13 +2223,7 @@ public class AccessibilityNodeInfo implements Parcelable {
*/
public AccessibilityNodeInfo getLabeledBy() {
enforceSealed();
- if (!canPerformRequestOverConnection(mLabeledById)) {
- return null;
- }
- AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
- return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- mWindowId, mLabeledById, false, FLAG_PREFETCH_PREDECESSORS
- | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ return getNodeForAccessibilityId(mLabeledById);
}
/**
@@ -2453,6 +2557,9 @@ public class AccessibilityNodeInfo implements Parcelable {
parcel.writeLong(mParentNodeId);
parcel.writeLong(mLabelForId);
parcel.writeLong(mLabeledById);
+ parcel.writeLong(mTraversalBefore);
+ parcel.writeLong(mTraversalAfter);
+
parcel.writeInt(mConnectionId);
final LongArray childIds = mChildNodeIds;
@@ -2571,6 +2678,8 @@ public class AccessibilityNodeInfo implements Parcelable {
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);
@@ -2633,6 +2742,9 @@ public class AccessibilityNodeInfo implements Parcelable {
mParentNodeId = parcel.readLong();
mLabelForId = parcel.readLong();
mLabeledById = parcel.readLong();
+ mTraversalBefore = parcel.readLong();
+ mTraversalAfter = parcel.readLong();
+
mConnectionId = parcel.readInt();
final int childrenSize = parcel.readInt();
@@ -2725,6 +2837,8 @@ public class AccessibilityNodeInfo implements Parcelable {
mParentNodeId = ROOT_NODE_ID;
mLabelForId = ROOT_NODE_ID;
mLabeledById = ROOT_NODE_ID;
+ mTraversalBefore = ROOT_NODE_ID;
+ mTraversalAfter = ROOT_NODE_ID;
mWindowId = UNDEFINED_ITEM_ID;
mConnectionId = UNDEFINED_CONNECTION_ID;
mMaxTextLength = -1;
@@ -2911,6 +3025,8 @@ public class AccessibilityNodeInfo implements Parcelable {
builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
builder.append("; mParentNodeId: " + mParentNodeId);
+ builder.append("; traversalBefore: ").append(mTraversalBefore);
+ builder.append("; traversalAfter: ").append(mTraversalAfter);
int granularities = mMovementGranularities;
builder.append("; MovementGranularities: [");
@@ -2963,6 +3079,16 @@ public class AccessibilityNodeInfo implements Parcelable {
return builder.toString();
}
+ private AccessibilityNodeInfo getNodeForAccessibilityId(long accessibilityId) {
+ if (!canPerformRequestOverConnection(accessibilityId)) {
+ return null;
+ }
+ AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+ return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
+ mWindowId, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS
+ | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ }
+
/**
* 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.
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 80f364b..dd7fa18 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -2528,6 +2528,26 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}.
+ *
+ * @param viewId The id of the view whose before view in accessibility traversal to set.
+ * @param nextId The id of the next in the accessibility traversal.
+ **/
+ public void setAccessibilityTraversalBefore(int viewId, int nextId) {
+ setInt(viewId, "setAccessibilityTraversalBefore", nextId);
+ }
+
+ /**
+ * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}.
+ *
+ * @param viewId The id of the view whose after view in accessibility traversal to set.
+ * @param nextId The id of the next in the accessibility traversal.
+ **/
+ public void setAccessibilityTraversalAfter(int viewId, int nextId) {
+ setInt(viewId, "setAccessibilityTraversalAfter", nextId);
+ }
+
+ /**
* Equivalent to calling View.setLabelFor(int).
*
* @param viewId The id of the view whose property to set.
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index c4131b3..91a8598 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2363,6 +2363,18 @@
representation this attribute can be used for providing such. -->
<attr name="contentDescription" format="string" localization="suggested" />
+ <!-- Sets the id of a view before which this one is visited in accessibility traversal.
+ A screen-reader must visit the content of this view before the content of the one
+ it precedes.
+ @see android.view.View#setAccessibilityTraversalBefore(int)} -->
+ <attr name="accessibilityTraversalBefore" format="integer" />
+
+ <!-- Sets the id of a view after which this one is visited in accessibility traversal.
+ A screen-reader must visit the content of the other view before the content of
+ this one.
+ @see android.view.View#setAccessibilityTraversalAfter(int)} -->
+ <attr name="accessibilityTraversalAfter" format="integer" />
+
<!-- Name of the method in this View's context to invoke when the view is
clicked. This name must correspond to a public method that takes
exactly one parameter of type View. For instance, if you specify
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c0b7fd0..c0a5ab2 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2597,5 +2597,7 @@
<public type="attr" name="resizeClip"/>
<public type="attr" name="collapseContentDescription"/>
+ <public type="attr" name="accessibilityTraversalBefore" />
+ <public type="attr" name="accessibilityTraversalAfter" />
</resources>