Adding APIs to enable reporting virtual view hierarchies to accessibility serivces.
Added an interface that is the contract for a client to expose a virtual
view hierarchy to accessibility services. Clients impement this interface
and set it in the View that is the root of the virtual sub-tree. Adding
this finctionality via compostion as opposed to inheritance enables apps
to maintain backwards compatibility by setting the accessibility virtual
hierarchy provider on the View only if the API version is high enough.
bug:5382859
Change-Id: I7e3927b71a5517943c6cb071be2e87fba23132bf
diff --git a/api/current.txt b/api/current.txt
index b30f7a6..a52a9b7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22873,6 +22873,7 @@
method public boolean fitsSystemWindows();
method public android.view.View focusSearch(int);
method public void forceLayout();
+ method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider();
method public float getAlpha();
method public android.view.animation.Animation getAnimation();
method public android.os.IBinder getApplicationWindowToken();
@@ -23273,6 +23274,7 @@
public static class View.AccessibilityDelegate {
ctor public View.AccessibilityDelegate();
method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider(android.view.View);
method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public void onInitializeAccessibilityNodeInfo(android.view.View, android.view.accessibility.AccessibilityNodeInfo);
method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
@@ -24028,6 +24030,7 @@
public class AccessibilityNodeInfo implements android.os.Parcelable {
method public void addAction(int);
method public void addChild(android.view.View);
+ method public void addChild(android.view.View, int);
method public int describeContents();
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String);
method public int getActions();
@@ -24052,6 +24055,7 @@
method public boolean isScrollable();
method public boolean isSelected();
method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
+ method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int);
method public static android.view.accessibility.AccessibilityNodeInfo obtain();
method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.accessibility.AccessibilityNodeInfo);
method public boolean performAction(int);
@@ -24069,10 +24073,12 @@
method public void setLongClickable(boolean);
method public void setPackageName(java.lang.CharSequence);
method public void setParent(android.view.View);
+ method public void setParent(android.view.View, int);
method public void setPassword(boolean);
method public void setScrollable(boolean);
method public void setSelected(boolean);
method public void setSource(android.view.View);
+ method public void setSource(android.view.View, int);
method public void setText(java.lang.CharSequence);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
@@ -24082,6 +24088,13 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
+ public abstract class AccessibilityNodeProvider {
+ ctor public AccessibilityNodeProvider();
+ method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int);
+ method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int);
+ method public boolean performAccessibilityAction(int, int);
+ }
+
public class AccessibilityRecord {
method public int getAddedCount();
method public java.lang.CharSequence getBeforeText();
@@ -24127,6 +24140,7 @@
method public void setScrollY(int);
method public void setScrollable(boolean);
method public void setSource(android.view.View);
+ method public void setSource(android.view.View, int);
method public void setToIndex(int);
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 7c41082..52d9801 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -33,14 +33,14 @@
* Finds an {@link AccessibilityNodeInfo} by accessibility id.
*
* @param accessibilityWindowId A unique window id.
- * @param accessibilityViewId A unique View accessibility id.
+ * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id).
* @param interactionId The id of the interaction for matching with the callback result.
* @param callback Callback which to receive the result.
* @param threadId The id of the calling thread.
* @return The current window scale, where zero means a failure.
*/
float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
- int accessibilityViewId, int interactionId,
+ long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long threadId);
/**
@@ -51,15 +51,15 @@
*
* @param text The searched text.
* @param accessibilityWindowId A unique window id.
- * @param accessibilityViewId A unique View accessibility id from where to start the search.
- * Use {@link android.view.View#NO_ID} to start from the root.
+ * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id) from
+ * where to start the search. Use {@link android.view.View#NO_ID} to start from the root.
* @param interactionId The id of the interaction for matching with the callback result.
* @param callback Callback which to receive the result.
* @param threadId The id of the calling thread.
* @return The current window scale, where zero means a failure.
*/
- float findAccessibilityNodeInfosByViewText(String text, int accessibilityWindowId,
- int accessibilityViewId, int interractionId,
+ float findAccessibilityNodeInfosByText(String text, int accessibilityWindowId,
+ long accessibilityNodeId, int interractionId,
IAccessibilityInteractionConnectionCallback callback, long threadId);
/**
@@ -75,7 +75,7 @@
* @param threadId The id of the calling thread.
* @return The current window scale, where zero means a failure.
*/
- float findAccessibilityNodeInfosByViewTextInActiveWindow(String text,
+ float findAccessibilityNodeInfosByTextInActiveWindow(String text,
int interactionId, IAccessibilityInteractionConnectionCallback callback,
long threadId);
@@ -96,14 +96,14 @@
* Performs an accessibility action on an {@link AccessibilityNodeInfo}.
*
* @param accessibilityWindowId The id of the window.
- * @param accessibilityViewId A unique View accessibility id.
+ * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id).
* @param action The action to perform.
* @param interactionId The id of the interaction for matching with the callback result.
* @param callback Callback which to receive the result.
* @param threadId The id of the calling thread.
* @return Whether the action was performed.
*/
- boolean performAccessibilityAction(int accessibilityWindowId, int accessibilityViewId,
+ boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId,
int action, int interactionId, IAccessibilityInteractionConnectionCallback callback,
long threadId);
}
diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java
new file mode 100644
index 0000000..a08d5cb
--- /dev/null
+++ b/core/java/android/util/SparseLongArray.java
@@ -0,0 +1,247 @@
+/*
+ * 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.util;
+
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * SparseLongArrays map integers to longs. Unlike a normal array of longs,
+ * there can be gaps in the indices. It is intended to be more efficient
+ * than using a HashMap to map Integers to Longs.
+ *
+ * @hide
+ */
+public class SparseLongArray implements Cloneable {
+
+ private int[] mKeys;
+ private long[] mValues;
+ private int mSize;
+
+ /**
+ * Creates a new SparseLongArray containing no mappings.
+ */
+ public SparseLongArray() {
+ this(10);
+ }
+
+ /**
+ * Creates a new SparseLongArray containing no mappings that will not
+ * require any additional memory allocation to store the specified
+ * number of mappings.
+ */
+ public SparseLongArray(int initialCapacity) {
+ initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity);
+
+ mKeys = new int[initialCapacity];
+ mValues = new long[initialCapacity];
+ mSize = 0;
+ }
+
+ @Override
+ public SparseLongArray clone() {
+ SparseLongArray clone = null;
+ try {
+ clone = (SparseLongArray) super.clone();
+ clone.mKeys = mKeys.clone();
+ clone.mValues = mValues.clone();
+ } catch (CloneNotSupportedException cnse) {
+ /* ignore */
+ }
+ return clone;
+ }
+
+ /**
+ * Gets the long mapped from the specified key, or <code>0</code>
+ * if no such mapping has been made.
+ */
+ public long get(int key) {
+ return get(key, 0);
+ }
+
+ /**
+ * Gets the long mapped from the specified key, or the specified value
+ * if no such mapping has been made.
+ */
+ public long get(int key, long valueIfKeyNotFound) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i < 0) {
+ return valueIfKeyNotFound;
+ } else {
+ return mValues[i];
+ }
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ public void delete(int key) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ removeAt(i);
+ }
+ }
+
+ /**
+ * Removes the mapping at the given index.
+ */
+ public void removeAt(int index) {
+ System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
+ System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1));
+ mSize--;
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(int key, long value) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ mValues[i] = value;
+ } else {
+ i = ~i;
+
+ if (mSize >= mKeys.length) {
+ growKeyAndValueArrays(mSize + 1);
+ }
+
+ if (mSize - i != 0) {
+ System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+ System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+ }
+
+ mKeys[i] = key;
+ mValues[i] = value;
+ mSize++;
+ }
+ }
+
+ /**
+ * Returns the number of key-value mappings that this SparseIntArray
+ * currently stores.
+ */
+ public int size() {
+ return mSize;
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the key from the <code>index</code>th key-value mapping that this
+ * SparseLongArray stores.
+ */
+ public int keyAt(int index) {
+ return mKeys[index];
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the value from the <code>index</code>th key-value mapping that this
+ * SparseLongArray stores.
+ */
+ public long valueAt(int index) {
+ return mValues[index];
+ }
+
+ /**
+ * Returns the index for which {@link #keyAt} would return the
+ * specified key, or a negative number if the specified
+ * key is not mapped.
+ */
+ public int indexOfKey(int key) {
+ return binarySearch(mKeys, 0, mSize, key);
+ }
+
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified key, or a negative number if no keys map to the
+ * specified value.
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ */
+ public int indexOfValue(long value) {
+ for (int i = 0; i < mSize; i++)
+ if (mValues[i] == value)
+ return i;
+
+ return -1;
+ }
+
+ /**
+ * Removes all key-value mappings from this SparseIntArray.
+ */
+ public void clear() {
+ mSize = 0;
+ }
+
+ /**
+ * Puts a key/value pair into the array, optimizing for the case where
+ * the key is greater than all existing keys in the array.
+ */
+ public void append(int key, long value) {
+ if (mSize != 0 && key <= mKeys[mSize - 1]) {
+ put(key, value);
+ return;
+ }
+
+ int pos = mSize;
+ if (pos >= mKeys.length) {
+ growKeyAndValueArrays(pos + 1);
+ }
+
+ mKeys[pos] = key;
+ mValues[pos] = value;
+ mSize = pos + 1;
+ }
+
+ private void growKeyAndValueArrays(int minNeededSize) {
+ int n = ArrayUtils.idealLongArraySize(minNeededSize);
+
+ int[] nkeys = new int[n];
+ long[] nvalues = new long[n];
+
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+
+ private static int binarySearch(int[] a, int start, int len, long key) {
+ int high = start + len, low = start - 1, guess;
+
+ while (high - low > 1) {
+ guess = (high + low) / 2;
+
+ if (a[guess] < key)
+ low = guess;
+ else
+ high = guess;
+ }
+
+ if (high == start + len)
+ return ~(start + len);
+ else if (a[high] == key)
+ return high;
+ else
+ return ~high;
+ }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index fea79d5..61c8ee0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -62,6 +62,7 @@
import android.view.accessibility.AccessibilityEventSource;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
@@ -1969,6 +1970,21 @@
public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 0x00000002;
/**
+ * Find views that contain {@link AccessibilityNodeProvider}. Such
+ * a View is a root of virtual view hierarchy and may contain the searched
+ * text. If this flag is set Views with providers are automatically
+ * added and it is a responsibility of the client to call the APIs of
+ * the provider to determine whether the virtual tree rooted at this View
+ * contains the text, i.e. getting the list of {@link AccessibilityNodeInfo}s
+ * represeting the virtual views with this text.
+ *
+ * @see #findViewsWithText(ArrayList, CharSequence, int)
+ *
+ * @hide
+ */
+ public static final int FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS = 0x00000004;
+
+ /**
* Controls the over-scroll mode for this view.
* See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)},
* {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS},
@@ -4058,14 +4074,20 @@
* Note: The client is responsible for recycling the obtained instance by calling
* {@link AccessibilityNodeInfo#recycle()} to minimize object creation.
* </p>
+ *
* @return A populated {@link AccessibilityNodeInfo}.
*
* @see AccessibilityNodeInfo
*/
public AccessibilityNodeInfo createAccessibilityNodeInfo() {
- AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(this);
- onInitializeAccessibilityNodeInfo(info);
- return info;
+ AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+ if (provider != null) {
+ return provider.createAccessibilityNodeInfo(View.NO_ID);
+ } else {
+ AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(this);
+ onInitializeAccessibilityNodeInfo(info);
+ return info;
+ }
}
/**
@@ -4168,6 +4190,36 @@
}
/**
+ * Gets the provider for managing a virtual view hierarchy rooted at this View
+ * and reported to {@link android.accessibilityservice.AccessibilityService}s
+ * that explore the window content.
+ * <p>
+ * If this method returns an instance, this instance is responsible for managing
+ * {@link AccessibilityNodeInfo}s describing the virtual sub-tree rooted at this
+ * View including the one representing the View itself. Similarly the returned
+ * instance is responsible for performing accessibility actions on any virtual
+ * view or the root view itself.
+ * </p>
+ * <p>
+ * If an {@link AccessibilityDelegate} has been specified via calling
+ * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
+ * {@link AccessibilityDelegate#getAccessibilityNodeProvider(View)}
+ * is responsible for handling this call.
+ * </p>
+ *
+ * @return The provider.
+ *
+ * @see AccessibilityNodeProvider
+ */
+ public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+ if (mAccessibilityDelegate != null) {
+ return mAccessibilityDelegate.getAccessibilityNodeProvider(this);
+ } else {
+ return null;
+ }
+ }
+
+ /**
* Gets the unique identifier of this view on the screen for accessibility purposes.
* If this {@link View} is not attached to any window, {@value #NO_ID} is returned.
*
@@ -5186,14 +5238,18 @@
*
* @param outViews The output list of matching Views.
* @param searched The text to match against.
- *
+ *
* @see #FIND_VIEWS_WITH_TEXT
* @see #FIND_VIEWS_WITH_CONTENT_DESCRIPTION
* @see #setContentDescription(CharSequence)
*/
public void findViewsWithText(ArrayList<View> outViews, CharSequence searched, int flags) {
- if ((flags & FIND_VIEWS_WITH_CONTENT_DESCRIPTION) != 0 && !TextUtils.isEmpty(searched)
- && !TextUtils.isEmpty(mContentDescription)) {
+ if (getAccessibilityNodeProvider() != null) {
+ if ((flags & FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS) != 0) {
+ outViews.add(this);
+ }
+ } else if ((flags & FIND_VIEWS_WITH_CONTENT_DESCRIPTION) != 0
+ && !TextUtils.isEmpty(searched) && !TextUtils.isEmpty(mContentDescription)) {
String searchedLowerCase = searched.toString().toLowerCase();
String contentDescriptionLowerCase = mContentDescription.toString().toLowerCase();
if (contentDescriptionLowerCase.contains(searchedLowerCase)) {
@@ -14886,5 +14942,23 @@
AccessibilityEvent event) {
return host.onRequestSendAccessibilityEventInternal(child, event);
}
+
+ /**
+ * Gets the provider for managing a virtual view hierarchy rooted at this View
+ * and reported to {@link android.accessibilityservice.AccessibilityService}s
+ * that explore the window content.
+ * <p>
+ * The default implementation behaves as
+ * {@link View#getAccessibilityNodeProvider() View#getAccessibilityNodeProvider()} for
+ * the case of no accessibility delegate been set.
+ * </p>
+ *
+ * @return The provider.
+ *
+ * @see AccessibilityNodeProvider
+ */
+ public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
+ return null;
+ }
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a36aecb..4f9cd90 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -65,6 +65,7 @@
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -2335,7 +2336,7 @@
public final static int DO_PERFORM_ACCESSIBILITY_ACTION = 1020;
public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1021;
public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1022;
- public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1023;
+ public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 1023;
@Override
public String getMessageName(Message message) {
@@ -2386,8 +2387,8 @@
return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID:
return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID";
- case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT:
- return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT";
+ case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
+ return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
}
return super.getMessageName(message);
@@ -2620,10 +2621,10 @@
.findAccessibilityNodeInfoByViewIdUiThread(msg);
}
} break;
- case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: {
+ case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: {
if (mView != null) {
getAccessibilityInteractionController()
- .findAccessibilityNodeInfosByViewTextUiThread(msg);
+ .findAccessibilityNodeInfosByTextUiThread(msg);
}
} break;
}
@@ -4475,48 +4476,52 @@
*/
final class AccessibilityInteractionConnection
extends IAccessibilityInteractionConnection.Stub {
- private final WeakReference<ViewRootImpl> mViewAncestor;
+ private final WeakReference<ViewRootImpl> mViewRootImpl;
AccessibilityInteractionConnection(ViewRootImpl viewAncestor) {
- mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
+ mViewRootImpl = new WeakReference<ViewRootImpl>(viewAncestor);
}
- public void findAccessibilityNodeInfoByAccessibilityId(int accessibilityId,
+ public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
int interactionId, IAccessibilityInteractionConnectionCallback callback,
int interrogatingPid, long interrogatingTid) {
- if (mViewAncestor.get() != null) {
+ ViewRootImpl viewRootImpl = mViewRootImpl.get();
+ if (viewRootImpl != null && viewRootImpl.mView != null) {
getAccessibilityInteractionController()
- .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityId,
+ .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId,
interactionId, callback, interrogatingPid, interrogatingTid);
}
}
- public void performAccessibilityAction(int accessibilityId, int action,
+ public void performAccessibilityAction(long accessibilityNodeId, int action,
int interactionId, IAccessibilityInteractionConnectionCallback callback,
int interogatingPid, long interrogatingTid) {
- if (mViewAncestor.get() != null) {
+ ViewRootImpl viewRootImpl = mViewRootImpl.get();
+ if (viewRootImpl != null && viewRootImpl.mView != null) {
getAccessibilityInteractionController()
- .performAccessibilityActionClientThread(accessibilityId, action, interactionId,
- callback, interogatingPid, interrogatingTid);
+ .performAccessibilityActionClientThread(accessibilityNodeId, action,
+ interactionId, callback, interogatingPid, interrogatingTid);
}
}
public void findAccessibilityNodeInfoByViewId(int viewId,
int interactionId, IAccessibilityInteractionConnectionCallback callback,
int interrogatingPid, long interrogatingTid) {
- if (mViewAncestor.get() != null) {
+ ViewRootImpl viewRootImpl = mViewRootImpl.get();
+ if (viewRootImpl != null && viewRootImpl.mView != null) {
getAccessibilityInteractionController()
.findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback,
interrogatingPid, interrogatingTid);
}
}
- public void findAccessibilityNodeInfosByViewText(String text, int accessibilityId,
+ public void findAccessibilityNodeInfosByText(String text, long accessibilityNodeId,
int interactionId, IAccessibilityInteractionConnectionCallback callback,
int interrogatingPid, long interrogatingTid) {
- if (mViewAncestor.get() != null) {
+ ViewRootImpl viewRootImpl = mViewRootImpl.get();
+ if (viewRootImpl != null && viewRootImpl.mView != null) {
getAccessibilityInteractionController()
- .findAccessibilityNodeInfosByViewTextClientThread(text, accessibilityId,
+ .findAccessibilityNodeInfosByTextClientThread(text, accessibilityNodeId,
interactionId, callback, interrogatingPid, interrogatingTid);
}
}
@@ -4589,14 +4594,18 @@
}
}
- public void findAccessibilityNodeInfoByAccessibilityIdClientThread(int accessibilityId,
- int interactionId, IAccessibilityInteractionConnectionCallback callback,
- int interrogatingPid, long interrogatingTid) {
+ public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
+ long accessibilityNodeId, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
+ long interrogatingTid) {
Message message = Message.obtain();
message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
- message.arg1 = accessibilityId;
- message.arg2 = interactionId;
- message.obj = callback;
+ SomeArgs args = mPool.acquire();
+ args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+ args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
+ args.argi3 = interactionId;
+ args.arg1 = callback;
+ message.obj = args;
// If the interrogation is performed by the same thread as the main UI
// thread in this process, set the message as a static reference so
// after this call completes the same thread but in the interrogating
@@ -4604,23 +4613,31 @@
if (interrogatingPid == Process.myPid()
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
message.setTarget(ViewRootImpl.this);
- AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
+ AccessibilityInteractionClient.getInstanceForThread(
+ interrogatingTid).setSameThreadMessage(message);
} else {
sendMessage(message);
}
}
public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
- final int accessibilityId = message.arg1;
- final int interactionId = message.arg2;
+ SomeArgs args = (SomeArgs) message.obj;
+ final int accessibilityViewId = args.argi1;
+ final int virtualDescendantId = args.argi2;
+ final int interactionId = args.argi3;
final IAccessibilityInteractionConnectionCallback callback =
- (IAccessibilityInteractionConnectionCallback) message.obj;
-
+ (IAccessibilityInteractionConnectionCallback) args.arg1;
+ mPool.release(args);
AccessibilityNodeInfo info = null;
try {
- View target = findViewByAccessibilityId(accessibilityId);
- if (target != null) {
- info = target.createAccessibilityNodeInfo();
+ View target = findViewByAccessibilityId(accessibilityViewId);
+ if (target != null && target.getVisibility() == View.VISIBLE) {
+ AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
+ if (provider != null) {
+ info = provider.createAccessibilityNodeInfo(virtualDescendantId);
+ } else if (virtualDescendantId == View.NO_ID) {
+ info = target.createAccessibilityNodeInfo();
+ }
}
} finally {
try {
@@ -4646,7 +4663,8 @@
if (interrogatingPid == Process.myPid()
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
message.setTarget(ViewRootImpl.this);
- AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
+ AccessibilityInteractionClient.getInstanceForThread(
+ interrogatingTid).setSameThreadMessage(message);
} else {
sendMessage(message);
}
@@ -4674,16 +4692,17 @@
}
}
- public void findAccessibilityNodeInfosByViewTextClientThread(String text,
- int accessibilityViewId, int interactionId,
+ public void findAccessibilityNodeInfosByTextClientThread(String text,
+ long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
long interrogatingTid) {
Message message = Message.obtain();
- message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT;
+ message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT;
SomeArgs args = mPool.acquire();
args.arg1 = text;
- args.argi1 = accessibilityViewId;
- args.argi2 = interactionId;
+ args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+ args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
+ args.argi3 = interactionId;
args.arg2 = callback;
message.obj = args;
// If the interrogation is performed by the same thread as the main UI
@@ -4693,53 +4712,64 @@
if (interrogatingPid == Process.myPid()
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
message.setTarget(ViewRootImpl.this);
- AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
+ AccessibilityInteractionClient.getInstanceForThread(
+ interrogatingTid).setSameThreadMessage(message);
} else {
sendMessage(message);
}
}
- public void findAccessibilityNodeInfosByViewTextUiThread(Message message) {
+ public void findAccessibilityNodeInfosByTextUiThread(Message message) {
SomeArgs args = (SomeArgs) message.obj;
final String text = (String) args.arg1;
final int accessibilityViewId = args.argi1;
- final int interactionId = args.argi2;
+ final int virtualDescendantId = args.argi2;
+ final int interactionId = args.argi3;
final IAccessibilityInteractionConnectionCallback callback =
(IAccessibilityInteractionConnectionCallback) args.arg2;
mPool.release(args);
-
List<AccessibilityNodeInfo> infos = null;
try {
- ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList;
- foundViews.clear();
-
- View root = null;
+ View target = null;
if (accessibilityViewId != View.NO_ID) {
- root = findViewByAccessibilityId(accessibilityViewId);
+ target = findViewByAccessibilityId(accessibilityViewId);
} else {
- root = ViewRootImpl.this.mView;
+ target = ViewRootImpl.this.mView;
}
-
- if (root == null || root.getVisibility() != View.VISIBLE) {
- return;
- }
-
- root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
- | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
- if (foundViews.isEmpty()) {
- return;
- }
-
- infos = mTempAccessibilityNodeInfoList;
- infos.clear();
-
- final int viewCount = foundViews.size();
- for (int i = 0; i < viewCount; i++) {
- View foundView = foundViews.get(i);
- if (foundView.getVisibility() == View.VISIBLE) {
- infos.add(foundView.createAccessibilityNodeInfo());
+ if (target != null && target.getVisibility() == View.VISIBLE) {
+ AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
+ if (provider != null) {
+ infos = provider.findAccessibilityNodeInfosByText(text,
+ virtualDescendantId);
+ } else if (virtualDescendantId == View.NO_ID) {
+ ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList;
+ foundViews.clear();
+ target.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
+ | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
+ | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
+ if (!foundViews.isEmpty()) {
+ infos = mTempAccessibilityNodeInfoList;
+ infos.clear();
+ final int viewCount = foundViews.size();
+ for (int i = 0; i < viewCount; i++) {
+ View foundView = foundViews.get(i);
+ if (foundView.getVisibility() == View.VISIBLE) {
+ provider = foundView.getAccessibilityNodeProvider();
+ if (provider != null) {
+ List<AccessibilityNodeInfo> infosFromProvider =
+ provider.findAccessibilityNodeInfosByText(text,
+ virtualDescendantId);
+ if (infosFromProvider != null) {
+ infos.addAll(infosFromProvider);
+ }
+ } else {
+ infos.add(foundView.createAccessibilityNodeInfo());
+ }
+ }
+ }
+ }
}
- }
+ }
} finally {
try {
callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
@@ -4749,15 +4779,16 @@
}
}
- public void performAccessibilityActionClientThread(int accessibilityId, int action,
+ public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
int interactionId, IAccessibilityInteractionConnectionCallback callback,
int interogatingPid, long interrogatingTid) {
Message message = Message.obtain();
message.what = DO_PERFORM_ACCESSIBILITY_ACTION;
+ message.arg1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+ message.arg2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
SomeArgs args = mPool.acquire();
- args.argi1 = accessibilityId;
- args.argi2 = action;
- args.argi3 = interactionId;
+ args.argi1 = action;
+ args.argi2 = interactionId;
args.arg1 = callback;
message.obj = args;
// If the interrogation is performed by the same thread as the main UI
@@ -4767,36 +4798,60 @@
if (interogatingPid == Process.myPid()
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
message.setTarget(ViewRootImpl.this);
- AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
+ AccessibilityInteractionClient.getInstanceForThread(
+ interrogatingTid).setSameThreadMessage(message);
} else {
sendMessage(message);
}
}
public void perfromAccessibilityActionUiThread(Message message) {
+ final int accessibilityViewId = message.arg1;
+ final int virtualDescendantId = message.arg2;
SomeArgs args = (SomeArgs) message.obj;
- final int accessibilityId = args.argi1;
- final int action = args.argi2;
- final int interactionId = args.argi3;
+ final int action = args.argi1;
+ final int interactionId = args.argi2;
final IAccessibilityInteractionConnectionCallback callback =
(IAccessibilityInteractionConnectionCallback) args.arg1;
mPool.release(args);
-
boolean succeeded = false;
try {
- switch (action) {
- case AccessibilityNodeInfo.ACTION_FOCUS: {
- succeeded = performActionFocus(accessibilityId);
- } break;
- case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
- succeeded = performActionClearFocus(accessibilityId);
- } break;
- case AccessibilityNodeInfo.ACTION_SELECT: {
- succeeded = performActionSelect(accessibilityId);
- } break;
- case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {
- succeeded = performActionClearSelection(accessibilityId);
- } break;
+ View target = findViewByAccessibilityId(accessibilityViewId);
+ if (target != null && target.getVisibility() == View.VISIBLE) {
+ AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
+ if (provider != null) {
+ succeeded = provider.performAccessibilityAction(action,
+ virtualDescendantId);
+ } else if (virtualDescendantId == View.NO_ID) {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_FOCUS: {
+ if (!target.hasFocus()) {
+ // Get out of touch mode since accessibility
+ // wants to move focus around.
+ ensureTouchMode(false);
+ succeeded = target.requestFocus();
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
+ if (target.hasFocus()) {
+ target.clearFocus();
+ succeeded = !target.isFocused();
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_SELECT: {
+ if (!target.isSelected()) {
+ target.setSelected(true);
+ succeeded = target.isSelected();
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {
+ if (target.isSelected()) {
+ target.setSelected(false);
+ succeeded = !target.isSelected();
+ }
+ } break;
+ }
+ }
}
} finally {
try {
@@ -4807,52 +4862,6 @@
}
}
- private boolean performActionFocus(int accessibilityId) {
- View target = findViewByAccessibilityId(accessibilityId);
- if (target == null || target.getVisibility() != View.VISIBLE) {
- return false;
- }
- // Get out of touch mode since accessibility wants to move focus around.
- ensureTouchMode(false);
- return target.requestFocus();
- }
-
- private boolean performActionClearFocus(int accessibilityId) {
- View target = findViewByAccessibilityId(accessibilityId);
- if (target == null || target.getVisibility() != View.VISIBLE) {
- return false;
- }
- if (!target.isFocused()) {
- return false;
- }
- target.clearFocus();
- return !target.isFocused();
- }
-
- private boolean performActionSelect(int accessibilityId) {
- View target = findViewByAccessibilityId(accessibilityId);
- if (target == null || target.getVisibility() != View.VISIBLE) {
- return false;
- }
- if (target.isSelected()) {
- return false;
- }
- target.setSelected(true);
- return target.isSelected();
- }
-
- private boolean performActionClearSelection(int accessibilityId) {
- View target = findViewByAccessibilityId(accessibilityId);
- if (target == null || target.getVisibility() != View.VISIBLE) {
- return false;
- }
- if (!target.isSelected()) {
- return false;
- }
- target.setSelected(false);
- return !target.isSelected();
- }
-
private View findViewByAccessibilityId(int accessibilityId) {
View root = ViewRootImpl.this.mView;
if (root == null) {
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 86dd9df..c6f778f 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -876,7 +876,7 @@
record.mParcelableData = parcel.readParcelable(null);
parcel.readList(record.mText, null);
record.mSourceWindowId = parcel.readInt();
- record.mSourceViewId = parcel.readInt();
+ record.mSourceNodeId = parcel.readLong();
record.mSealed = (parcel.readInt() == 1);
}
@@ -930,7 +930,7 @@
parcel.writeParcelable(record.mParcelableData, flags);
parcel.writeList(record.mText);
parcel.writeInt(record.mSourceWindowId);
- parcel.writeInt(record.mSourceViewId);
+ parcel.writeLong(record.mSourceNodeId);
parcel.writeInt(record.mSealed ? 1 : 0);
}
@@ -951,7 +951,7 @@
if (DEBUG) {
builder.append("\n");
builder.append("; sourceWindowId: ").append(mSourceWindowId);
- builder.append("; sourceViewId: ").append(mSourceViewId);
+ builder.append("; mSourceNodeId: ").append(mSourceNodeId);
for (int i = 0; i < mRecords.size(); i++) {
AccessibilityRecord record = mRecords.get(i);
builder.append(" Record ");
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 25b980b..5f2990a 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -21,6 +21,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.util.LongSparseArray;
import java.util.Collections;
import java.util.List;
@@ -65,7 +66,8 @@
private static final Object sStaticLock = new Object();
- private static AccessibilityInteractionClient sInstance;
+ private static final LongSparseArray<AccessibilityInteractionClient> sClients =
+ new LongSparseArray<AccessibilityInteractionClient>();
private final AtomicInteger mInteractionIdCounter = new AtomicInteger();
@@ -84,17 +86,36 @@
private final Rect mTempBounds = new Rect();
/**
- * @return The singleton of this class.
+ * @return The client for the current thread.
*/
public static AccessibilityInteractionClient getInstance() {
+ final long threadId = Thread.currentThread().getId();
+ return getInstanceForThread(threadId);
+ }
+
+ /**
+ * <strong>Note:</strong> We keep one instance per interrogating thread since
+ * the instance contains state which can lead to undesired thread interleavings.
+ * We do not have a thread local variable since other threads should be able to
+ * look up the correct client knowing a thread id. See ViewRootImpl for details.
+ *
+ * @return The client for a given <code>threadId</code>.
+ */
+ public static AccessibilityInteractionClient getInstanceForThread(long threadId) {
synchronized (sStaticLock) {
- if (sInstance == null) {
- sInstance = new AccessibilityInteractionClient();
+ AccessibilityInteractionClient client = sClients.get(threadId);
+ if (client == null) {
+ client = new AccessibilityInteractionClient();
+ sClients.put(threadId, client);
}
- return sInstance;
+ return client;
}
}
+ private AccessibilityInteractionClient() {
+ /* reducing constructor visibility */
+ }
+
/**
* Sets the message to be processed if the interacted view hierarchy
* and the interacting client are running in the same thread.
@@ -113,16 +134,17 @@
*
* @param connection A connection for interacting with the system.
* @param accessibilityWindowId A unique window id.
- * @param accessibilityViewId A unique View accessibility id.
+ * @param accessibilityNodeId A unique node accessibility id
+ * (accessibility view and virtual descendant id).
* @return An {@link AccessibilityNodeInfo} if found, null otherwise.
*/
public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(
IAccessibilityServiceConnection connection, int accessibilityWindowId,
- int accessibilityViewId) {
+ long accessibilityNodeId) {
try {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
- accessibilityWindowId, accessibilityViewId, interactionId, this,
+ accessibilityWindowId, accessibilityNodeId, interactionId, this,
Thread.currentThread().getId());
// If the scale is zero the call has failed.
if (windowScale > 0) {
@@ -173,16 +195,19 @@
* @param text The searched text.
* @return A list of found {@link AccessibilityNodeInfo}s.
*/
- public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow(
+ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByTextInActiveWindow(
IAccessibilityServiceConnection connection, String text) {
try {
final int interactionId = mInteractionIdCounter.getAndIncrement();
- final float windowScale = connection.findAccessibilityNodeInfosByViewTextInActiveWindow(
+ final float windowScale = connection.findAccessibilityNodeInfosByTextInActiveWindow(
text, interactionId, this, Thread.currentThread().getId());
// If the scale is zero the call has failed.
if (windowScale > 0) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
+ if (infos == null) {
+ return Collections.emptyList();
+ }
finalizeAccessibilityNodeInfos(infos, connection, windowScale);
return infos;
}
@@ -201,17 +226,17 @@
* @param connection A connection for interacting with the system.
* @param text The searched text.
* @param accessibilityWindowId A unique window id.
- * @param accessibilityViewId A unique View accessibility id from where to start the search.
- * Use {@link android.view.View#NO_ID} to start from the root.
+ * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id) from
+ * where to start the search. Use {@link android.view.View#NO_ID} to start from the root.
* @return A list of found {@link AccessibilityNodeInfo}s.
*/
- public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(
+ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(
IAccessibilityServiceConnection connection, String text, int accessibilityWindowId,
- int accessibilityViewId) {
+ long accessibilityNodeId) {
try {
final int interactionId = mInteractionIdCounter.getAndIncrement();
- final float windowScale = connection.findAccessibilityNodeInfosByViewText(text,
- accessibilityWindowId, accessibilityViewId, interactionId, this,
+ final float windowScale = connection.findAccessibilityNodeInfosByText(text,
+ accessibilityWindowId, accessibilityNodeId, interactionId, this,
Thread.currentThread().getId());
// If the scale is zero the call has failed.
if (windowScale > 0) {
@@ -231,16 +256,16 @@
*
* @param connection A connection for interacting with the system.
* @param accessibilityWindowId The id of the window.
- * @param accessibilityViewId A unique View accessibility id.
+ * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id).
* @param action The action to perform.
* @return Whether the action was performed.
*/
public boolean performAccessibilityAction(IAccessibilityServiceConnection connection,
- int accessibilityWindowId, int accessibilityViewId, int action) {
+ int accessibilityWindowId, long accessibilityNodeId, int action) {
try {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final boolean success = connection.performAccessibilityAction(
- accessibilityWindowId, accessibilityViewId, action, interactionId, this,
+ accessibilityWindowId, accessibilityNodeId, action, interactionId, this,
Thread.currentThread().getId());
if (success) {
return getPerformAccessibilityActionResult(interactionId);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 7671312..e792666 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -21,7 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import android.util.SparseIntArray;
+import android.util.SparseLongArray;
import android.view.View;
import java.util.Collections;
@@ -97,6 +97,59 @@
private static final int PROPERTY_SCROLLABLE = 0x00000200;
+ /**
+ * Bits that provide the id of a virtual descendant of a view.
+ */
+ private static final long VIRTUAL_DESCENDANT_ID_MASK = 0xffffffff00000000L;
+
+ /**
+ * Bit shift of {@link #VIRTUAL_DESCENDANT_ID_MASK} to get to the id for a
+ * virtual descendant of a view. Such a descendant does not exist in the view
+ * hierarchy and is only reported via the accessibility APIs.
+ */
+ private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32;
+
+ /**
+ * Gets the accessibility view id which identifies a View in the view three.
+ *
+ * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
+ * @return The accessibility view id part of the node id.
+ *
+ * @hide
+ */
+ public static int getAccessibilityViewId(long accessibilityNodeId) {
+ return (int) accessibilityNodeId;
+ }
+
+ /**
+ * Gets the virtual descendant id which identifies an imaginary view in a
+ * containing View.
+ *
+ * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
+ * @return The virtual view id part of the node id.
+ *
+ * @hide
+ */
+ public static int getVirtualDescendantId(long accessibilityNodeId) {
+ return (int) ((accessibilityNodeId & VIRTUAL_DESCENDANT_ID_MASK)
+ >> VIRTUAL_DESCENDANT_ID_SHIFT);
+ }
+
+ /**
+ * Makes a node id by shifting the <code>virtualDescendantId</code>
+ * by {@link #VIRTUAL_DESCENDANT_ID_SHIFT} and taking
+ * the bitwise or with the <code>accessibilityViewId</code>.
+ *
+ * @param accessibilityViewId A View accessibility id.
+ * @param virtualDescendantId A virtual descendant id.
+ * @return The node id.
+ *
+ * @hide
+ */
+ public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) {
+ return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
+ }
+
// Housekeeping.
private static final int MAX_POOL_SIZE = 50;
private static final Object sPoolLock = new Object();
@@ -107,9 +160,9 @@
private boolean mSealed;
// Data.
- private int mAccessibilityViewId = View.NO_ID;
- private int mAccessibilityWindowId = View.NO_ID;
- private int mParentAccessibilityViewId = View.NO_ID;
+ private long mSourceNodeId = makeNodeId(View.NO_ID, View.NO_ID);
+ private int mWindowId = View.NO_ID;
+ private long mParentNodeId = makeNodeId(View.NO_ID, View.NO_ID);
private int mBooleanProperties;
private final Rect mBoundsInParent = new Rect();
private final Rect mBoundsInScreen = new Rect();
@@ -119,7 +172,7 @@
private CharSequence mText;
private CharSequence mContentDescription;
- private SparseIntArray mChildAccessibilityIds = new SparseIntArray();
+ private SparseLongArray mChildIds = new SparseLongArray();
private int mActions;
private IAccessibilityServiceConnection mConnection;
@@ -133,13 +186,43 @@
/**
* Sets the source.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
*
* @param source The info source.
*/
public void setSource(View source) {
+ setSource(source, View.NO_ID);
+ }
+
+ /**
+ * Sets the source to be a virtual descendant of the given <code>root</code>.
+ * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
+ * is set as the source.
+ * <p>
+ * A virtual descendant is an imaginary View that is reported as a part of the view
+ * hierarchy for accessibility purposes. This enables custom views that draw complex
+ * content to report 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 setSource(View root, int virtualDescendantId) {
enforceNotSealed();
- mAccessibilityViewId = source.getAccessibilityViewId();
- mAccessibilityWindowId = source.getAccessibilityWindowId();
+ mWindowId = (root != null) ? root.getAccessibilityWindowId() : View.NO_ID;
+ final int rootAccessibilityViewId =
+ (root != null) ? root.getAccessibilityViewId() : View.NO_ID;
+ mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
}
/**
@@ -148,7 +231,7 @@
* @return The window id.
*/
public int getWindowId() {
- return mAccessibilityWindowId;
+ return mWindowId;
}
/**
@@ -157,7 +240,7 @@
* @return The child count.
*/
public int getChildCount() {
- return mChildAccessibilityIds.size();
+ return mChildIds.size();
}
/**
@@ -176,21 +259,20 @@
*/
public AccessibilityNodeInfo getChild(int index) {
enforceSealed();
- final int childAccessibilityViewId = mChildAccessibilityIds.get(index);
- if (!canPerformRequestOverConnection(childAccessibilityViewId)) {
+ if (!canPerformRequestOverConnection(mSourceNodeId)) {
return null;
}
+ final long childId = mChildIds.get(index);
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
- return client.findAccessibilityNodeInfoByAccessibilityId(mConnection,
- mAccessibilityWindowId, childAccessibilityViewId);
+ return client.findAccessibilityNodeInfoByAccessibilityId(mConnection, mWindowId, childId);
}
/**
* Adds a child.
* <p>
- * <strong>Note:</strong> Cannot be called from an
- * {@link android.accessibilityservice.AccessibilityService}.
- * This class is made immutable before being delivered to an AccessibilityService.
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
* </p>
*
* @param child The child.
@@ -198,10 +280,30 @@
* @throws IllegalStateException If called from an AccessibilityService.
*/
public void addChild(View child) {
+ addChild(child, View.NO_ID);
+ }
+
+ /**
+ * Adds a virtual child which is a descendant of the given <code>root</code>.
+ * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
+ * is added as a child.
+ * <p>
+ * A virtual descendant is an imaginary View that is reported as a part of the view
+ * hierarchy for accessibility purposes. This enables custom views that draw complex
+ * content to report them selves as a tree of virtual views, thus conveying their
+ * logical structure.
+ * </p>
+ *
+ * @param root The root of the virtual subtree.
+ * @param virtualDescendantId The id of the virtual child.
+ */
+ public void addChild(View root, int virtualDescendantId) {
enforceNotSealed();
- final int childAccessibilityViewId = child.getAccessibilityViewId();
- final int index = mChildAccessibilityIds.size();
- mChildAccessibilityIds.put(index, childAccessibilityViewId);
+ final int index = mChildIds.size();
+ final int rootAccessibilityViewId =
+ (root != null) ? root.getAccessibilityViewId() : View.NO_ID;
+ final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
+ mChildIds.put(index, childNodeId);
}
/**
@@ -249,12 +351,11 @@
*/
public boolean performAction(int action) {
enforceSealed();
- if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
+ if (!canPerformRequestOverConnection(mSourceNodeId)) {
return false;
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
- return client.performAccessibilityAction(mConnection, mAccessibilityWindowId,
- mAccessibilityViewId, action);
+ return client.performAccessibilityAction(mConnection, mWindowId, mSourceNodeId, action);
}
/**
@@ -273,12 +374,11 @@
*/
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
enforceSealed();
- if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
+ if (!canPerformRequestOverConnection(mSourceNodeId)) {
return Collections.emptyList();
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
- return client.findAccessibilityNodeInfosByViewText(mConnection, text,
- mAccessibilityWindowId, mAccessibilityViewId);
+ return client.findAccessibilityNodeInfosByText(mConnection, text, mWindowId, mSourceNodeId);
}
/**
@@ -293,12 +393,12 @@
*/
public AccessibilityNodeInfo getParent() {
enforceSealed();
- if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
+ if (!canPerformRequestOverConnection(mSourceNodeId)) {
return null;
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnection,
- mAccessibilityWindowId, mParentAccessibilityViewId);
+ mWindowId, mParentNodeId);
}
/**
@@ -314,8 +414,33 @@
* @throws IllegalStateException If called from an AccessibilityService.
*/
public void setParent(View parent) {
+ setParent(parent, View.NO_ID);
+ }
+
+ /**
+ * Sets the parent to be a virtual descendant of the given <code>root</code>.
+ * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
+ * is set as the parent.
+ * <p>
+ * A virtual descendant is an imaginary View that is reported as a part of the view
+ * hierarchy for accessibility purposes. This enables custom views that draw complex
+ * content to report them selves as a tree of virtual views, thus conveying their
+ * logical structure.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param root The root of the virtual subtree.
+ * @param virtualDescendantId The id of the virtual descendant.
+ */
+ public void setParent(View root, int virtualDescendantId) {
enforceNotSealed();
- mParentAccessibilityViewId = parent.getAccessibilityViewId();
+ final int rootAccessibilityViewId =
+ (root != null) ? root.getAccessibilityViewId() : View.NO_ID;
+ mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
}
/**
@@ -827,6 +952,7 @@
* 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)
@@ -838,6 +964,22 @@
}
/**
+ * 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.
@@ -907,15 +1049,15 @@
parcel.writeStrongBinder(mConnection.asBinder());
}
parcel.writeInt(isSealed() ? 1 : 0);
- parcel.writeInt(mAccessibilityViewId);
- parcel.writeInt(mAccessibilityWindowId);
- parcel.writeInt(mParentAccessibilityViewId);
+ parcel.writeLong(mSourceNodeId);
+ parcel.writeInt(mWindowId);
+ parcel.writeLong(mParentNodeId);
- SparseIntArray childIds = mChildAccessibilityIds;
+ SparseLongArray childIds = mChildIds;
final int childIdsSize = childIds.size();
parcel.writeInt(childIdsSize);
for (int i = 0; i < childIdsSize; i++) {
- parcel.writeInt(childIds.valueAt(i));
+ parcel.writeLong(childIds.valueAt(i));
}
parcel.writeInt(mBoundsInParent.top);
@@ -950,9 +1092,9 @@
private void init(AccessibilityNodeInfo other) {
mSealed = other.mSealed;
mConnection = other.mConnection;
- mAccessibilityViewId = other.mAccessibilityViewId;
- mParentAccessibilityViewId = other.mParentAccessibilityViewId;
- mAccessibilityWindowId = other.mAccessibilityWindowId;
+ mSourceNodeId = other.mSourceNodeId;
+ mParentNodeId = other.mParentNodeId;
+ mWindowId = other.mWindowId;
mBoundsInParent.set(other.mBoundsInParent);
mBoundsInScreen.set(other.mBoundsInScreen);
mPackageName = other.mPackageName;
@@ -961,7 +1103,7 @@
mContentDescription = other.mContentDescription;
mActions= other.mActions;
mBooleanProperties = other.mBooleanProperties;
- mChildAccessibilityIds = other.mChildAccessibilityIds.clone();
+ mChildIds = other.mChildIds.clone();
}
/**
@@ -975,14 +1117,14 @@
parcel.readStrongBinder());
}
mSealed = (parcel.readInt() == 1);
- mAccessibilityViewId = parcel.readInt();
- mAccessibilityWindowId = parcel.readInt();
- mParentAccessibilityViewId = parcel.readInt();
+ mSourceNodeId = parcel.readLong();
+ mWindowId = parcel.readInt();
+ mParentNodeId = parcel.readLong();
- SparseIntArray childIds = mChildAccessibilityIds;
+ SparseLongArray childIds = mChildIds;
final int childrenSize = parcel.readInt();
for (int i = 0; i < childrenSize; i++) {
- final int childId = parcel.readInt();
+ final long childId = parcel.readLong();
childIds.put(i, childId);
}
@@ -1012,10 +1154,10 @@
private void clear() {
mSealed = false;
mConnection = null;
- mAccessibilityViewId = View.NO_ID;
- mParentAccessibilityViewId = View.NO_ID;
- mAccessibilityWindowId = View.NO_ID;
- mChildAccessibilityIds.clear();
+ mSourceNodeId = makeNodeId(View.NO_ID, View.NO_ID);
+ mParentNodeId = makeNodeId(View.NO_ID, View.NO_ID);
+ mWindowId = View.NO_ID;
+ mChildIds.clear();
mBoundsInParent.set(0, 0, 0, 0);
mBoundsInScreen.set(0, 0, 0, 0);
mBooleanProperties = 0;
@@ -1047,9 +1189,9 @@
}
}
- private boolean canPerformRequestOverConnection(int accessibilityViewId) {
- return (mAccessibilityWindowId != View.NO_ID
- && accessibilityViewId != View.NO_ID
+ private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
+ return (mWindowId != View.NO_ID
+ && getAccessibilityViewId(accessibilityNodeId) != View.NO_ID
&& mConnection != null);
}
@@ -1065,10 +1207,10 @@
return false;
}
AccessibilityNodeInfo other = (AccessibilityNodeInfo) object;
- if (mAccessibilityViewId != other.mAccessibilityViewId) {
+ if (mSourceNodeId != other.mSourceNodeId) {
return false;
}
- if (mAccessibilityWindowId != other.mAccessibilityWindowId) {
+ if (mWindowId != other.mWindowId) {
return false;
}
return true;
@@ -1078,8 +1220,9 @@
public int hashCode() {
final int prime = 31;
int result = 1;
- result = prime * result + mAccessibilityViewId;
- result = prime * result + mAccessibilityWindowId;
+ result = prime * result + getAccessibilityViewId(mSourceNodeId);
+ result = prime * result + getVirtualDescendantId(mSourceNodeId);
+ result = prime * result + mWindowId;
return result;
}
@@ -1089,9 +1232,10 @@
builder.append(super.toString());
if (DEBUG) {
- builder.append("; accessibilityId: " + mAccessibilityViewId);
- builder.append("; parentAccessibilityId: " + mParentAccessibilityViewId);
- SparseIntArray childIds = mChildAccessibilityIds;
+ builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
+ builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
+ builder.append("; mParentNodeId: " + mParentNodeId);
+ SparseLongArray childIds = mChildIds;
builder.append("; childAccessibilityIds: [");
for (int i = 0, count = childIds.size(); i < count; i++) {
builder.append(childIds.valueAt(i));
diff --git a/core/java/android/view/accessibility/AccessibilityNodeProvider.java b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
new file mode 100644
index 0000000..5890417
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
@@ -0,0 +1,130 @@
+/*
+ * 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.AccessibilityService;
+import android.view.View;
+
+import java.util.List;
+
+/**
+ * This class is the contract a client should implement to enable support of a
+ * virtual view hierarchy rooted at a given view for accessibility purposes. A virtual
+ * view hierarchy is a tree of imaginary Views that is reported as a part of the view
+ * hierarchy when an {@link AccessibilityService} explores the window content.
+ * Since the virtual View tree does not exist this class is responsible for
+ * managing the {@link AccessibilityNodeInfo}s describing that tree to accessibility
+ * services.
+ * </p>
+ * <p>
+ * The main use case of these APIs is to enable a custom view that draws complex content,
+ * for example a monthly calendar grid, to be presented as a tree of logical nodes,
+ * for example month days each containing events, thus conveying its logical structure.
+ * <p>
+ * <p>
+ * A typical use case is to override {@link View#getAccessibilityNodeProvider()} of the
+ * View that is a root of a virtual View hierarchy to return an instance of this class.
+ * In such a case this instance is responsible for managing {@link AccessibilityNodeInfo}s
+ * describing the virtual sub-tree rooted at the View including the one representing the
+ * View itself. Similarly the returned instance is responsible for performing accessibility
+ * actions on any virtual view or the root view itself. For example:
+ * </p>
+ * <pre>
+ * getAccessibilityNodeProvider(
+ * if (mAccessibilityNodeProvider == null) {
+ * mAccessibilityNodeProvider = new AccessibilityNodeProvider() {
+ * public boolean performAccessibilityAction(int action, int virtualDescendantId) {
+ * // Implementation.
+ * return false;
+ * }
+ *
+ * public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text, int virtualDescendantId) {
+ * // Implementation.
+ * return null;
+ * }
+ *
+ * public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualDescendantId) {
+ * // Implementation.
+ * return null;
+ * }
+ * });
+ * return mAccessibilityNodeProvider;
+ * </pre>
+ */
+public abstract class AccessibilityNodeProvider {
+
+ /**
+ * Returns an {@link AccessibilityNodeInfo} representing a virtual view,
+ * i.e. a descendant of the host View, with the given <code>virtualViewId</code>
+ * or the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ * <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>
+ * The implementer is responsible for obtaining an accessibility node info from the
+ * pool of reusable instances and setting the desired properties of the node info
+ * before returning it.
+ * </p>
+ *
+ * @param virtualViewId A client defined virtual view id.
+ * @return A populated {@link AccessibilityNodeInfo} for a virtual descendant or the
+ * host View.
+ *
+ * @see AccessibilityNodeInfo
+ */
+ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
+ return null;
+ }
+
+ /**
+ * Performs an accessibility action on a virtual view, i.e. a descendant of the
+ * host View, with the given <code>virtualViewId</code> or the host View itself
+ * if <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ *
+ * @param action The action to perform.
+ * @param virtualViewId A client defined virtual view id.
+ * @return True if the action was performed.
+ *
+ * @see #createAccessibilityNodeInfo(int)
+ * @see AccessibilityNodeInfo
+ */
+ public boolean performAccessibilityAction(int action, int virtualViewId) {
+ return false;
+ }
+
+ /**
+ * Finds {@link AccessibilityNodeInfo}s by text. The match is case insensitive
+ * containment. The search is relative to the virtual view, i.e. a descendant of the
+ * host View, with the given <code>virtualViewId</code> or the host View itself
+ * <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ *
+ * @param virtualViewId A client defined virtual view id which defined
+ * the root of the tree in which to perform the search.
+ * @param text The searched text.
+ * @return A list of node info.
+ *
+ * @see #createAccessibilityNodeInfo(int)
+ * @see AccessibilityNodeInfo
+ */
+ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text,
+ int virtualViewId) {
+ return null;
+ }
+}
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index a4e0688..f3ca0d5 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -78,7 +78,7 @@
int mAddedCount= UNDEFINED;
int mRemovedCount = UNDEFINED;
- int mSourceViewId = View.NO_ID;
+ long mSourceNodeId = AccessibilityNodeInfo.makeNodeId(View.NO_ID, View.NO_ID);
int mSourceWindowId = View.NO_ID;
CharSequence mClassName;
@@ -103,14 +103,28 @@
* @throws IllegalStateException If called from an AccessibilityService.
*/
public void setSource(View source) {
+ setSource(source, View.NO_ID);
+ }
+
+ /**
+ * Sets the source to be a virtual descendant of the given <code>root</code>.
+ * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
+ * is set as the source.
+ * <p>
+ * A virtual descendant is an imaginary View that is reported as a part of the view
+ * hierarchy for accessibility purposes. This enables custom views that draw complex
+ * content to report them selves as a tree of virtual views, thus conveying their
+ * logical structure.
+ * </p>
+ *
+ * @param root The root of the virtual subtree.
+ * @param virtualDescendantId The id of the virtual descendant.
+ */
+ public void setSource(View root, int virtualDescendantId) {
enforceNotSealed();
- if (source != null) {
- mSourceWindowId = source.getAccessibilityWindowId();
- mSourceViewId = source.getAccessibilityViewId();
- } else {
- mSourceWindowId = View.NO_ID;
- mSourceViewId = View.NO_ID;
- }
+ mSourceWindowId = (root != null) ? root.getAccessibilityWindowId() : View.NO_ID;
+ final int rootViewId = (root != null) ? root.getAccessibilityViewId() : View.NO_ID;
+ mSourceNodeId = AccessibilityNodeInfo.makeNodeId(rootViewId, virtualDescendantId);
}
/**
@@ -125,12 +139,13 @@
*/
public AccessibilityNodeInfo getSource() {
enforceSealed();
- if (mSourceWindowId == View.NO_ID || mSourceViewId == View.NO_ID || mConnection == null) {
+ if (mConnection == null || mSourceWindowId == View.NO_ID
+ || AccessibilityNodeInfo.getAccessibilityViewId(mSourceNodeId) == View.NO_ID) {
return null;
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnection, mSourceWindowId,
- mSourceViewId);
+ mSourceNodeId);
}
/**
@@ -395,6 +410,7 @@
public int getMaxScrollX() {
return mMaxScrollX;
}
+
/**
* Sets the max scroll offset of the source left edge in pixels.
*
@@ -707,7 +723,7 @@
mParcelableData = record.mParcelableData;
mText.addAll(record.mText);
mSourceWindowId = record.mSourceWindowId;
- mSourceViewId = record.mSourceViewId;
+ mSourceNodeId = record.mSourceNodeId;
mConnection = record.mConnection;
}
@@ -732,7 +748,7 @@
mBeforeText = null;
mParcelableData = null;
mText.clear();
- mSourceViewId = View.NO_ID;
+ mSourceNodeId = AccessibilityNodeInfo.makeNodeId(View.NO_ID, View.NO_ID);
mSourceWindowId = View.NO_ID;
}
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index 535d594..a90c427 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -27,7 +27,7 @@
*/
oneway interface IAccessibilityInteractionConnection {
- void findAccessibilityNodeInfoByAccessibilityId(int accessibilityViewId, int interactionId,
+ void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback,
int interrogatingPid, long interrogatingTid);
@@ -35,11 +35,11 @@
IAccessibilityInteractionConnectionCallback callback,
int interrogatingPid, long interrogatingTid);
- void findAccessibilityNodeInfosByViewText(String text, int accessibilityViewId,
+ void findAccessibilityNodeInfosByText(String text, long accessibilityNodeId,
int interactionId, IAccessibilityInteractionConnectionCallback callback,
int interrogatingPid, long interrogatingTid);
- void performAccessibilityAction(int accessibilityId, int action, int interactionId,
+ void performAccessibilityAction(long accessibilityNodeId, int action, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
long interrogatingTid);
}
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
index 3521296..1ed54cb 100644
--- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
@@ -128,7 +128,7 @@
// find a view by text
List<AccessibilityNodeInfo> buttons = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfosByViewTextInActiveWindow(connection, "butto");
+ .findAccessibilityNodeInfosByTextInActiveWindow(connection, "butto");
assertEquals(9, buttons.size());
} finally {
if (DEBUG) {
@@ -151,7 +151,7 @@
// find a view by text
List<AccessibilityNodeInfo> buttons = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfosByViewTextInActiveWindow(connection,
+ .findAccessibilityNodeInfosByTextInActiveWindow(connection,
"contentDescription");
assertEquals(1, buttons.size());
} finally {
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index fd528cc..30c12f9 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1066,17 +1066,17 @@
return getCompatibilityScale(mSecurityPolicy.getRetrievalAllowingWindowLocked());
}
- public float findAccessibilityNodeInfosByViewTextInActiveWindow(
+ public float findAccessibilityNodeInfosByTextInActiveWindow(
String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long threadId)
throws RemoteException {
- return findAccessibilityNodeInfosByViewText(text,
+ return findAccessibilityNodeInfosByText(text,
mSecurityPolicy.mRetrievalAlowingWindowId, View.NO_ID, interactionId, callback,
threadId);
}
- public float findAccessibilityNodeInfosByViewText(String text,
- int accessibilityWindowId, int accessibilityViewId, int interactionId,
+ public float findAccessibilityNodeInfosByText(String text,
+ int accessibilityWindowId, long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
IAccessibilityInteractionConnection connection = null;
@@ -1099,7 +1099,7 @@
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.findAccessibilityNodeInfosByViewText(text, accessibilityViewId,
+ connection.findAccessibilityNodeInfosByText(text, accessibilityNodeId,
interactionId, callback, interrogatingPid, interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
@@ -1112,7 +1112,7 @@
}
public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
- int accessibilityViewId, int interactionId,
+ long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
IAccessibilityInteractionConnection connection = null;
@@ -1136,12 +1136,12 @@
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityViewId,
+ connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
interactionId, callback, interrogatingPid, interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
- Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
- + accessibilityViewId);
+ Slog.e(LOG_TAG, "Error requesting node with accessibilityNodeId: "
+ + accessibilityNodeId);
}
} finally {
Binder.restoreCallingIdentity(identityToken);
@@ -1150,7 +1150,7 @@
}
public boolean performAccessibilityAction(int accessibilityWindowId,
- int accessibilityViewId, int action, int interactionId,
+ long accessibilityNodeId, int action, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) {
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
@@ -1172,12 +1172,12 @@
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.performAccessibilityAction(accessibilityViewId, action, interactionId,
+ connection.performAccessibilityAction(accessibilityNodeId, action, interactionId,
callback, interrogatingPid, interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
- Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
- + accessibilityViewId);
+ Slog.e(LOG_TAG, "Error requesting node with accessibilityNodeId: "
+ + accessibilityNodeId);
}
} finally {
Binder.restoreCallingIdentity(identityToken);