diff options
11 files changed, 125 insertions, 624 deletions
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 27a03b6..5f7a17d 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -54,10 +54,6 @@ interface IAccessibilityServiceConnection { int action, in Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); - boolean computeClickPointInScreen(int accessibilityWindowId, long accessibilityNodeId, - int interactionId, IAccessibilityInteractionConnectionCallback callback, - long threadId); - AccessibilityWindowInfo getWindow(int windowId); List<AccessibilityWindowInfo> getWindows(); diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 5e05683..68ad782 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -636,95 +636,6 @@ final class AccessibilityInteractionController { } } - public void computeClickPointInScreenClientThread(long accessibilityNodeId, - Region interactiveRegion, int interactionId, - IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, - long interrogatingTid, MagnificationSpec spec) { - Message message = mHandler.obtainMessage(); - message.what = PrivateHandler.MSG_COMPUTE_CLICK_POINT_IN_SCREEN; - - SomeArgs args = SomeArgs.obtain(); - args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); - args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId); - args.argi3 = interactionId; - args.arg1 = callback; - args.arg2 = spec; - args.arg3 = interactiveRegion; - - 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 - // client can handle the message to generate the result. - if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) { - AccessibilityInteractionClient.getInstanceForThread( - interrogatingTid).setSameThreadMessage(message); - } else { - mHandler.sendMessage(message); - } - } - - private void computeClickPointInScreenUiThread(Message message) { - SomeArgs args = (SomeArgs) message.obj; - final int accessibilityViewId = args.argi1; - final int virtualDescendantId = args.argi2; - final int interactionId = args.argi3; - final IAccessibilityInteractionConnectionCallback callback = - (IAccessibilityInteractionConnectionCallback) args.arg1; - final MagnificationSpec spec = (MagnificationSpec) args.arg2; - final Region interactiveRegion = (Region) args.arg3; - args.recycle(); - - boolean succeeded = false; - Point point = mTempPoint; - try { - if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { - return; - } - View target = null; - if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { - target = findViewByAccessibilityId(accessibilityViewId); - } else { - target = mViewRootImpl.mView; - } - if (target != null && isShown(target)) { - AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); - if (provider != null) { - // For virtual views just use the center of the bounds in screen. - AccessibilityNodeInfo node = null; - if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { - node = provider.createAccessibilityNodeInfo(virtualDescendantId); - } else { - node = provider.createAccessibilityNodeInfo( - AccessibilityNodeProvider.HOST_VIEW_ID); - } - if (node != null) { - succeeded = true; - Rect boundsInScreen = mTempRect; - node.getBoundsInScreen(boundsInScreen); - point.set(boundsInScreen.centerX(), boundsInScreen.centerY()); - } - } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { - // For a real view, ask the view to compute the click point. - succeeded = target.computeClickPointInScreenForAccessibility( - interactiveRegion, point); - } - } - } finally { - try { - Point result = null; - if (succeeded) { - applyAppScaleAndMagnificationSpecIfNeeded(point, spec); - result = point; - } - callback.setComputeClickPointInScreenActionResult(result, interactionId); - } catch (RemoteException re) { - /* ignore - the other side will time out */ - } - } - } - private View findViewByAccessibilityId(int accessibilityId) { View root = mViewRootImpl.mView; if (root == null) { @@ -1201,7 +1112,6 @@ final class AccessibilityInteractionController { private final static int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4; private final static int MSG_FIND_FOCUS = 5; private final static int MSG_FOCUS_SEARCH = 6; - private final static int MSG_COMPUTE_CLICK_POINT_IN_SCREEN = 7; public PrivateHandler(Looper looper) { super(looper); @@ -1223,8 +1133,6 @@ final class AccessibilityInteractionController { return "MSG_FIND_FOCUS"; case MSG_FOCUS_SEARCH: return "MSG_FOCUS_SEARCH"; - case MSG_COMPUTE_CLICK_POINT_IN_SCREEN: - return "MSG_COMPUTE_CLICK_POINT_IN_SCREEN"; default: throw new IllegalArgumentException("Unknown message type: " + type); } @@ -1252,9 +1160,6 @@ final class AccessibilityInteractionController { case MSG_FOCUS_SEARCH: { focusSearchUiThread(message); } break; - case MSG_COMPUTE_CLICK_POINT_IN_SCREEN: { - computeClickPointInScreenUiThread(message); - } break; default: throw new IllegalArgumentException("Unknown message type: " + type); } diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 1c5c41c..5e45c8f 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -402,6 +402,23 @@ public final class MotionEvent extends InputEvent implements Parcelable { public static final int FLAG_TAINTED = 0x80000000; /** + * Private flag indicating that this event was synthesized by the system and + * should be delivered to the accessibility focused view first. When being + * dispatched such an event is not handled by predecessors of the accessibility + * focused view and after the event reaches that view the flag is cleared and + * normal event dispatch is performed. This ensures that the platform can click + * on any view that has accessibility focus which is semantically equivalent to + * asking the view to perform a click accessibility action but more generic as + * views not implementing click action correctly can still be activated. + * + * @hide + * @see #isTargetAccessibilityFocus() + * @see #setTargetAccessibilityFocus(boolean) + */ + public static final int FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000; + + + /** * Flag indicating the motion event intersected the top edge of the screen. */ public static final int EDGE_TOP = 0x00000001; @@ -1766,6 +1783,20 @@ public final class MotionEvent extends InputEvent implements Parcelable { nativeSetFlags(mNativePtr, tainted ? flags | FLAG_TAINTED : flags & ~FLAG_TAINTED); } + /** @hide */ + public final boolean isTargetAccessibilityFocus() { + final int flags = getFlags(); + return (flags & FLAG_TARGET_ACCESSIBILITY_FOCUS) != 0; + } + + /** @hide */ + public final void setTargetAccessibilityFocus(boolean targetsFocus) { + final int flags = getFlags(); + nativeSetFlags(mNativePtr, targetsFocus + ? flags | FLAG_TARGET_ACCESSIBILITY_FOCUS + : flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS); + } + /** * Returns the time (in ms) when the user originally pressed down to start * a stream of position events. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 5b26ebb..97e1bc0 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5522,12 +5522,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Gets the location of this view in screen coordintates. + * Gets the location of this view in screen coordinates. * * @param outRect The output location * @hide */ public void getBoundsOnScreen(Rect outRect) { + getBoundsOnScreen(outRect, false); + } + + /** + * Gets the location of this view in screen coordinates. + * + * @param outRect The output location + * @param clipToParent Whether to clip child bounds to the parent ones. + * @hide + */ + public void getBoundsOnScreen(Rect outRect, boolean clipToParent) { if (mAttachInfo == null) { return; } @@ -5547,6 +5558,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, position.offset(-parentView.mScrollX, -parentView.mScrollY); + if (clipToParent) { + position.left = Math.max(position.left, 0); + position.top = Math.max(position.top, 0); + position.right = Math.min(position.right, parentView.getWidth()); + position.bottom = Math.min(position.bottom, parentView.getHeight()); + } + if (!parentView.hasIdentityMatrix()) { parentView.getMatrix().mapRect(position); } @@ -5580,7 +5598,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, getDrawingRect(bounds); info.setBoundsInParent(bounds); - getBoundsOnScreen(bounds); + getBoundsOnScreen(bounds, true); info.setBoundsInScreen(bounds); ViewParent parent = getParentForAccessibility(); @@ -5776,142 +5794,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Computes a point on which a sequence of a down/up event can be sent to - * trigger clicking this view. This method is for the exclusive use by the - * accessibility layer to determine where to send a click event in explore - * by touch mode. - * - * @param interactiveRegion The interactive portion of this window. - * @param outPoint The point to populate. - * @return True of such a point exists. - */ - boolean computeClickPointInScreenForAccessibility(Region interactiveRegion, - Point outPoint) { - // Since the interactive portion of the view is a region but as a view - // may have a transformation matrix which cannot be applied to a - // region we compute the view bounds rectangle and all interactive - // predecessor's and sibling's (siblings of predecessors included) - // rectangles that intersect the view bounds. At the - // end if the view was partially covered by another interactive - // view we compute the view's interactive region and pick a point - // on its boundary path as regions do not offer APIs to get inner - // points. Note that the the code is optimized to fail early and - // avoid unnecessary allocations plus computations. - - // The current approach has 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. Also a view may be handling - // raw touch events instead 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. - - // Cannot click on an unattached view. - if (mAttachInfo == null) { - return false; - } - - // Attached to an invisible window means this view is not visible. - if (mAttachInfo.mWindowVisibility != View.VISIBLE) { - return false; - } - - RectF bounds = mAttachInfo.mTmpTransformRect; - bounds.set(0, 0, getWidth(), getHeight()); - List<RectF> intersections = mAttachInfo.mTmpRectList; - intersections.clear(); - - if (mParent instanceof ViewGroup) { - ViewGroup parentGroup = (ViewGroup) mParent; - if (!parentGroup.translateBoundsAndIntersectionsInWindowCoordinates( - this, bounds, intersections)) { - intersections.clear(); - return false; - } - } - - // Take into account the window location. - final int dx = mAttachInfo.mWindowLeft; - final int dy = mAttachInfo.mWindowTop; - bounds.offset(dx, dy); - offsetRects(intersections, dx, dy); - - if (intersections.isEmpty() && interactiveRegion == null) { - outPoint.set((int) bounds.centerX(), (int) bounds.centerY()); - } else { - // This view is partially covered by other views, then compute - // the not covered region and pick a point on its boundary. - Region region = new Region(); - region.set((int) bounds.left, (int) bounds.top, - (int) bounds.right, (int) bounds.bottom); - - final int intersectionCount = intersections.size(); - for (int i = intersectionCount - 1; i >= 0; i--) { - RectF intersection = intersections.remove(i); - region.op((int) intersection.left, (int) intersection.top, - (int) intersection.right, (int) intersection.bottom, - Region.Op.DIFFERENCE); - } - - // If the view is completely covered, done. - if (region.isEmpty()) { - return false; - } - - // Take into account the interactive portion of the window - // as the rest is covered by other windows. If no such a region - // then the whole window is interactive. - if (interactiveRegion != null) { - region.op(interactiveRegion, Region.Op.INTERSECT); - } - - // Take into account the window bounds. - final View root = getRootView(); - if (root != null) { - region.op(dx, dy, root.getWidth() + dx, root.getHeight() + dy, Region.Op.INTERSECT); - } - - // If the view is completely covered, done. - if (region.isEmpty()) { - return false; - } - - // Try a shortcut here. - if (region.isRect()) { - Rect regionBounds = mAttachInfo.mTmpInvalRect; - region.getBounds(regionBounds); - outPoint.set(regionBounds.centerX(), regionBounds.centerY()); - return true; - } - - // Get the a point on the region boundary path. - Path path = region.getBoundaryPath(); - PathMeasure pathMeasure = new PathMeasure(path, false); - final float[] coordinates = mAttachInfo.mTmpTransformLocation; - - // Without loss of generality pick a point. - final float point = pathMeasure.getLength() * 0.01f; - if (!pathMeasure.getPosTan(point, coordinates, null)) { - return false; - } - - outPoint.set(Math.round(coordinates[0]), Math.round(coordinates[1])); - } - - return true; - } - - static void offsetRects(List<RectF> rects, float offsetX, float offsetY) { - final int rectCount = rects.size(); - for (int i = 0; i < rectCount; i++) { - RectF intersection = rects.get(i); - intersection.offset(offsetX, offsetY); - } - } - - /** * Returns the delegate for implementing accessibility support via * composition. For more details see {@link AccessibilityDelegate}. * @@ -8525,6 +8407,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return True if the event was handled by the view, false otherwise. */ public boolean dispatchTouchEvent(MotionEvent event) { + // If the event should be handled by accessibility focus first. + if (event.isTargetAccessibilityFocus()) { + // We don't have focus or no virtual descendant has it, do not handle the event. + if (!isAccessibilityFocused() && !(getViewRootImpl() != null && getViewRootImpl() + .getAccessibilityFocusedHost() == this)) { + return false; + } + // We have focus and got the event, then use normal event dispatch. + event.setTargetAccessibilityFocus(false); + } + boolean result = false; if (mInputEventConsistencyVerifier != null) { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 49e4efa..879fe19 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -474,9 +474,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @ViewDebug.ExportedProperty(category = "layout") private int mChildCountWithTransientState = 0; - // Iterator over the children in decreasing Z order (top children first). - private OrderedChildIterator mOrderedChildIterator; - /** * Currently registered axes for nested scrolling. Flag set consisting of * {@link #SCROLL_AXIS_HORIZONTAL} {@link #SCROLL_AXIS_VERTICAL} or {@link #SCROLL_AXIS_NONE} @@ -782,144 +779,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** - * Translates the given bounds and intersections from child coordinates to - * local coordinates. In case any interactive sibling of the calling child - * covers the latter, a new intersections is added to the intersection list. - * This method is for the exclusive use by the accessibility layer to compute - * a point where a sequence of down and up events would click on a view. - * - * @param child The child making the call. - * @param bounds The bounds to translate in child coordinates. - * @param intersections The intersections of interactive views covering the child. - * @return True if the bounds and intersections were computed, false otherwise. - */ - boolean translateBoundsAndIntersectionsInWindowCoordinates(View child, - RectF bounds, List<RectF> intersections) { - // Not attached, done. - if (mAttachInfo == null) { - return false; - } - - if (getAlpha() <= 0 || getTransitionAlpha() <= 0 || - getVisibility() != VISIBLE) { - // Cannot click on a view with an invisible predecessor. - return false; - } - - // Compensate for the child transformation. - if (!child.hasIdentityMatrix()) { - Matrix matrix = child.getMatrix(); - matrix.mapRect(bounds); - final int intersectionCount = intersections.size(); - for (int i = 0; i < intersectionCount; i++) { - RectF intersection = intersections.get(i); - matrix.mapRect(intersection); - } - } - - // Translate the bounds from child to parent coordinates. - final int dx = child.mLeft - mScrollX; - final int dy = child.mTop - mScrollY; - bounds.offset(dx, dy); - offsetRects(intersections, dx, dy); - - // If the bounds do not intersect our bounds, done. - if (!bounds.intersects(0, 0, getWidth(), getHeight())) { - return false; - } - - // Clip the bounds by our bounds. - bounds.left = Math.max(bounds.left, 0); - bounds.top = Math.max(bounds.top, 0); - bounds.right = Math.min(bounds.right, getWidth()); - bounds.bottom = Math.min(bounds.bottom, getHeight()); - - Iterator<View> iterator = obtainOrderedChildIterator(); - while (iterator.hasNext()) { - View sibling = iterator.next(); - - // We care only about siblings over the child. - if (sibling == child) { - break; - } - - // Ignore invisible views as they are not interactive. - if (!isVisible(sibling)) { - continue; - } - - // Compute the sibling bounds in its coordinates. - RectF siblingBounds = mAttachInfo.mTmpTransformRect1; - siblingBounds.set(0, 0, sibling.getWidth(), sibling.getHeight()); - - // Translate the sibling bounds to our coordinates. - offsetChildRectToMyCoords(siblingBounds, sibling); - - // Compute the intersection between the child and the sibling. - if (siblingBounds.intersect(bounds)) { - // Conservatively we consider an overlapping sibling to be - // interactive and ignore it. This is not ideal as if the - // sibling completely covers the view despite handling no - // touch events we will not be able to click on the view. - intersections.add(siblingBounds); - } - } - - releaseOrderedChildIterator(); - - if (mParent instanceof ViewGroup) { - ViewGroup parentGroup = (ViewGroup) mParent; - return parentGroup.translateBoundsAndIntersectionsInWindowCoordinates( - this, bounds, intersections); - } - - return true; - } - - private void offsetChildRectToMyCoords(RectF rect, View child) { - if (!child.hasIdentityMatrix()) { - child.getMatrix().mapRect(rect); - } - final int childDx = child.mLeft - mScrollX; - final int childDy = child.mTop - mScrollY; - rect.offset(childDx, childDy); - } - - private static boolean isVisible(View view) { - return (view.getAlpha() > 0 && view.getTransitionAlpha() > 0 && - view.getVisibility() == VISIBLE); - } - - /** - * Obtains the iterator to traverse the children in a descending Z order. - * Only one party can use the iterator at any given time and you cannot - * modify the children while using this iterator. Acquisition if already - * obtained is an error. - * - * @return The child iterator. - */ - OrderedChildIterator obtainOrderedChildIterator() { - if (mOrderedChildIterator == null) { - mOrderedChildIterator = new OrderedChildIterator(); - } else if (mOrderedChildIterator.isInitialized()) { - throw new IllegalStateException("Already obtained"); - } - mOrderedChildIterator.initialize(); - return mOrderedChildIterator; - } - - /** - * Releases the iterator to traverse the children in a descending Z order. - * Release if not obtained is an error. - */ - void releaseOrderedChildIterator() { - if (mOrderedChildIterator == null || !mOrderedChildIterator.isInitialized()) { - throw new IllegalStateException("Not obtained"); - } - mOrderedChildIterator.release(); - } - - /** * Called when a child view has changed whether or not it is tracking transient state. */ public void childHasTransientStateChanged(View child, boolean childHasTransientState) { @@ -2074,6 +1933,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } + // Whether this event should be handled by the accessibility focus first. + final boolean targetAccessibilityFocus = ev.isTargetAccessibilityFocus(); + boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); @@ -2090,19 +1952,24 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Check for interception. final boolean intercepted; - if (actionMasked == MotionEvent.ACTION_DOWN - || mFirstTouchTarget != null) { - final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; - if (!disallowIntercept) { - intercepted = onInterceptTouchEvent(ev); - ev.setAction(action); // restore action in case it was changed + if (!targetAccessibilityFocus) { + if (actionMasked == MotionEvent.ACTION_DOWN + || mFirstTouchTarget != null) { + final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; + if (!disallowIntercept) { + intercepted = onInterceptTouchEvent(ev); + ev.setAction(action); // restore action in case it was changed + } else { + intercepted = false; + } } else { - intercepted = false; + // There are no touch targets and this action is not an initial down + // so this view group continues to intercept touches. + intercepted = true; } } else { - // There are no touch targets and this action is not an initial down - // so this view group continues to intercept touches. - intercepted = true; + // If event should reach the accessibility focus first, do not intercept it. + intercepted = false; } // Check for cancelation. @@ -2116,7 +1983,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (!canceled && !intercepted) { if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) - || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { + || actionMasked == MotionEvent.ACTION_HOVER_MOVE + || targetAccessibilityFocus) { final int actionIndex = ev.getActionIndex(); // always 0 for down final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) : TouchTarget.ALL_POINTER_IDS; @@ -7408,57 +7276,4 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager canvas.drawLines(sDebugLines, paint); } - - private final class OrderedChildIterator implements Iterator<View> { - private List<View> mOrderedChildList; - private boolean mUseCustomOrder; - private int mCurrentIndex; - private boolean mInitialized; - - public void initialize() { - mOrderedChildList = buildOrderedChildList(); - mUseCustomOrder = (mOrderedChildList == null) - && isChildrenDrawingOrderEnabled(); - mCurrentIndex = mChildrenCount - 1; - mInitialized = true; - } - - public void release() { - if (mOrderedChildList != null) { - mOrderedChildList.clear(); - } - mUseCustomOrder = false; - mCurrentIndex = 0; - mInitialized = false; - } - - public boolean isInitialized() { - return mInitialized; - } - - @Override - public boolean hasNext() { - return (mCurrentIndex >= 0); - } - - @Override - public View next() { - if (!hasNext()) { - throw new NoSuchElementException("No such element"); - } - return getChild(mCurrentIndex--); - } - - private View getChild(int index) { - final int childIndex = mUseCustomOrder - ? getChildDrawingOrder(mChildrenCount, index) : index; - return (mOrderedChildList == null) - ? mChildren[childIndex] : mOrderedChildList.get(childIndex); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 15e7060..f78c018 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2712,7 +2712,7 @@ public final class ViewRootImpl implements ViewParent, final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider(); if (provider == null) { - host.getBoundsOnScreen(bounds); + host.getBoundsOnScreen(bounds, true); } else if (mAccessibilityFocusedVirtualView != null) { mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds); } else { @@ -6844,26 +6844,6 @@ public final class ViewRootImpl implements ViewParent, } @Override - public void computeClickPointInScreen(long accessibilityNodeId, Region interactiveRegion, - int interactionId, IAccessibilityInteractionConnectionCallback callback, - int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { - ViewRootImpl viewRootImpl = mViewRootImpl.get(); - if (viewRootImpl != null && viewRootImpl.mView != null) { - viewRootImpl.getAccessibilityInteractionController() - .computeClickPointInScreenClientThread(accessibilityNodeId, - interactiveRegion, interactionId, callback, interrogatingPid, - interrogatingTid, spec); - } else { - // We cannot make the call and notify the caller so it does not wait. - try { - callback.setComputeClickPointInScreenActionResult(null, interactionId); - } catch (RemoteException re) { - /* best effort - ignore */ - } - } - } - - @Override public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 374f7e0..cefd34d 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -99,8 +99,6 @@ public final class AccessibilityInteractionClient private boolean mPerformAccessibilityActionResult; - private Point mComputeClickPointResult; - private Message mSameThreadMessage; private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache = @@ -522,43 +520,6 @@ 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(); } @@ -674,34 +635,6 @@ 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() { @@ -709,7 +642,6 @@ 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 66a3f46..cecc4af 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl @@ -54,8 +54,4 @@ 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 f480216..42ae1b3 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl @@ -52,12 +52,4 @@ 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); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 8c314cf..a712d78 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -170,6 +170,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final Rect mTempRect = new Rect(); + private final Rect mTempRect1 = new Rect(); + private final Point mTempPoint = new Point(); private final PackageManager mPackageManager; @@ -2535,57 +2537,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } @Override - public boolean computeClickPointInScreen(int accessibilityWindowId, - long accessibilityNodeId, int interactionId, - IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) - throws RemoteException { - final int resolvedWindowId; - IAccessibilityInteractionConnection connection = null; - Region partialInteractiveRegion = mTempRegion; - synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { - return false; - } - resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); - final boolean permissionGranted = - mSecurityPolicy.canRetrieveWindowContentLocked(this); - if (!permissionGranted) { - return false; - } else { - connection = getConnectionLocked(resolvedWindowId); - if (connection == null) { - return false; - } - } - if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( - resolvedWindowId, partialInteractiveRegion)) { - partialInteractiveRegion = null; - } - } - final int interrogatingPid = Binder.getCallingPid(); - final long identityToken = Binder.clearCallingIdentity(); - MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId); - try { - connection.computeClickPointInScreen(accessibilityNodeId, partialInteractiveRegion, - interactionId, callback, interrogatingPid, interrogatingTid, spec); - return true; - } catch (RemoteException re) { - if (DEBUG) { - Slog.e(LOG_TAG, "Error computeClickPointInScreen()."); - } - } finally { - Binder.restoreCallingIdentity(identityToken); - } - return false; - } - - @Override public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP); synchronized (mLock) { @@ -3238,38 +3189,36 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } synchronized (mLock) { - Point point = mClient.computeClickPointInScreen(mConnectionId, - focus.getWindowId(), focus.getSourceNodeId()); + Rect boundsInScreen = mTempRect; + focus.getBoundsInScreen(boundsInScreen); - if (point == null) { + // Clip to the window bounds. + Rect windowBounds = mTempRect1; + getWindowBounds(focus.getWindowId(), windowBounds); + boundsInScreen.intersect(windowBounds); + if (boundsInScreen.isEmpty()) { return false; } + // Apply magnification if needed. MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId()); if (spec != null && !spec.isNop()) { - point.offset((int) -spec.offsetX, (int) -spec.offsetY); - point.x = (int) (point.x * (1 / spec.scale)); - point.y = (int) (point.y * (1 / spec.scale)); + boundsInScreen.offset((int) -spec.offsetX, (int) -spec.offsetY); + boundsInScreen.scale(1 / spec.scale); } - // Make sure the point is within the window. - Rect windowBounds = mTempRect; - getWindowBounds(focus.getWindowId(), windowBounds); - if (!windowBounds.contains(point.x, point.y)) { - return false; - } - - // Make sure the point is within the screen. + // Clip to the screen bounds. Point screenSize = mTempPoint; mDefaultDisplay.getRealSize(screenSize); - if (point.x < 0 || point.x > screenSize.x - || point.y < 0 || point.y > screenSize.y) { + boundsInScreen.intersect(0, 0, screenSize.x, screenSize.y); + if (boundsInScreen.isEmpty()) { return false; } - outPoint.set(point.x, point.y); - return true; + outPoint.set(boundsInScreen.centerX(), boundsInScreen.centerY()); } + + return true; } private AccessibilityNodeInfo getAccessibilityFocusNotLocked() { diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java index b9ed89b..f18b5ef 100644 --- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java @@ -77,6 +77,10 @@ class TouchExplorer implements EventStreamTransformation { private static final int STATE_DELEGATING = 0x00000004; private static final int STATE_GESTURE_DETECTING = 0x00000005; + private static final int CLICK_LOCATION_NONE = 0; + private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1; + private static final int CLICK_LOCATION_LAST_TOUCH_EXPLORED = 2; + // The maximum of the cosine between the vectors of two moving // pointers so they can be considered moving in the same direction. private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4) @@ -942,12 +946,16 @@ class TouchExplorer implements EventStreamTransformation { * * @param prototype The prototype from which to create the injected events. * @param policyFlags The policy flags associated with the event. + * @param targetAccessibilityFocus Whether the event targets the accessibility focus. */ - private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) { + private void sendActionDownAndUp(MotionEvent prototype, int policyFlags, + boolean targetAccessibilityFocus) { // Tap with the pointer that last explored. final int pointerId = prototype.getPointerId(prototype.getActionIndex()); final int pointerIdBits = (1 << pointerId); + prototype.setTargetAccessibilityFocus(targetAccessibilityFocus); sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags); + prototype.setTargetAccessibilityFocus(targetAccessibilityFocus); sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); } @@ -1155,7 +1163,8 @@ class TouchExplorer implements EventStreamTransformation { final int pointerIndex = secondTapUp.findPointerIndex(pointerId); Point clickLocation = mTempPoint; - if (!computeClickLocation(clickLocation)) { + final int result = computeClickLocation(clickLocation); + if (result == CLICK_LOCATION_NONE) { return; } @@ -1171,7 +1180,8 @@ class TouchExplorer implements EventStreamTransformation { secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties, coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0, secondTapUp.getSource(), secondTapUp.getFlags()); - sendActionDownAndUp(event, policyFlags); + final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS); + sendActionDownAndUp(event, policyFlags, targetAccessibilityFocus); event.recycle(); } @@ -1216,7 +1226,7 @@ class TouchExplorer implements EventStreamTransformation { MAX_DRAGGING_ANGLE_COS); } - private boolean computeClickLocation(Point outLocation) { + private int computeClickLocation(Point outLocation) { MotionEvent lastExploreEvent = mInjectedPointerTracker.getLastInjectedHoverEventForClick(); if (lastExploreEvent != null) { final int lastExplorePointerIndex = lastExploreEvent.getActionIndex(); @@ -1224,14 +1234,17 @@ class TouchExplorer implements EventStreamTransformation { outLocation.y = (int) lastExploreEvent.getY(lastExplorePointerIndex); if (!mAms.accessibilityFocusOnlyInActiveWindow() || mLastTouchedWindowId == mAms.getActiveWindowId()) { - mAms.getAccessibilityFocusClickPointInScreen(outLocation); + if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) { + return CLICK_LOCATION_ACCESSIBILITY_FOCUS; + } else { + return CLICK_LOCATION_LAST_TOUCH_EXPLORED; + } } - return true; } if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) { - return true; + return CLICK_LOCATION_ACCESSIBILITY_FOCUS; } - return false; + return CLICK_LOCATION_NONE; } /** @@ -1310,14 +1323,13 @@ class TouchExplorer implements EventStreamTransformation { return; } - int clickLocationX; - int clickLocationY; - final int pointerId = mEvent.getPointerId(mEvent.getActionIndex()); final int pointerIndex = mEvent.findPointerIndex(pointerId); Point clickLocation = mTempPoint; - if (!computeClickLocation(clickLocation)) { + final int result = computeClickLocation(clickLocation); + + if (result == CLICK_LOCATION_NONE) { return; } |