Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f855a3ce authored by Svetoslav's avatar Svetoslav Committed by Android Git Automerger
Browse files

am ded133c4: Fix broken activation of the selected view in accessibility mode.

* commit 'ded133c4':
  Fix broken activation of the selected view in accessibility mode.
parents be876df1 ded133c4
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -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();
+0 −95
Original line number Diff line number Diff line
@@ -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);
            }
+31 −0
Original line number Diff line number Diff line
@@ -401,6 +401,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.
     */
@@ -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.
+31 −138
Original line number Diff line number Diff line
@@ -5553,12 +5553,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;
        }
@@ -5578,6 +5589,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);
            }
@@ -5609,7 +5627,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();
@@ -5804,142 +5822,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return false;
    }
    /**
     * 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}.
@@ -8555,6 +8437,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) {
+20 −205

File changed.

Preview size limit exceeded, changes collapsed.

Loading