Merge "Intra-process view hierarchy interrogation does not work."
This commit is contained in:
committed by
Android (Google) Code Review
commit
12bde60b39
@@ -18,6 +18,7 @@ package android.accessibilityservice;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
|
||||
|
||||
/**
|
||||
* Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService.
|
||||
@@ -30,84 +31,79 @@ interface IAccessibilityServiceConnection {
|
||||
|
||||
/**
|
||||
* Finds an {@link AccessibilityNodeInfo} by accessibility id.
|
||||
* <p>
|
||||
* <strong>
|
||||
* It is a client responsibility to recycle the received info by
|
||||
* calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
|
||||
* of multiple instances.
|
||||
* </strong>
|
||||
* </p>
|
||||
*
|
||||
* @param accessibilityWindowId A unique window id.
|
||||
* @param accessibilityViewId A unique View accessibility id.
|
||||
* @return The node info.
|
||||
* @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.
|
||||
*/
|
||||
AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
|
||||
int accessibilityViewId);
|
||||
float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
|
||||
int accessibilityViewId, int interactionId,
|
||||
IAccessibilityInteractionConnectionCallback callback, long threadId);
|
||||
|
||||
/**
|
||||
* Finds {@link AccessibilityNodeInfo}s by View text. The match is case
|
||||
* insensitive containment. The search is performed in the window whose
|
||||
* id is specified and starts from the View whose accessibility id is
|
||||
* specified.
|
||||
* <p>
|
||||
* <strong>
|
||||
* It is a client responsibility to recycle the received infos by
|
||||
* calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
|
||||
* of multiple instances.
|
||||
* </strong>
|
||||
* </p>
|
||||
*
|
||||
* @param text The searched text.
|
||||
* @param accessibilityId The id of the view from which to start searching.
|
||||
* @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.
|
||||
* @return A list of node info.
|
||||
* @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.
|
||||
*/
|
||||
List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text,
|
||||
int accessibilityWindowId, int accessibilityViewId);
|
||||
float findAccessibilityNodeInfosByViewText(String text, int accessibilityWindowId,
|
||||
int accessibilityViewId, int interractionId,
|
||||
IAccessibilityInteractionConnectionCallback callback, long threadId);
|
||||
|
||||
/**
|
||||
* Finds {@link AccessibilityNodeInfo}s by View text. The match is case
|
||||
* insensitive containment. The search is performed in the currently
|
||||
* active window and start from the root View in the window.
|
||||
* <p>
|
||||
* <strong>
|
||||
* It is a client responsibility to recycle the received infos by
|
||||
* calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
|
||||
* of multiple instances.
|
||||
* </strong>
|
||||
* </p>
|
||||
*
|
||||
* @param text The searched text.
|
||||
* @param accessibilityId The id of the view from which to start searching.
|
||||
* Use {@link android.view.View#NO_ID} to start from the root.
|
||||
* @return A list of node info.
|
||||
* @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.
|
||||
*/
|
||||
List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow(String text);
|
||||
float findAccessibilityNodeInfosByViewTextInActiveWindow(String text,
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback,
|
||||
long threadId);
|
||||
|
||||
/**
|
||||
* Finds an {@link AccessibilityNodeInfo} by View id. The search is performed
|
||||
* in the currently active window and start from the root View in the window.
|
||||
* <p>
|
||||
* <strong>
|
||||
* It is a client responsibility to recycle the received info by
|
||||
* calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
|
||||
* of multiple instances.
|
||||
* </strong>
|
||||
* </p>
|
||||
* in the currently active window and starts from the root View in the window.
|
||||
*
|
||||
* @param id The id of the node.
|
||||
* @return The node info.
|
||||
* @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.
|
||||
*/
|
||||
AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId);
|
||||
float findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId, int interactionId,
|
||||
IAccessibilityInteractionConnectionCallback callback, long threadId);
|
||||
|
||||
/**
|
||||
* Performs an accessibility action on an {@link AccessibilityNodeInfo}.
|
||||
*
|
||||
* @param accessibilityWindowId The id of the window.
|
||||
* @param accessibilityViewId The of a view in the .
|
||||
* @param accessibilityViewId A unique View accessibility 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,
|
||||
int action);
|
||||
int action, int interactionId, IAccessibilityInteractionConnectionCallback callback,
|
||||
long threadId);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ import android.util.SparseArray;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityInteractionClient;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
@@ -4362,37 +4363,42 @@ public final class ViewRootImpl extends Handler implements ViewParent,
|
||||
}
|
||||
|
||||
public void findAccessibilityNodeInfoByAccessibilityId(int accessibilityId,
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback) {
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback,
|
||||
int interrogatingPid, long interrogatingTid) {
|
||||
if (mViewAncestor.get() != null) {
|
||||
getAccessibilityInteractionController()
|
||||
.findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityId,
|
||||
interactionId, callback);
|
||||
interactionId, callback, interrogatingPid, interrogatingTid);
|
||||
}
|
||||
}
|
||||
|
||||
public void performAccessibilityAction(int accessibilityId, int action,
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback) {
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback,
|
||||
int interogatingPid, long interrogatingTid) {
|
||||
if (mViewAncestor.get() != null) {
|
||||
getAccessibilityInteractionController()
|
||||
.performAccessibilityActionClientThread(accessibilityId, action, interactionId,
|
||||
callback);
|
||||
callback, interogatingPid, interrogatingTid);
|
||||
}
|
||||
}
|
||||
|
||||
public void findAccessibilityNodeInfoByViewId(int viewId,
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback) {
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback,
|
||||
int interrogatingPid, long interrogatingTid) {
|
||||
if (mViewAncestor.get() != null) {
|
||||
getAccessibilityInteractionController()
|
||||
.findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback);
|
||||
.findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback,
|
||||
interrogatingPid, interrogatingTid);
|
||||
}
|
||||
}
|
||||
|
||||
public void findAccessibilityNodeInfosByViewText(String text, int accessibilityId,
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback) {
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback,
|
||||
int interrogatingPid, long interrogatingTid) {
|
||||
if (mViewAncestor.get() != null) {
|
||||
getAccessibilityInteractionController()
|
||||
.findAccessibilityNodeInfosByViewTextClientThread(text, accessibilityId,
|
||||
interactionId, callback);
|
||||
interactionId, callback, interrogatingPid, interrogatingTid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4468,13 +4474,24 @@ public final class ViewRootImpl extends Handler implements ViewParent,
|
||||
}
|
||||
|
||||
public void findAccessibilityNodeInfoByAccessibilityIdClientThread(int accessibilityId,
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback) {
|
||||
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;
|
||||
sendMessage(message);
|
||||
// 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
|
||||
// client can handle the message to generate the result.
|
||||
if (interrogatingPid == Process.myPid()
|
||||
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
|
||||
message.setTarget(ViewRootImpl.this);
|
||||
AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
|
||||
} else {
|
||||
sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
|
||||
@@ -4502,13 +4519,24 @@ public final class ViewRootImpl extends Handler implements ViewParent,
|
||||
}
|
||||
|
||||
public void findAccessibilityNodeInfoByViewIdClientThread(int viewId, int interactionId,
|
||||
IAccessibilityInteractionConnectionCallback callback) {
|
||||
IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
|
||||
long interrogatingTid) {
|
||||
Message message = Message.obtain();
|
||||
message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
|
||||
message.arg1 = viewId;
|
||||
message.arg2 = interactionId;
|
||||
message.obj = callback;
|
||||
sendMessage(message);
|
||||
// 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
|
||||
// client can handle the message to generate the result.
|
||||
if (interrogatingPid == Process.myPid()
|
||||
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
|
||||
message.setTarget(ViewRootImpl.this);
|
||||
AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
|
||||
} else {
|
||||
sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
|
||||
@@ -4535,7 +4563,8 @@ public final class ViewRootImpl extends Handler implements ViewParent,
|
||||
|
||||
public void findAccessibilityNodeInfosByViewTextClientThread(String text,
|
||||
int accessibilityViewId, int interactionId,
|
||||
IAccessibilityInteractionConnectionCallback callback) {
|
||||
IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
|
||||
long interrogatingTid) {
|
||||
Message message = Message.obtain();
|
||||
message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT;
|
||||
SomeArgs args = mPool.acquire();
|
||||
@@ -4544,7 +4573,17 @@ public final class ViewRootImpl extends Handler implements ViewParent,
|
||||
args.argi2 = interactionId;
|
||||
args.arg2 = callback;
|
||||
message.obj = args;
|
||||
sendMessage(message);
|
||||
// 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
|
||||
// client can handle the message to generate the result.
|
||||
if (interrogatingPid == Process.myPid()
|
||||
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
|
||||
message.setTarget(ViewRootImpl.this);
|
||||
AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
|
||||
} else {
|
||||
sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void findAccessibilityNodeInfosByViewTextUiThread(Message message) {
|
||||
@@ -4597,7 +4636,8 @@ public final class ViewRootImpl extends Handler implements ViewParent,
|
||||
}
|
||||
|
||||
public void performAccessibilityActionClientThread(int accessibilityId, int action,
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback) {
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback,
|
||||
int interogatingPid, long interrogatingTid) {
|
||||
Message message = Message.obtain();
|
||||
message.what = DO_PERFORM_ACCESSIBILITY_ACTION;
|
||||
SomeArgs args = mPool.acquire();
|
||||
@@ -4606,7 +4646,17 @@ public final class ViewRootImpl extends Handler implements ViewParent,
|
||||
args.argi3 = interactionId;
|
||||
args.arg1 = callback;
|
||||
message.obj = args;
|
||||
sendMessage(message);
|
||||
// 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
|
||||
// client can handle the message to generate the result.
|
||||
if (interogatingPid == Process.myPid()
|
||||
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
|
||||
message.setTarget(ViewRootImpl.this);
|
||||
AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
|
||||
} else {
|
||||
sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void perfromAccessibilityActionUiThread(Message message) {
|
||||
|
||||
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
** Copyright 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.IAccessibilityServiceConnection;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* This class is a singleton that performs accessibility interaction
|
||||
* which is it queries remote view hierarchies about snapshots of their
|
||||
* views as well requests from these hierarchies to perform certain
|
||||
* actions on their views.
|
||||
*
|
||||
* Rationale: The content retrieval APIs are synchronous from a client's
|
||||
* perspective but internally they are asynchronous. The client thread
|
||||
* calls into the system requesting an action and providing a callback
|
||||
* to receive the result after which it waits up to a timeout for that
|
||||
* result. The system enforces security and the delegates the request
|
||||
* to a given view hierarchy where a message is posted (from a binder
|
||||
* thread) describing what to be performed by the main UI thread the
|
||||
* result of which it delivered via the mentioned callback. However,
|
||||
* the blocked client thread and the main UI thread of the target view
|
||||
* hierarchy can be the same thread, for example an accessibility service
|
||||
* and an activity run in the same process, thus they are executed on the
|
||||
* same main thread. In such a case the retrieval will fail since the UI
|
||||
* thread that has to process the message describing the work to be done
|
||||
* is blocked waiting for a result is has to compute! To avoid this scenario
|
||||
* when making a call the client also passes its process and thread ids so
|
||||
* the accessed view hierarchy can detect if the client making the request
|
||||
* is running in its main UI thread. In such a case the view hierarchy,
|
||||
* specifically the binder thread performing the IPC to it, does not post a
|
||||
* message to be run on the UI thread but passes it to the singleton
|
||||
* interaction client through which all interactions occur and the latter is
|
||||
* responsible to execute the message before starting to wait for the
|
||||
* asynchronous result delivered via the callback. In this case the expected
|
||||
* result is already received so no waiting is performed.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class AccessibilityInteractionClient
|
||||
extends IAccessibilityInteractionConnectionCallback.Stub {
|
||||
|
||||
private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
|
||||
|
||||
private static final Object sStaticLock = new Object();
|
||||
|
||||
private static AccessibilityInteractionClient sInstance;
|
||||
|
||||
private final AtomicInteger mInteractionIdCounter = new AtomicInteger();
|
||||
|
||||
private final Object mInstanceLock = new Object();
|
||||
|
||||
private int mInteractionId = -1;
|
||||
|
||||
private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
|
||||
|
||||
private List<AccessibilityNodeInfo> mFindAccessibilityNodeInfosResult;
|
||||
|
||||
private boolean mPerformAccessibilityActionResult;
|
||||
|
||||
private Message mSameThreadMessage;
|
||||
|
||||
private final Rect mTempBounds = new Rect();
|
||||
|
||||
/**
|
||||
* @return The singleton of this class.
|
||||
*/
|
||||
public static AccessibilityInteractionClient getInstance() {
|
||||
synchronized (sStaticLock) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new AccessibilityInteractionClient();
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message to be processed if the interacted view hierarchy
|
||||
* and the interacting client are running in the same thread.
|
||||
*
|
||||
* @param message The message.
|
||||
*/
|
||||
public void setSameThreadMessage(Message message) {
|
||||
synchronized (mInstanceLock) {
|
||||
mSameThreadMessage = message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an {@link AccessibilityNodeInfo} by accessibility id.
|
||||
*
|
||||
* @param connection A connection for interacting with the system.
|
||||
* @param accessibilityWindowId A unique window id.
|
||||
* @param accessibilityViewId A unique View accessibility id.
|
||||
* @return An {@link AccessibilityNodeInfo} if found, null otherwise.
|
||||
*/
|
||||
public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(
|
||||
IAccessibilityServiceConnection connection, int accessibilityWindowId,
|
||||
int accessibilityViewId) {
|
||||
try {
|
||||
final int interactionId = mInteractionIdCounter.getAndIncrement();
|
||||
final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
|
||||
accessibilityWindowId, accessibilityViewId, interactionId, this,
|
||||
Thread.currentThread().getId());
|
||||
// If the scale is zero the call has failed.
|
||||
if (windowScale > 0) {
|
||||
handleSameThreadMessageIfNeeded();
|
||||
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
|
||||
interactionId);
|
||||
finalizeAccessibilityNodeInfo(info, connection, windowScale);
|
||||
return info;
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
/* ignore */
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an {@link AccessibilityNodeInfo} by View id. The search is performed
|
||||
* in the currently active window and starts from the root View in the window.
|
||||
*
|
||||
* @param connection A connection for interacting with the system.
|
||||
* @param id The id of the node.
|
||||
* @return An {@link AccessibilityNodeInfo} if found, null otherwise.
|
||||
*/
|
||||
public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(
|
||||
IAccessibilityServiceConnection connection, int viewId) {
|
||||
try {
|
||||
final int interactionId = mInteractionIdCounter.getAndIncrement();
|
||||
final float windowScale = connection.findAccessibilityNodeInfoByViewIdInActiveWindow(
|
||||
viewId, interactionId, this, Thread.currentThread().getId());
|
||||
// If the scale is zero the call has failed.
|
||||
if (windowScale > 0) {
|
||||
handleSameThreadMessageIfNeeded();
|
||||
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
|
||||
interactionId);
|
||||
finalizeAccessibilityNodeInfo(info, connection, windowScale);
|
||||
return info;
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
/* ignore */
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds {@link AccessibilityNodeInfo}s by View text. The match is case
|
||||
* insensitive containment. The search is performed in the currently
|
||||
* active window and starts from the root View in the window.
|
||||
*
|
||||
* @param connection A connection for interacting with the system.
|
||||
* @param text The searched text.
|
||||
* @return A list of found {@link AccessibilityNodeInfo}s.
|
||||
*/
|
||||
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow(
|
||||
IAccessibilityServiceConnection connection, String text) {
|
||||
try {
|
||||
final int interactionId = mInteractionIdCounter.getAndIncrement();
|
||||
final float windowScale = connection.findAccessibilityNodeInfosByViewTextInActiveWindow(
|
||||
text, interactionId, this, Thread.currentThread().getId());
|
||||
// If the scale is zero the call has failed.
|
||||
if (windowScale > 0) {
|
||||
handleSameThreadMessageIfNeeded();
|
||||
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
|
||||
interactionId);
|
||||
finalizeAccessibilityNodeInfos(infos, connection, windowScale);
|
||||
return infos;
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
/* ignore */
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds {@link AccessibilityNodeInfo}s by View text. The match is case
|
||||
* insensitive containment. The search is performed in the window whose
|
||||
* id is specified and starts from the View whose accessibility id is
|
||||
* specified.
|
||||
*
|
||||
* @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.
|
||||
* @return A list of found {@link AccessibilityNodeInfo}s.
|
||||
*/
|
||||
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(
|
||||
IAccessibilityServiceConnection connection, String text, int accessibilityWindowId,
|
||||
int accessibilityViewId) {
|
||||
try {
|
||||
final int interactionId = mInteractionIdCounter.getAndIncrement();
|
||||
final float windowScale = connection.findAccessibilityNodeInfosByViewText(text,
|
||||
accessibilityWindowId, accessibilityViewId, interactionId, this,
|
||||
Thread.currentThread().getId());
|
||||
// If the scale is zero the call has failed.
|
||||
if (windowScale > 0) {
|
||||
handleSameThreadMessageIfNeeded();
|
||||
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
|
||||
interactionId);
|
||||
finalizeAccessibilityNodeInfos(infos, connection, windowScale);
|
||||
return infos;
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
/* ignore */
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an accessibility action on an {@link AccessibilityNodeInfo}.
|
||||
*
|
||||
* @param connection A connection for interacting with the system.
|
||||
* @param accessibilityWindowId The id of the window.
|
||||
* @param accessibilityViewId A unique View accessibility id.
|
||||
* @param action The action to perform.
|
||||
* @return Whether the action was performed.
|
||||
*/
|
||||
public boolean performAccessibilityAction(IAccessibilityServiceConnection connection,
|
||||
int accessibilityWindowId, int accessibilityViewId, int action) {
|
||||
try {
|
||||
final int interactionId = mInteractionIdCounter.getAndIncrement();
|
||||
final boolean success = connection.performAccessibilityAction(
|
||||
accessibilityWindowId, accessibilityViewId, action, interactionId, this,
|
||||
Thread.currentThread().getId());
|
||||
if (success) {
|
||||
handleSameThreadMessageIfNeeded();
|
||||
return getPerformAccessibilityActionResult(interactionId);
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
/* ignore */
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}.
|
||||
*
|
||||
* @param interactionId The interaction id to match the result with the request.
|
||||
* @return The result {@link AccessibilityNodeInfo}.
|
||||
*/
|
||||
private AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) {
|
||||
synchronized (mInstanceLock) {
|
||||
final boolean success = waitForResultTimedLocked(interactionId);
|
||||
AccessibilityNodeInfo result = success ? mFindAccessibilityNodeInfoResult : null;
|
||||
clearResultLocked();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info,
|
||||
int interactionId) {
|
||||
synchronized (mInstanceLock) {
|
||||
if (interactionId > mInteractionId) {
|
||||
mFindAccessibilityNodeInfoResult = info;
|
||||
mInteractionId = interactionId;
|
||||
}
|
||||
mInstanceLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the the result of an async request that returns {@link AccessibilityNodeInfo}s.
|
||||
*
|
||||
* @param interactionId The interaction id to match the result with the request.
|
||||
* @return The result {@link AccessibilityNodeInfo}s.
|
||||
*/
|
||||
private List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear(
|
||||
int interactionId) {
|
||||
synchronized (mInstanceLock) {
|
||||
final boolean success = waitForResultTimedLocked(interactionId);
|
||||
List<AccessibilityNodeInfo> result = success ? mFindAccessibilityNodeInfosResult : null;
|
||||
clearResultLocked();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
|
||||
int interactionId) {
|
||||
synchronized (mInstanceLock) {
|
||||
if (interactionId > mInteractionId) {
|
||||
mFindAccessibilityNodeInfosResult = infos;
|
||||
mInteractionId = interactionId;
|
||||
}
|
||||
mInstanceLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result of a request to perform an accessibility action.
|
||||
*
|
||||
* @param interactionId The interaction id to match the result with the request.
|
||||
* @return Whether the action was performed.
|
||||
*/
|
||||
private boolean getPerformAccessibilityActionResult(int interactionId) {
|
||||
synchronized (mInstanceLock) {
|
||||
final boolean success = waitForResultTimedLocked(interactionId);
|
||||
final boolean result = success ? mPerformAccessibilityActionResult : false;
|
||||
clearResultLocked();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) {
|
||||
synchronized (mInstanceLock) {
|
||||
if (interactionId > mInteractionId) {
|
||||
mPerformAccessibilityActionResult = succeeded;
|
||||
mInteractionId = interactionId;
|
||||
}
|
||||
mInstanceLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the result state.
|
||||
*/
|
||||
private void clearResultLocked() {
|
||||
mInteractionId = -1;
|
||||
mFindAccessibilityNodeInfoResult = null;
|
||||
mFindAccessibilityNodeInfosResult = null;
|
||||
mPerformAccessibilityActionResult = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits up to a given bound for a result of a request and returns it.
|
||||
*
|
||||
* @param interactionId The interaction id to match the result with the request.
|
||||
* @return Whether the result was received.
|
||||
*/
|
||||
private boolean waitForResultTimedLocked(int interactionId) {
|
||||
long waitTimeMillis = TIMEOUT_INTERACTION_MILLIS;
|
||||
final long startTimeMillis = SystemClock.uptimeMillis();
|
||||
while (true) {
|
||||
try {
|
||||
if (mInteractionId == interactionId) {
|
||||
return true;
|
||||
}
|
||||
if (mInteractionId > interactionId) {
|
||||
return false;
|
||||
}
|
||||
final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
|
||||
waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis;
|
||||
if (waitTimeMillis <= 0) {
|
||||
return false;
|
||||
}
|
||||
mInstanceLock.wait(waitTimeMillis);
|
||||
} catch (InterruptedException ie) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies compatibility scale to the info bounds if it is not equal to one.
|
||||
*
|
||||
* @param info The info whose bounds to scale.
|
||||
* @param scale The scale to apply.
|
||||
*/
|
||||
private void applyCompatibilityScaleIfNeeded(AccessibilityNodeInfo info, float scale) {
|
||||
if (scale == 1.0f) {
|
||||
return;
|
||||
}
|
||||
Rect bounds = mTempBounds;
|
||||
info.getBoundsInParent(bounds);
|
||||
bounds.scale(scale);
|
||||
info.setBoundsInParent(bounds);
|
||||
|
||||
info.getBoundsInScreen(bounds);
|
||||
bounds.scale(scale);
|
||||
info.setBoundsInScreen(bounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the message stored if the interacted and interacting
|
||||
* threads are the same otherwise this is a NOP.
|
||||
*/
|
||||
private void handleSameThreadMessageIfNeeded() {
|
||||
Message sameProcessMessage = getSameProcessMessageAndClear();
|
||||
if (sameProcessMessage != null) {
|
||||
sameProcessMessage.getTarget().handleMessage(sameProcessMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize an {@link AccessibilityNodeInfo} before passing it to the client.
|
||||
*
|
||||
* @param info The info.
|
||||
* @param connection The current connection to the system.
|
||||
* @param windowScale The source window compatibility scale.
|
||||
*/
|
||||
private void finalizeAccessibilityNodeInfo(AccessibilityNodeInfo info,
|
||||
IAccessibilityServiceConnection connection, float windowScale) {
|
||||
if (info != null) {
|
||||
applyCompatibilityScaleIfNeeded(info, windowScale);
|
||||
info.setConnection(connection);
|
||||
info.setSealed(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize {@link AccessibilityNodeInfo}s before passing them to the client.
|
||||
*
|
||||
* @param infos The {@link AccessibilityNodeInfo}s.
|
||||
* @param connection The current connection to the system.
|
||||
* @param windowScale The source window compatibility scale.
|
||||
*/
|
||||
private void finalizeAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
|
||||
IAccessibilityServiceConnection connection, float windowScale) {
|
||||
if (infos != null) {
|
||||
final int infosCount = infos.size();
|
||||
for (int i = 0; i < infosCount; i++) {
|
||||
AccessibilityNodeInfo info = infos.get(i);
|
||||
finalizeAccessibilityNodeInfo(info, connection, windowScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message stored if the interacted and interacting
|
||||
* threads are the same.
|
||||
*
|
||||
* @return The message.
|
||||
*/
|
||||
private Message getSameProcessMessageAndClear() {
|
||||
synchronized (mInstanceLock) {
|
||||
Message result = mSameThreadMessage;
|
||||
mSameThreadMessage = null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import android.accessibilityservice.IAccessibilityServiceConnection;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.View;
|
||||
@@ -181,13 +180,9 @@ public class AccessibilityNodeInfo implements Parcelable {
|
||||
if (!canPerformRequestOverConnection(childAccessibilityViewId)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId,
|
||||
childAccessibilityViewId);
|
||||
} catch (RemoteException re) {
|
||||
/* ignore*/
|
||||
}
|
||||
return null;
|
||||
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
|
||||
return client.findAccessibilityNodeInfoByAccessibilityId(mConnection,
|
||||
mAccessibilityWindowId, childAccessibilityViewId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,13 +252,9 @@ public class AccessibilityNodeInfo implements Parcelable {
|
||||
if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return mConnection.performAccessibilityAction(mAccessibilityWindowId,
|
||||
mAccessibilityViewId, action);
|
||||
} catch (RemoteException e) {
|
||||
/* ignore */
|
||||
}
|
||||
return false;
|
||||
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
|
||||
return client.performAccessibilityAction(mConnection, mAccessibilityWindowId,
|
||||
mAccessibilityViewId, action);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,13 +275,9 @@ public class AccessibilityNodeInfo implements Parcelable {
|
||||
if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
return mConnection.findAccessibilityNodeInfosByViewText(text, mAccessibilityWindowId,
|
||||
mAccessibilityViewId);
|
||||
} catch (RemoteException e) {
|
||||
/* ignore */
|
||||
}
|
||||
return Collections.emptyList();
|
||||
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
|
||||
return client.findAccessibilityNodeInfosByViewText(mConnection, text,
|
||||
mAccessibilityWindowId, mAccessibilityViewId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,13 +295,9 @@ public class AccessibilityNodeInfo implements Parcelable {
|
||||
if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return mConnection.findAccessibilityNodeInfoByAccessibilityId(
|
||||
mAccessibilityWindowId, mParentAccessibilityViewId);
|
||||
} catch (RemoteException e) {
|
||||
/* ignore */
|
||||
}
|
||||
return null;
|
||||
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
|
||||
return client.findAccessibilityNodeInfoByAccessibilityId(mConnection,
|
||||
mAccessibilityWindowId, mParentAccessibilityViewId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,6 @@ package android.view.accessibility;
|
||||
|
||||
import android.accessibilityservice.IAccessibilityServiceConnection;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -127,13 +126,9 @@ public class AccessibilityRecord {
|
||||
if (mSourceWindowId == View.NO_ID || mSourceViewId == View.NO_ID || mConnection == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return mConnection.findAccessibilityNodeInfoByAccessibilityId(mSourceWindowId,
|
||||
mSourceViewId);
|
||||
} catch (RemoteException e) {
|
||||
/* ignore */
|
||||
}
|
||||
return null;
|
||||
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
|
||||
return client.findAccessibilityNodeInfoByAccessibilityId(mConnection, mSourceWindowId,
|
||||
mSourceViewId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.view.accessibility.AccessibilityNodeInfo;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Callback for specifying the result for an asynchronous request made
|
||||
* via calling a method on IAccessibilityInteractionCallback.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IAccessibilityInteractionCallback {
|
||||
|
||||
/**
|
||||
* Sets the result of an async request that returns an {@link AccessibilityNodeInfo}.
|
||||
*
|
||||
* @param infos The result {@link AccessibilityNodeInfo}.
|
||||
* @param interactionId The interaction id to match the result with the request.
|
||||
*/
|
||||
void setFindAccessibilityNodeInfoResult(in AccessibilityNodeInfo info, int interactionId);
|
||||
|
||||
/**
|
||||
* Sets the result of an async request that returns {@link AccessibilityNodeInfo}s.
|
||||
*
|
||||
* @param infos The result {@link AccessibilityNodeInfo}s.
|
||||
* @param interactionId The interaction id to match the result with the request.
|
||||
*/
|
||||
void setFindAccessibilityNodeInfosResult(in List<AccessibilityNodeInfo> infos,
|
||||
int interactionId);
|
||||
|
||||
/**
|
||||
* Sets the result of a request to perform an accessibility action.
|
||||
*
|
||||
* @param Whether the action was performed.
|
||||
* @param interactionId The interaction id to match the result with the request.
|
||||
*/
|
||||
void setPerformAccessibilityActionResult(boolean succeeded, int interactionId);
|
||||
}
|
||||
@@ -28,14 +28,18 @@ import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
|
||||
oneway interface IAccessibilityInteractionConnection {
|
||||
|
||||
void findAccessibilityNodeInfoByAccessibilityId(int accessibilityViewId, int interactionId,
|
||||
IAccessibilityInteractionConnectionCallback callback);
|
||||
IAccessibilityInteractionConnectionCallback callback,
|
||||
int interrogatingPid, long interrogatingTid);
|
||||
|
||||
void findAccessibilityNodeInfoByViewId(int id, int interactionId,
|
||||
IAccessibilityInteractionConnectionCallback callback);
|
||||
IAccessibilityInteractionConnectionCallback callback,
|
||||
int interrogatingPid, long interrogatingTid);
|
||||
|
||||
void findAccessibilityNodeInfosByViewText(String text, int accessibilityViewId,
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback);
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback,
|
||||
int interrogatingPid, long interrogatingTid);
|
||||
|
||||
void performAccessibilityAction(int accessibilityId, int action, int interactionId,
|
||||
IAccessibilityInteractionConnectionCallback callback);
|
||||
IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
|
||||
long interrogatingTid);
|
||||
}
|
||||
|
||||
@@ -27,10 +27,28 @@ import java.util.List;
|
||||
*/
|
||||
oneway interface IAccessibilityInteractionConnectionCallback {
|
||||
|
||||
/**
|
||||
* Sets the result of an async request that returns an {@link AccessibilityNodeInfo}.
|
||||
*
|
||||
* @param infos The result {@link AccessibilityNodeInfo}.
|
||||
* @param interactionId The interaction id to match the result with the request.
|
||||
*/
|
||||
void setFindAccessibilityNodeInfoResult(in AccessibilityNodeInfo info, int interactionId);
|
||||
|
||||
/**
|
||||
* Sets the result of an async request that returns {@link AccessibilityNodeInfo}s.
|
||||
*
|
||||
* @param infos The result {@link AccessibilityNodeInfo}s.
|
||||
* @param interactionId The interaction id to match the result with the request.
|
||||
*/
|
||||
void setFindAccessibilityNodeInfosResult(in List<AccessibilityNodeInfo> infos,
|
||||
int interactionId);
|
||||
|
||||
/**
|
||||
* Sets the result of a request to perform an accessibility action.
|
||||
*
|
||||
* @param Whether the action was performed.
|
||||
* @param interactionId The interaction id to match the result with the request.
|
||||
*/
|
||||
void setPerformAccessibilityActionResult(boolean succeeded, int interactionId);
|
||||
}
|
||||
|
||||
@@ -30,20 +30,20 @@
|
||||
>
|
||||
<Button
|
||||
android:id="@+id/button1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="160px"
|
||||
android:layout_height="100px"
|
||||
android:text="@string/button1"
|
||||
/>
|
||||
<Button
|
||||
android:id="@+id/button2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="160px"
|
||||
android:layout_height="100px"
|
||||
android:text="@string/button2"
|
||||
/>
|
||||
<Button
|
||||
android:id="@+id/button3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="160px"
|
||||
android:layout_height="100px"
|
||||
android:text="@string/button3"
|
||||
/>
|
||||
</LinearLayout>
|
||||
@@ -55,20 +55,20 @@
|
||||
>
|
||||
<Button
|
||||
android:id="@+id/button4"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="160px"
|
||||
android:layout_height="100px"
|
||||
android:text="@string/button4"
|
||||
/>
|
||||
<Button
|
||||
android:id="@+id/button5"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="160px"
|
||||
android:layout_height="100px"
|
||||
android:text="@string/button5"
|
||||
/>
|
||||
<Button
|
||||
android:id="@+id/button6"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="160px"
|
||||
android:layout_height="100px"
|
||||
android:text="@string/button6"
|
||||
/>
|
||||
</LinearLayout>
|
||||
@@ -80,20 +80,20 @@
|
||||
>
|
||||
<Button
|
||||
android:id="@+id/button7"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="160px"
|
||||
android:layout_height="100px"
|
||||
android:text="@string/button7"
|
||||
/>
|
||||
<Button
|
||||
android:id="@+id/button8"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="160px"
|
||||
android:layout_height="100px"
|
||||
android:text="@string/button8"
|
||||
/>
|
||||
<Button
|
||||
android:id="@+id/button9"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="160px"
|
||||
android:layout_height="100px"
|
||||
android:text="@string/button9"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityInteractionClient;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.accessibility.IAccessibilityManager;
|
||||
@@ -81,8 +82,8 @@ public class InterrogationActivityTest
|
||||
// bring up the activity
|
||||
getActivity();
|
||||
|
||||
AccessibilityNodeInfo button =
|
||||
getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
|
||||
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
|
||||
assertNotNull(button);
|
||||
assertEquals(0, button.getChildCount());
|
||||
|
||||
@@ -91,8 +92,8 @@ public class InterrogationActivityTest
|
||||
button.getBoundsInParent(bounds);
|
||||
assertEquals(0, bounds.left);
|
||||
assertEquals(0, bounds.top);
|
||||
assertEquals(73, bounds.right);
|
||||
assertEquals(48, bounds.bottom);
|
||||
assertEquals(160, bounds.right);
|
||||
assertEquals(100, bounds.bottom);
|
||||
|
||||
// char sequence attributes
|
||||
assertEquals("com.android.frameworks.coretests", button.getPackageName());
|
||||
@@ -133,8 +134,8 @@ public class InterrogationActivityTest
|
||||
getActivity();
|
||||
|
||||
// find a view by text
|
||||
List<AccessibilityNodeInfo> buttons =
|
||||
getConnection().findAccessibilityNodeInfosByViewTextInActiveWindow("butto");
|
||||
List<AccessibilityNodeInfo> buttons = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfosByViewTextInActiveWindow(getConnection(), "butto");
|
||||
assertEquals(9, buttons.size());
|
||||
} finally {
|
||||
afterClassIfNeeded();
|
||||
@@ -170,8 +171,8 @@ public class InterrogationActivityTest
|
||||
classNameAndTextList.add("android.widget.ButtonButton8");
|
||||
classNameAndTextList.add("android.widget.ButtonButton9");
|
||||
|
||||
AccessibilityNodeInfo root =
|
||||
getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.root);
|
||||
AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.root);
|
||||
assertNotNull("We must find the existing root.", root);
|
||||
|
||||
Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
|
||||
@@ -214,15 +215,16 @@ public class InterrogationActivityTest
|
||||
getActivity();
|
||||
|
||||
// find a view and make sure it is not focused
|
||||
AccessibilityNodeInfo button =
|
||||
getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
|
||||
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
|
||||
assertFalse(button.isFocused());
|
||||
|
||||
// focus the view
|
||||
assertTrue(button.performAction(ACTION_FOCUS));
|
||||
|
||||
// find the view again and make sure it is focused
|
||||
button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
|
||||
button = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
|
||||
assertTrue(button.isFocused());
|
||||
} finally {
|
||||
afterClassIfNeeded();
|
||||
@@ -242,22 +244,24 @@ public class InterrogationActivityTest
|
||||
getActivity();
|
||||
|
||||
// find a view and make sure it is not focused
|
||||
AccessibilityNodeInfo button =
|
||||
getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
|
||||
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
|
||||
assertFalse(button.isFocused());
|
||||
|
||||
// focus the view
|
||||
assertTrue(button.performAction(ACTION_FOCUS));
|
||||
|
||||
// find the view again and make sure it is focused
|
||||
button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
|
||||
button = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
|
||||
assertTrue(button.isFocused());
|
||||
|
||||
// unfocus the view
|
||||
assertTrue(button.performAction(ACTION_CLEAR_FOCUS));
|
||||
|
||||
// find the view again and make sure it is not focused
|
||||
button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
|
||||
button = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
|
||||
assertFalse(button.isFocused());
|
||||
} finally {
|
||||
afterClassIfNeeded();
|
||||
@@ -278,15 +282,16 @@ public class InterrogationActivityTest
|
||||
getActivity();
|
||||
|
||||
// find a view and make sure it is not selected
|
||||
AccessibilityNodeInfo button =
|
||||
getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
|
||||
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
|
||||
assertFalse(button.isSelected());
|
||||
|
||||
// select the view
|
||||
assertTrue(button.performAction(ACTION_SELECT));
|
||||
|
||||
// find the view again and make sure it is selected
|
||||
button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
|
||||
button = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
|
||||
assertTrue(button.isSelected());
|
||||
} finally {
|
||||
afterClassIfNeeded();
|
||||
@@ -306,22 +311,24 @@ public class InterrogationActivityTest
|
||||
getActivity();
|
||||
|
||||
// find a view and make sure it is not selected
|
||||
AccessibilityNodeInfo button =
|
||||
getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
|
||||
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
|
||||
assertFalse(button.isSelected());
|
||||
|
||||
// select the view
|
||||
assertTrue(button.performAction(ACTION_SELECT));
|
||||
|
||||
// find the view again and make sure it is selected
|
||||
button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
|
||||
button = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
|
||||
assertTrue(button.isSelected());
|
||||
|
||||
// unselect the view
|
||||
assertTrue(button.performAction(ACTION_CLEAR_SELECTION));
|
||||
|
||||
// find the view again and make sure it is not selected
|
||||
button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
|
||||
button = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
|
||||
assertFalse(button.isSelected());
|
||||
} finally {
|
||||
afterClassIfNeeded();
|
||||
@@ -342,8 +349,8 @@ public class InterrogationActivityTest
|
||||
getActivity();
|
||||
|
||||
// find a view and make sure it is not focused
|
||||
AccessibilityNodeInfo button =
|
||||
getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
|
||||
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
|
||||
assertFalse(button.isSelected());
|
||||
|
||||
// focus the view
|
||||
@@ -406,8 +413,8 @@ public class InterrogationActivityTest
|
||||
getActivity();
|
||||
|
||||
// find a view and make sure it is not focused
|
||||
AccessibilityNodeInfo button =
|
||||
getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
|
||||
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
|
||||
.findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
|
||||
AccessibilityNodeInfo parent = button.getParent();
|
||||
final int childCount = parent.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
|
||||
@@ -40,7 +40,6 @@ import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextUtils.SimpleStringSplitter;
|
||||
@@ -72,7 +71,6 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* This class is instantiated by the system as a system level service and can be
|
||||
@@ -871,10 +869,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
|
||||
boolean mIsAutomation;
|
||||
|
||||
final Callback mCallback = new Callback();
|
||||
|
||||
final AtomicInteger mInteractionIdCounter = new AtomicInteger();
|
||||
|
||||
final Rect mTempBounds = new Rect();
|
||||
|
||||
// the events pending events to be dispatched to this service
|
||||
@@ -974,14 +968,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId)
|
||||
public float findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId,
|
||||
int interactionId, IAccessibilityInteractionConnectionCallback callback,
|
||||
long interrogatingTid)
|
||||
throws RemoteException {
|
||||
IAccessibilityInteractionConnection connection = null;
|
||||
synchronized (mLock) {
|
||||
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
|
||||
final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
|
||||
if (!permissionGranted) {
|
||||
return null;
|
||||
return 0;
|
||||
} else {
|
||||
connection = getConnectionToRetrievalAllowingWindowLocked();
|
||||
if (connection == null) {
|
||||
@@ -989,22 +985,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
Slog.e(LOG_TAG, "No interaction connection to a retrieve "
|
||||
+ "allowing window.");
|
||||
}
|
||||
return null;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
final int interrogatingPid = Binder.getCallingPid();
|
||||
final long identityToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
final int interactionId = mInteractionIdCounter.getAndIncrement();
|
||||
connection.findAccessibilityNodeInfoByViewId(viewId, interactionId, mCallback);
|
||||
AccessibilityNodeInfo info = mCallback.getFindAccessibilityNodeInfoResultAndClear(
|
||||
interactionId);
|
||||
if (info != null) {
|
||||
applyCompatibilityScaleIfNeeded(info);
|
||||
info.setConnection(this);
|
||||
info.setSealed(true);
|
||||
}
|
||||
return info;
|
||||
connection.findAccessibilityNodeInfoByViewId(viewId, interactionId, callback,
|
||||
interrogatingPid, interrogatingTid);
|
||||
} catch (RemoteException re) {
|
||||
if (DEBUG) {
|
||||
Slog.e(LOG_TAG, "Error finding node.");
|
||||
@@ -1012,51 +1001,44 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identityToken);
|
||||
}
|
||||
return null;
|
||||
return getCompatibilityScale(mSecurityPolicy.getRetrievalAllowingWindowLocked());
|
||||
}
|
||||
|
||||
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow(
|
||||
String text) throws RemoteException {
|
||||
public float findAccessibilityNodeInfosByViewTextInActiveWindow(
|
||||
String text, int interactionId,
|
||||
IAccessibilityInteractionConnectionCallback callback, long threadId)
|
||||
throws RemoteException {
|
||||
return findAccessibilityNodeInfosByViewText(text,
|
||||
mSecurityPolicy.mRetrievalAlowingWindowId, View.NO_ID);
|
||||
mSecurityPolicy.mRetrievalAlowingWindowId, View.NO_ID, interactionId, callback,
|
||||
threadId);
|
||||
}
|
||||
|
||||
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text,
|
||||
int accessibilityWindowId, int accessibilityViewId) throws RemoteException {
|
||||
public float findAccessibilityNodeInfosByViewText(String text,
|
||||
int accessibilityWindowId, int accessibilityViewId, int interactionId,
|
||||
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
|
||||
throws RemoteException {
|
||||
IAccessibilityInteractionConnection connection = null;
|
||||
synchronized (mLock) {
|
||||
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
|
||||
final boolean permissionGranted =
|
||||
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
|
||||
if (!permissionGranted) {
|
||||
return null;
|
||||
return 0;
|
||||
} else {
|
||||
connection = getConnectionToRetrievalAllowingWindowLocked();
|
||||
if (connection == null) {
|
||||
if (DEBUG) {
|
||||
Slog.e(LOG_TAG, "No interaction connection to focused window.");
|
||||
}
|
||||
return null;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
final int interrogatingPid = Binder.getCallingPid();
|
||||
final long identityToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
final int interactionId = mInteractionIdCounter.getAndIncrement();
|
||||
connection.findAccessibilityNodeInfosByViewText(text, accessibilityViewId,
|
||||
interactionId, mCallback);
|
||||
List<AccessibilityNodeInfo> infos =
|
||||
mCallback.getFindAccessibilityNodeInfosResultAndClear(interactionId);
|
||||
if (infos != null) {
|
||||
final int infoCount = infos.size();
|
||||
for (int i = 0; i < infoCount; i++) {
|
||||
AccessibilityNodeInfo info = infos.get(i);
|
||||
applyCompatibilityScaleIfNeeded(info);
|
||||
info.setConnection(this);
|
||||
info.setSealed(true);
|
||||
}
|
||||
}
|
||||
return infos;
|
||||
interactionId, callback, interrogatingPid, interrogatingTid);
|
||||
} catch (RemoteException re) {
|
||||
if (DEBUG) {
|
||||
Slog.e(LOG_TAG, "Error finding node.");
|
||||
@@ -1064,18 +1046,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identityToken);
|
||||
}
|
||||
return null;
|
||||
return getCompatibilityScale(accessibilityWindowId);
|
||||
}
|
||||
|
||||
public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(
|
||||
int accessibilityWindowId, int accessibilityViewId) throws RemoteException {
|
||||
public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
|
||||
int accessibilityViewId, int interactionId,
|
||||
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
|
||||
throws RemoteException {
|
||||
IAccessibilityInteractionConnection connection = null;
|
||||
synchronized (mLock) {
|
||||
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
|
||||
final boolean permissionGranted =
|
||||
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
|
||||
if (!permissionGranted) {
|
||||
return null;
|
||||
return 0;
|
||||
} else {
|
||||
connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
|
||||
if (connection == null) {
|
||||
@@ -1083,23 +1067,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
Slog.e(LOG_TAG, "No interaction connection to window: "
|
||||
+ accessibilityWindowId);
|
||||
}
|
||||
return null;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
final int interrogatingPid = Binder.getCallingPid();
|
||||
final long identityToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
final int interactionId = mInteractionIdCounter.getAndIncrement();
|
||||
connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityViewId,
|
||||
interactionId, mCallback);
|
||||
AccessibilityNodeInfo info =
|
||||
mCallback.getFindAccessibilityNodeInfoResultAndClear(interactionId);
|
||||
if (info != null) {
|
||||
applyCompatibilityScaleIfNeeded(info);
|
||||
info.setConnection(this);
|
||||
info.setSealed(true);
|
||||
}
|
||||
return info;
|
||||
interactionId, callback, interrogatingPid, interrogatingTid);
|
||||
} catch (RemoteException re) {
|
||||
if (DEBUG) {
|
||||
Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
|
||||
@@ -1108,11 +1084,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identityToken);
|
||||
}
|
||||
return null;
|
||||
return getCompatibilityScale(accessibilityWindowId);
|
||||
}
|
||||
|
||||
public boolean performAccessibilityAction(int accessibilityWindowId,
|
||||
int accessibilityViewId, int action) {
|
||||
int accessibilityViewId, int action, int interactionId,
|
||||
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) {
|
||||
IAccessibilityInteractionConnection connection = null;
|
||||
synchronized (mLock) {
|
||||
final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
|
||||
@@ -1130,12 +1107,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
}
|
||||
}
|
||||
final int interrogatingPid = Binder.getCallingPid();
|
||||
final long identityToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
final int interactionId = mInteractionIdCounter.getAndIncrement();
|
||||
connection.performAccessibilityAction(accessibilityViewId, action, interactionId,
|
||||
mCallback);
|
||||
return mCallback.getPerformAccessibilityActionResult(interactionId);
|
||||
callback, interrogatingPid, interrogatingTid);
|
||||
} catch (RemoteException re) {
|
||||
if (DEBUG) {
|
||||
Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
|
||||
@@ -1144,7 +1120,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identityToken);
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName componentName) {
|
||||
@@ -1173,22 +1149,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
return mWindowIdToInteractionConnectionMap.get(windowId);
|
||||
}
|
||||
|
||||
private void applyCompatibilityScaleIfNeeded(AccessibilityNodeInfo info) {
|
||||
IBinder windowToken = mWindowIdToWindowTokenMap.get(info.getWindowId());
|
||||
final float scale = mWindowManagerService.getWindowCompatibilityScale(windowToken);
|
||||
|
||||
if (scale == 1.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
Rect bounds = mTempBounds;
|
||||
info.getBoundsInParent(bounds);
|
||||
bounds.scale(scale);
|
||||
info.setBoundsInParent(bounds);
|
||||
|
||||
info.getBoundsInScreen(bounds);
|
||||
bounds.scale(scale);
|
||||
info.setBoundsInScreen(bounds);
|
||||
private float getCompatibilityScale(int windowId) {
|
||||
IBinder windowToken = mWindowIdToWindowTokenMap.get(windowId);
|
||||
return mWindowManagerService.getWindowCompatibilityScale(windowToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1275,99 +1238,4 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class Callback extends IAccessibilityInteractionConnectionCallback.Stub {
|
||||
private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
|
||||
|
||||
private int mInteractionId = -1;
|
||||
private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
|
||||
private List<AccessibilityNodeInfo> mFindAccessibilityNodeInfosResult;
|
||||
private boolean mPerformAccessibilityActionResult;
|
||||
|
||||
public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info,
|
||||
int interactionId) {
|
||||
synchronized (mLock) {
|
||||
if (interactionId > mInteractionId) {
|
||||
mFindAccessibilityNodeInfoResult = info;
|
||||
mInteractionId = interactionId;
|
||||
}
|
||||
mLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) {
|
||||
synchronized (mLock) {
|
||||
waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
|
||||
AccessibilityNodeInfo result = mFindAccessibilityNodeInfoResult;
|
||||
clearLocked();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
|
||||
int interactionId) {
|
||||
synchronized (mLock) {
|
||||
if (interactionId > mInteractionId) {
|
||||
mFindAccessibilityNodeInfosResult = infos;
|
||||
mInteractionId = interactionId;
|
||||
}
|
||||
mLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear(
|
||||
int interactionId) {
|
||||
synchronized (mLock) {
|
||||
waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
|
||||
List<AccessibilityNodeInfo> result = mFindAccessibilityNodeInfosResult;
|
||||
clearLocked();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) {
|
||||
synchronized (mLock) {
|
||||
if (interactionId > mInteractionId) {
|
||||
mPerformAccessibilityActionResult = succeeded;
|
||||
mInteractionId = interactionId;
|
||||
}
|
||||
mLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getPerformAccessibilityActionResult(int interactionId) {
|
||||
synchronized (mLock) {
|
||||
waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
|
||||
final boolean result = mPerformAccessibilityActionResult;
|
||||
clearLocked();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public void clearLocked() {
|
||||
mInteractionId = -1;
|
||||
mFindAccessibilityNodeInfoResult = null;
|
||||
mFindAccessibilityNodeInfosResult = null;
|
||||
mPerformAccessibilityActionResult = false;
|
||||
}
|
||||
|
||||
private void waitForResultTimedLocked(long waitTimeMillis, int interactionId) {
|
||||
final long startTimeMillis = SystemClock.uptimeMillis();
|
||||
while (true) {
|
||||
try {
|
||||
if (mInteractionId == interactionId) {
|
||||
return;
|
||||
}
|
||||
final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
|
||||
waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis;
|
||||
if (waitTimeMillis <= 0) {
|
||||
return;
|
||||
}
|
||||
mLock.wait(waitTimeMillis);
|
||||
} catch (InterruptedException ie) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user