Merge "Adding explicit text traversal granularities and actions for web navigation."
diff --git a/api/current.txt b/api/current.txt
index 408d044..f2b35a8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24930,6 +24930,7 @@
method public static java.lang.String eventTypeToString(int);
method public long getEventTime();
method public int getEventType();
+ method public int getGranularity();
method public java.lang.CharSequence getPackageName();
method public android.view.accessibility.AccessibilityRecord getRecord(int);
method public int getRecordCount();
@@ -24939,6 +24940,7 @@
method public static android.view.accessibility.AccessibilityEvent obtain();
method public void setEventTime(long);
method public void setEventType(int);
+ method public void setGranularity(int);
method public void setPackageName(java.lang.CharSequence);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
@@ -24960,6 +24962,7 @@
field public static final int TYPE_VIEW_SELECTED = 4; // 0x4
field public static final int TYPE_VIEW_TEXT_CHANGED = 16; // 0x10
field public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
+ field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY = 131072; // 0x20000
field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
field public static final int TYPE_WINDOW_STATE_CHANGED = 32; // 0x20
}
@@ -25000,7 +25003,7 @@
method public int getChildCount();
method public java.lang.CharSequence getClassName();
method public java.lang.CharSequence getContentDescription();
- method public java.lang.CharSequence[] getGranularities();
+ method public int getGranularities();
method public java.lang.CharSequence getPackageName();
method public android.view.accessibility.AccessibilityNodeInfo getParent();
method public java.lang.CharSequence getText();
@@ -25034,7 +25037,7 @@
method public void setEnabled(boolean);
method public void setFocusable(boolean);
method public void setFocused(boolean);
- method public void setGranularities(java.lang.CharSequence[]);
+ method public void setGranularities(int);
method public void setLongClickable(boolean);
method public void setPackageName(java.lang.CharSequence);
method public void setParent(android.view.View);
@@ -25047,7 +25050,8 @@
method public void setText(java.lang.CharSequence);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
- field public static final java.lang.String ACTION_ARGUMENT_GRANULARITY = "ACTION_ARGUMENT_GRANULARITY";
+ field public static final java.lang.String ACTION_ARGUMENT_GRANULARITY_INT = "ACTION_ARGUMENT_GRANULARITY_INT";
+ field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
@@ -25055,11 +25059,18 @@
field public static final int ACTION_FOCUS = 1; // 0x1
field public static final int ACTION_LONG_CLICK = 32; // 0x20
field public static final int ACTION_NEXT_AT_GRANULARITY = 256; // 0x100
+ field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
field public static final int ACTION_PREVIOUS_AT_GRANULARITY = 512; // 0x200
+ field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
field public static final int ACTION_SELECT = 4; // 0x4
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
field public static final int FOCUS_INPUT = 1; // 0x1
+ field public static final int GRANULARITY_CHARACTER = 1; // 0x1
+ field public static final int GRANULARITY_LINE = 4; // 0x4
+ field public static final int GRANULARITY_PAGE = 16; // 0x10
+ field public static final int GRANULARITY_PARAGRAPH = 8; // 0x8
+ field public static final int GRANULARITY_WORD = 2; // 0x2
}
public abstract class AccessibilityNodeProvider {
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 6cb1578..6d1166e 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -226,6 +226,23 @@
* <li>{@link #getContentDescription()} - The content description of the source.</li>
* </ul>
* </p>
+ * <b>View text traversed at granularity</b> - represents the event of traversing the
+ * text of a view at a given granularity. For example, moving to the next word.</br>
+ * <em>Type:</em> {@link #TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY} </br>
+ * <em>Properties:</em></br>
+ * <ul>
+ * <li>{@link #getEventType()} - The type of the event.</li>
+ * <li>{@link #getSource()} - The source info (for registered clients).</li>
+ * <li>{@link #getClassName()} - The class name of the source.</li>
+ * <li>{@link #getPackageName()} - The package name of the source.</li>
+ * <li>{@link #getEventTime()} - The event time.</li>
+ * <li>{@link #getText()} - The text of the current text at the granularity.</li>
+ * <li>{@link #isPassword()} - Whether the source is password.</li>
+ * <li>{@link #isEnabled()} - Whether the source is enabled.</li>
+ * <li>{@link #getContentDescription()} - The content description of the source.</li>
+ * <li>{@link #getGranularity()} - Sets the granularity at which a view's text was traversed.</li>
+ * </ul>
+ * </p>
* <p>
* <b>View scrolled</b> - represents the event of scrolling a view. If
* the source is a descendant of {@link android.widget.AdapterView} the
@@ -580,6 +597,11 @@
public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000;
/**
+ * Represents the event of traversing the text of a view at a given granularity.
+ */
+ public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY = 0x00020000;
+
+ /**
* Mask for {@link AccessibilityEvent} all types.
*
* @see #TYPE_VIEW_CLICKED
@@ -597,6 +619,7 @@
* @see #TYPE_VIEW_SCROLLED
* @see #TYPE_VIEW_TEXT_SELECTION_CHANGED
* @see #TYPE_ANNOUNCEMENT
+ * @see #TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY
*/
public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
@@ -610,6 +633,7 @@
private int mEventType;
private CharSequence mPackageName;
private long mEventTime;
+ int mGranularity;
private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>();
@@ -627,6 +651,7 @@
void init(AccessibilityEvent event) {
super.init(event);
mEventType = event.mEventType;
+ mGranularity = event.mGranularity;
mEventTime = event.mEventTime;
mPackageName = event.mPackageName;
}
@@ -744,6 +769,27 @@
}
/**
+ * Sets the text granularity that was traversed.
+ *
+ * @param granularity The granularity.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setGranularity(int granularity) {
+ enforceNotSealed();
+ mGranularity = granularity;
+ }
+
+ /**
+ * Gets the text granularity that was traversed.
+ *
+ * @return The granularity.
+ */
+ public int getGranularity() {
+ return mGranularity;
+ }
+
+ /**
* Returns a cached instance if such is available or a new one is
* instantiated with its type property set.
*
@@ -831,6 +877,7 @@
protected void clear() {
super.clear();
mEventType = 0;
+ mGranularity = 0;
mPackageName = null;
mEventTime = 0;
while (!mRecords.isEmpty()) {
@@ -847,6 +894,7 @@
public void initFromParcel(Parcel parcel) {
mSealed = (parcel.readInt() == 1);
mEventType = parcel.readInt();
+ mGranularity = parcel.readInt();
mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
mEventTime = parcel.readLong();
mConnectionId = parcel.readInt();
@@ -897,6 +945,7 @@
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(isSealed() ? 1 : 0);
parcel.writeInt(mEventType);
+ parcel.writeInt(mGranularity);
TextUtils.writeToParcel(mPackageName, parcel, 0);
parcel.writeLong(mEventTime);
parcel.writeInt(mConnectionId);
@@ -953,6 +1002,7 @@
builder.append("EventType: ").append(eventTypeToString(mEventType));
builder.append("; EventTime: ").append(mEventTime);
builder.append("; PackageName: ").append(mPackageName);
+ builder.append("; Granularity: ").append(mGranularity);
builder.append(super.toString());
if (DEBUG) {
builder.append("\n");
@@ -1033,6 +1083,8 @@
return "TYPE_VIEW_ACCESSIBILITY_FOCUSED";
case TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED:
return "TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED";
+ case TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY:
+ return "TYPE_CURRENT_AT_GRANULARITY_CHANGED";
default:
return null;
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 3cb3b54..aec18fe 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -102,7 +102,7 @@
public static final int ACTION_CLEAR_SELECTION = 0x00000008;
/**
- * Action that clicks on the node info.
+ * Action that long clicks on the node info.
*/
public static final int ACTION_CLICK = 0x00000010;
@@ -122,78 +122,105 @@
public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080;
/**
- * Action that requests from the node to go to the next entity in its content
- * at a given granularity. For example, move to the next word, link, etc.
+ * Action that requests to go to the next entity in this node's text
+ * at a given granularity. For example, move to the next character, word, etc.
* <p>
- * <strong>Arguments:</strong>
- * <ul>
- * <li>
- * {@link #ACTION_ARGUMENT_GRANULARITY}
- * </li>
- * <li>
- * </p>
- * <p>
+ * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_GRANULARITY_INT}<br>
* <strong>Example:</strong>
* <code><pre><p>
- * // Assume the first granularity was presented to the user and she is
- * // making an explicit action to traverse the node at that granularity.
- * CharSequence granularity = info.getGranularity(0);
* Bundle arguments = new Bundle();
- * arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY, granularity);
+ * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY_INT,
+ * AccessibilityNodeInfo.GRANULARITY_CHARACTER);
* info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY, arguments);
* </code></pre></p>
- * </li>
- * </ul>
* </p>
- * @see #setGranularities(CharSequence[])
+ *
+ * @see #setGranularities(int)
* @see #getGranularities()
+ *
+ * @see #GRANULARITY_CHARACTER
+ * @see #GRANULARITY_WORD
+ * @see #GRANULARITY_LINE
+ * @see #GRANULARITY_PARAGRAPH
+ * @see #GRANULARITY_PAGE
*/
public static final int ACTION_NEXT_AT_GRANULARITY = 0x00000100;
/**
- * Action that requests from the node to go to the previous entity in its content
- * at a given granularity. For example, move to the next word, link, etc.
+ * Action that requests to go to the previous entity in this node's text
+ * at a given granularity. For example, move to the next character, word, etc.
* <p>
- * <strong>Arguments:</strong>
- * <ul>
- * <li>
- * {@link #ACTION_ARGUMENT_GRANULARITY}
- * </li>
- * <li>
- * </p>
- * <p>
+ * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_GRANULARITY_INT}<br>
* <strong>Example:</strong>
* <code><pre><p>
- * // Assume the first granularity was presented to the user and she is
- * // making an explicit action to traverse the node at that granularity.
- * CharSequence granularity = info.getGranularity(0);
* Bundle arguments = new Bundle();
- * arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY, granularity);
- * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY, arguments);
+ * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY_INT,
+ * AccessibilityNodeInfo.GRANULARITY_CHARACTER);
+ * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_GRANULARITY, arguments);
* </code></pre></p>
- * </li>
- * </ul>
* </p>
- * @see #setGranularities(CharSequence[])
+ *
+ * @see #setGranularities(int)
* @see #getGranularities()
+ *
+ * @see #GRANULARITY_CHARACTER
+ * @see #GRANULARITY_WORD
+ * @see #GRANULARITY_LINE
+ * @see #GRANULARITY_PARAGRAPH
+ * @see #GRANULARITY_PAGE
*/
public static final int ACTION_PREVIOUS_AT_GRANULARITY = 0x00000200;
/**
- * Argument for which content granularity to be used when traversing the node content.
+ * Action to move to the next HTML element of a given type. For example, move
+ * to the BUTTON, INPUT, TABLE, etc.
* <p>
- * <strong>Actions:</strong>
- * <ul>
- * <li>
- * {@link #ACTION_PREVIOUS_AT_GRANULARITY}
- * </li>
- * <li>
- * {@link #ACTION_PREVIOUS_AT_GRANULARITY}
- * </li>
- * </ul>
+ * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
+ * <strong>Example:</strong>
+ * <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
+ * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
+ * </code></pre></p>
* </p>
*/
- public static final String ACTION_ARGUMENT_GRANULARITY = "ACTION_ARGUMENT_GRANULARITY";
+ public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;
+
+ /**
+ * Action to move to the previous HTML element of a given type. For example, move
+ * to the BUTTON, INPUT, TABLE, etc.
+ * <p>
+ * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
+ * <strong>Example:</strong>
+ * <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
+ * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
+ * </code></pre></p>
+ * </p>
+ */
+ public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
+
+ /**
+ * Argument for which text granularity to be used when traversing the node text.
+ * <p>
+ * <strong>Type:</strong> int<br>
+ * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_GRANULARITY},
+ * {@link #ACTION_PREVIOUS_AT_GRANULARITY}
+ * </p>
+ */
+ public static final String ACTION_ARGUMENT_GRANULARITY_INT = "ACTION_ARGUMENT_GRANULARITY_INT";
+
+ /**
+ * Argument for which HTML element to get moving to the next/previous HTML element.
+ * <p>
+ * <strong>Type:</strong> String<br>
+ * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
+ * {@link #ACTION_PREVIOUS_HTML_ELEMENT}
+ * </p>
+ */
+ public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
+ "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
/**
* The input focus.
@@ -205,6 +232,33 @@
*/
public static final int FOCUS_ACCESSIBILITY = 2;
+ // Granularities
+
+ /**
+ * Granularity bit for traversing the text of a node by character.
+ */
+ public static final int GRANULARITY_CHARACTER = 0x00000001;
+
+ /**
+ * Granularity bit for traversing the text of a node by word.
+ */
+ public static final int GRANULARITY_WORD = 0x00000002;
+
+ /**
+ * Granularity bit for traversing the text of a node by line.
+ */
+ public static final int GRANULARITY_LINE = 0x00000004;
+
+ /**
+ * Granularity bit for traversing the text of a node by paragraph.
+ */
+ public static final int GRANULARITY_PARAGRAPH = 0x00000008;
+
+ /**
+ * Granularity bit for traversing the text of a node by page.
+ */
+ public static final int GRANULARITY_PAGE = 0x00000010;
+
// Boolean attributes.
private static final int PROPERTY_CHECKABLE = 0x00000001;
@@ -308,7 +362,7 @@
private final SparseLongArray mChildNodeIds = new SparseLongArray();
private int mActions;
- private CharSequence[] mGranularities;
+ private int mGranularities;
private int mConnectionId = UNDEFINED;
@@ -532,28 +586,28 @@
}
/**
- * Sets the granularities for traversing the content of this node.
+ * Sets the text granularities for traversing the text of this node.
* <p>
* <strong>Note:</strong> Cannot be called from an
* {@link android.accessibilityservice.AccessibilityService}.
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
*
- * @param granularities The granularity names.
+ * @param granularities The bit mask with granularities.
*
* @throws IllegalStateException If called from an AccessibilityService.
*/
- public void setGranularities(CharSequence[] granularities) {
+ public void setGranularities(int granularities) {
enforceNotSealed();
mGranularities = granularities;
}
/**
- * Gets the granularities for traversing the content of this node.
+ * Gets the granularities for traversing the text of this node.
*
- * @return The count.
+ * @return The bit mask with granularities.
*/
- public CharSequence[] getGranularities() {
+ public int getGranularities() {
return mGranularities;
}
@@ -1339,8 +1393,6 @@
parcel.writeLong(mParentNodeId);
parcel.writeInt(mConnectionId);
- parcel.writeCharSequenceArray(mGranularities);
-
SparseLongArray childIds = mChildNodeIds;
final int childIdsSize = childIds.size();
parcel.writeInt(childIdsSize);
@@ -1360,6 +1412,8 @@
parcel.writeInt(mActions);
+ parcel.writeInt(mGranularities);
+
parcel.writeInt(mBooleanProperties);
parcel.writeCharSequence(mPackageName);
@@ -1392,7 +1446,7 @@
mContentDescription = other.mContentDescription;
mActions= other.mActions;
mBooleanProperties = other.mBooleanProperties;
- mGranularities = (other.mGranularities) != null ? other.mGranularities.clone() : null;
+ mGranularities = other.mGranularities;
final int otherChildIdCount = other.mChildNodeIds.size();
for (int i = 0; i < otherChildIdCount; i++) {
mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i));
@@ -1411,8 +1465,6 @@
mParentNodeId = parcel.readLong();
mConnectionId = parcel.readInt();
- mGranularities = parcel.readCharSequenceArray();
-
SparseLongArray childIds = mChildNodeIds;
final int childrenSize = parcel.readInt();
for (int i = 0; i < childrenSize; i++) {
@@ -1432,6 +1484,8 @@
mActions = parcel.readInt();
+ mGranularities = parcel.readInt();
+
mBooleanProperties = parcel.readInt();
mPackageName = parcel.readCharSequence();
@@ -1449,7 +1503,7 @@
mParentNodeId = ROOT_NODE_ID;
mWindowId = UNDEFINED;
mConnectionId = UNDEFINED;
- mGranularities = null;
+ mGranularities = 0;
mChildNodeIds.clear();
mBoundsInParent.set(0, 0, 0, 0);
mBoundsInScreen.set(0, 0, 0, 0);
@@ -1482,6 +1536,29 @@
}
}
+ /**
+ * Gets the human readable granularity symbolic name.
+ *
+ * @param granularity The action.
+ * @return The symbolic name.
+ */
+ private static String getGranularitySymbolicName(int granularity) {
+ switch (granularity) {
+ case GRANULARITY_CHARACTER:
+ return "GRANULARITY_CHARACTER";
+ case GRANULARITY_WORD:
+ return "GRANULARITY_WORD";
+ case GRANULARITY_LINE:
+ return "GRANULARITY_LINE";
+ case GRANULARITY_PARAGRAPH:
+ return "GRANULARITY_PARAGRAPH";
+ case GRANULARITY_PAGE:
+ return "GRANULARITY_PAGE";
+ default:
+ throw new IllegalArgumentException("Unknown granularity: " + granularity);
+ }
+ }
+
private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
return (mWindowId != UNDEFINED
&& getAccessibilityViewId(accessibilityNodeId) != UNDEFINED
@@ -1529,11 +1606,13 @@
builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
builder.append("; mParentNodeId: " + mParentNodeId);
- CharSequence[] granularities = mGranularities;
+ int granularities = mGranularities;
builder.append("; granularities: [");
- for (int i = 0, count = granularities.length; i < count; i++) {
- builder.append(granularities[i]);
- if (i < count - 1) {
+ while (granularities != 0) {
+ final int granularity = 1 << Integer.numberOfTrailingZeros(granularities);
+ granularities &= ~granularity;
+ builder.append(getGranularitySymbolicName(granularity));
+ if (granularities != 0) {
builder.append(", ");
}
}
@@ -1570,7 +1649,6 @@
builder.append("; scrollable: " + isScrollable());
builder.append("; [");
-
for (int actionBits = mActions; actionBits != 0;) {
final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
actionBits &= ~action;
@@ -1579,7 +1657,6 @@
builder.append(", ");
}
}
-
builder.append("]");
return builder.toString();
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index b9404f3..01ddf1f 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1554,7 +1554,7 @@
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
- resolvedWindowId, action);
+ resolvedWindowId, action, arguments);
if (!permissionGranted) {
return false;
} else {
@@ -1702,7 +1702,16 @@
| AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS
| AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS
| AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY
- | AccessibilityNodeInfo.ACTION_PREVIOUS_AT_GRANULARITY;
+ | AccessibilityNodeInfo.ACTION_PREVIOUS_AT_GRANULARITY
+ | AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT
+ | AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT;
+
+ private static final int VALID_GRANULARITIES =
+ AccessibilityNodeInfo.GRANULARITY_CHARACTER
+ | AccessibilityNodeInfo.GRANULARITY_WORD
+ | AccessibilityNodeInfo.GRANULARITY_LINE
+ | AccessibilityNodeInfo.GRANULARITY_PARAGRAPH
+ | AccessibilityNodeInfo.GRANULARITY_PAGE;
private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
AccessibilityEvent.TYPE_VIEW_CLICKED
@@ -1752,10 +1761,12 @@
return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId);
}
- public boolean canPerformActionLocked(Service service, int windowId, int action) {
+ public boolean canPerformActionLocked(Service service, int windowId, int action,
+ Bundle arguments) {
return canRetrieveWindowContent(service)
&& isRetrievalAllowingWindow(windowId)
- && isActionPermitted(action);
+ && isActionPermitted(action)
+ && isActionArgumentsValid(action, arguments);
}
public boolean canRetrieveWindowContent(Service service) {
@@ -1779,6 +1790,29 @@
return (VALID_ACTIONS & action) != 0;
}
+ private boolean isActionArgumentsValid(int action, Bundle arguments) {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY:
+ case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_GRANULARITY: {
+ if (arguments.size() == 1) {
+ final int granularity = arguments.getInt(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY_INT);
+ return (granularity & VALID_GRANULARITIES) != 0
+ && Integer.bitCount(granularity) == 1;
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
+ case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT: {
+ if (arguments.size() == 1) {
+ String element = arguments.getString(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING);
+ return !TextUtils.isEmpty(element);
+ }
+ } break;
+ }
+ return false;
+ }
+
private void enforceCallingPermission(String permission, String function) {
if (OWN_PROCESS_ID == Binder.getCallingPid()) {
return;