diff options
| author | Svet Ganov <svetoslavganov@google.com> | 2014-09-03 21:33:00 -0700 |
|---|---|---|
| committer | Svet Ganov <svetoslavganov@google.com> | 2014-09-07 23:36:20 -0700 |
| commit | 7498efdc5e163d6b4a11db941c7d13c169d37284 (patch) | |
| tree | 2d94b4e0fe85ea98a438ecca6184d878ee5e61bd /core/java/android/view/accessibility | |
| parent | df11867be3f60b6bd5e24afca7820c29a28c85c7 (diff) | |
| download | frameworks_base-7498efdc5e163d6b4a11db941c7d13c169d37284.zip frameworks_base-7498efdc5e163d6b4a11db941c7d13c169d37284.tar.gz frameworks_base-7498efdc5e163d6b4a11db941c7d13c169d37284.tar.bz2 | |
Clicking on partially coverd views by other views or windows.
In touch exploration mode an accessibility service can move
accessibility focus in response to user gestures. In this case
when the user double-taps the system is sending down and up
events at the center of the acessibility focused view. This
works fine until the clicked view's center is covered by another
clickable view. In such a scenario the user thinks he is clicking
on one view but the click is handled by another. Terrible.
This change solves the problem of clicking on the wrong view
and also solves the problem of clicking on the wrong window.
The key idea is that when the system detects a double tap or
a double tap and hold it asks the accessibility focused node
(if such) to compute a point at which a click can be performed.
In respinse to that the node is asking the source view to
compute this.
If a view is partially covered by siblings or siblings of
predecessors that are clickable, the click point will be
properly computed to ensure the click occurs on the desired
view. The click point is also bounded in the interactive
part of the host window.
The current approach has rare edge cases that may produce false
positives or false negatives. For example, a portion of the
view may be covered by an interactive descendant of a
predecessor, which we do not compute (we check only siblings of
predecessors). Also a view may be handling raw touch events
instead of registering click listeners, which we cannot compute.
Despite these limitations this approach will work most of the
time and it is a huge improvement over just blindly sending
the down and up events in the center of the view.
Note that the additional computational complexity is incurred
only when the user wants to click on the accessibility focused
view which is very a rare event and this is a good tradeoff.
bug:15696993
Change-Id: I85927a77d6c24f7550b0d5f9f762722a8230830f
Diffstat (limited to 'core/java/android/view/accessibility')
3 files changed, 83 insertions, 0 deletions
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index db78ec5..374f7e0 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -17,6 +17,7 @@ package android.view.accessibility; import android.accessibilityservice.IAccessibilityServiceConnection; +import android.graphics.Point; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -98,6 +99,8 @@ public final class AccessibilityInteractionClient private boolean mPerformAccessibilityActionResult; + private Point mComputeClickPointResult; + private Message mSameThreadMessage; private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache = @@ -519,6 +522,43 @@ public final class AccessibilityInteractionClient return false; } + /** + * Computes a point in screen coordinates where sending a down/up events would + * perform a click on an {@link AccessibilityNodeInfo}. + * + * @param connectionId The id of a connection for interacting with the system. + * @param accessibilityWindowId A unique window id. Use + * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} + * to query the currently active window. + * @param accessibilityNodeId A unique view id or virtual descendant id from + * where to start the search. Use + * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} + * to start from the root. + * @return Point the click point of null if no such point. + */ + public Point computeClickPointInScreen(int connectionId, int accessibilityWindowId, + long accessibilityNodeId) { + try { + IAccessibilityServiceConnection connection = getConnection(connectionId); + if (connection != null) { + final int interactionId = mInteractionIdCounter.getAndIncrement(); + final boolean success = connection.computeClickPointInScreen( + accessibilityWindowId, accessibilityNodeId, + interactionId, this, Thread.currentThread().getId()); + if (success) { + return getComputeClickPointInScreenResultAndClear(interactionId); + } + } else { + if (DEBUG) { + Log.w(LOG_TAG, "No connection for connection id: " + connectionId); + } + } + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while calling remote computeClickPointInScreen", re); + } + return null; + } + public void clearCache() { sAccessibilityCache.clear(); } @@ -634,6 +674,34 @@ public final class AccessibilityInteractionClient } /** + * Gets the result of a request to compute a point in screen for clicking on a node. + * + * @param interactionId The interaction id to match the result with the request. + * @return The point or null if no such point. + */ + private Point getComputeClickPointInScreenResultAndClear(int interactionId) { + synchronized (mInstanceLock) { + final boolean success = waitForResultTimedLocked(interactionId); + Point result = success ? mComputeClickPointResult : null; + clearResultLocked(); + return result; + } + } + + /** + * {@inheritDoc} + */ + public void setComputeClickPointInScreenActionResult(Point point, int interactionId) { + synchronized (mInstanceLock) { + if (interactionId > mInteractionId) { + mComputeClickPointResult = point; + mInteractionId = interactionId; + } + mInstanceLock.notifyAll(); + } + } + + /** * Clears the result state. */ private void clearResultLocked() { @@ -641,6 +709,7 @@ public final class AccessibilityInteractionClient mFindAccessibilityNodeInfoResult = null; mFindAccessibilityNodeInfosResult = null; mPerformAccessibilityActionResult = false; + mComputeClickPointResult = null; } /** diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl index faf7789..66a3f46 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl @@ -17,6 +17,7 @@ package android.view.accessibility; import android.graphics.Region; +import android.graphics.Point; import android.os.Bundle; import android.view.MagnificationSpec; import android.view.accessibility.AccessibilityNodeInfo; @@ -53,4 +54,8 @@ oneway interface IAccessibilityInteractionConnection { void performAccessibilityAction(long accessibilityNodeId, int action, in Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid); + + void computeClickPointInScreen(long accessibilityNodeId, in Region bounds, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, + long interrogatingTid, in MagnificationSpec spec); } diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl index c1a3ab7..f480216 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl @@ -16,6 +16,7 @@ package android.view.accessibility; +import android.graphics.Point; import android.view.accessibility.AccessibilityNodeInfo; import java.util.List; @@ -51,4 +52,12 @@ oneway interface IAccessibilityInteractionConnectionCallback { * @param interactionId The interaction id to match the result with the request. */ void setPerformAccessibilityActionResult(boolean succeeded, int interactionId); + + /** + * Sets the result of a request to compute a point for clicking in a view. + * + * @param point The point of null if no such point. + * @param interactionId The interaction id to match the result with the request. + */ + void setComputeClickPointInScreenActionResult(in Point point, int interactionId); } |
