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

Commit adb12131 authored by Wenyu Zhang's avatar Wenyu Zhang
Browse files

[3/3] autoclick: Implement ignore minor cursor movement setting

1. When ignore cursor movement is OFF: The mouse can move anywhere
inside the ring indicator without resetting the click timer and the
center dot will follow the mouse.

2. When ignore cursor movement is ON: The mouse can move anywhere
inside the ring indicator without resetting the click timer and
the center dot stays in the center.

Demo: http://b/430320631#comment6

Change-Id: I713d4a3c74710accb2d27bebaaad502cc73b178d
Bug: b/430320631
Test: AutoclickControllerTest
Flag: com.android.server.accessibility.enable_autoclick_indicator
parent 1719321a
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -1306,6 +1306,9 @@ public class AutoclickController extends BaseEventStreamTransformation implement

        public void setIgnoreMinorCursorMovement(boolean ignoreMinorCursorMovement) {
            mIgnoreMinorCursorMovement = ignoreMinorCursorMovement;
            if (mAutoclickIndicatorView != null) {
                mAutoclickIndicatorView.setIgnoreMinorCursorMovement(ignoreMinorCursorMovement);
            }
        }

        public void setRevertToLeftClick(boolean revertToLeftClick) {
@@ -1362,7 +1365,12 @@ public class AutoclickController extends BaseEventStreamTransformation implement
                mTempPointerCoords = new PointerCoords[1];
                mTempPointerCoords[0] = new PointerCoords();
            }
            if (mIgnoreMinorCursorMovement) {
                mTempPointerCoords[0].x = mAnchorCoords.x;
                mTempPointerCoords[0].y = mAnchorCoords.y;
            } else {
                mLastMotionEvent.getPointerCoords(pointerIndex, mTempPointerCoords[0]);
            }

            int actionButton = BUTTON_PRIMARY;
            switch (selectedClickType) {
+19 −4
Original line number Diff line number Diff line
@@ -86,6 +86,8 @@ public class AutoclickIndicatorView extends View {
    // Status of whether the visual indicator should display or not.
    private boolean showIndicator = false;

    private boolean mIgnoreMinorCursorMovement = false;

    public AutoclickIndicatorView(Context context) {
        super(context);

@@ -151,16 +153,25 @@ public class AutoclickIndicatorView extends View {
                    /* bottom= */ mSnapshotY + mRadius);
            canvas.drawArc(mRingRect, /* startAngle= */ -90, mSweepAngle, false, mPaint);

            // Draw a point indicator at the cursor's current location.
            // Draw a circle at (mMouseX, mMouseY) with default black fill and white border.
            // Draw a point indicator. When mIgnoreMinorCursorMovement is true, the point stays at
            // the center of the ring. Otherwise, it follows the mouse movement.
            final float pointX;
            final float pointY;
            if (mIgnoreMinorCursorMovement) {
                pointX = mSnapshotX;
                pointY = mSnapshotY;
            } else {
                pointX = mMouseX;
                pointY = mMouseY;
            }
            mPointPaint.setStyle(Paint.Style.FILL);
            mPointPaint.setColor(Color.BLACK);
            canvas.drawCircle(mMouseX, mMouseY, mPointSizePx, mPointPaint);
            canvas.drawCircle(pointX, pointY, mPointSizePx, mPointPaint);

            mPointPaint.setStyle(Paint.Style.STROKE);
            mPointPaint.setStrokeWidth(mPointStrokeWidthPx);
            mPointPaint.setColor(Color.WHITE);
            canvas.drawCircle(mMouseX, mMouseY, mPointSizePx, mPointPaint);
            canvas.drawCircle(pointX, pointY, mPointSizePx, mPointPaint);
        }
    }

@@ -231,4 +242,8 @@ public class AutoclickIndicatorView extends View {
        mAnimationDuration = Math.max(duration, MINIMAL_ANIMATION_DURATION);
        mAnimator.setDuration(mAnimationDuration);
    }

    public void setIgnoreMinorCursorMovement(boolean ignoreMinorCursorMovement) {
        mIgnoreMinorCursorMovement = ignoreMinorCursorMovement;
    }
}
+56 −0
Original line number Diff line number Diff line
@@ -507,6 +507,62 @@ public class AutoclickControllerTest {
        assertThat(mController.mClickScheduler.getIsActiveForTesting()).isFalse();
    }

    @Test
    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
    public void sendClick_ignoreMinorMovementTrue_clicksAtAnchorPosition() {
        initializeAutoclick();
        enableIgnoreMinorCursorMovement();

        // First move event to set the anchor.
        float anchorX = 50f;
        float anchorY = 60f;
        injectFakeMouseMoveEvent(anchorX, anchorY, MotionEvent.ACTION_HOVER_MOVE);

        // Second move event to trigger the click.
        float lastX = 80f;
        float lastY = 80f;
        injectFakeMouseMoveEvent(lastX, lastY, MotionEvent.ACTION_HOVER_MOVE);
        mController.mClickScheduler.run();

        // Verify click happened at anchor position, not the last position.
        assertThat(mMotionEventCaptor.downEvent).isNotNull();
        assertThat(mMotionEventCaptor.downEvent.getX()).isEqualTo(anchorX);
        assertThat(mMotionEventCaptor.downEvent.getY()).isEqualTo(anchorY);
    }

    @Test
    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
    public void sendClick_ignoreMinorMovementFalse_clicksAtLastPosition() {
        initializeAutoclick();

        // Ensure setting is off.
        Settings.Secure.putIntForUser(
                mTestableContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT,
                AccessibilityUtils.State.OFF,
                mTestableContext.getUserId());
        mController.onChangeForTesting(
                /* selfChange= */ true,
                Settings.Secure.getUriFor(
                        Settings.Secure.ACCESSIBILITY_AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT));

        // First move event to set the anchor.
        float anchorX = 50f;
        float anchorY = 60f;
        injectFakeMouseMoveEvent(anchorX, anchorY, MotionEvent.ACTION_HOVER_MOVE);

        // Second move event to trigger the click.
        float lastX = 80f;
        float lastY = 80f;
        injectFakeMouseMoveEvent(lastX, lastY, MotionEvent.ACTION_HOVER_MOVE);
        mController.mClickScheduler.run();

        // Verify click happened at the last position.
        assertThat(mMotionEventCaptor.downEvent).isNotNull();
        assertThat(mMotionEventCaptor.downEvent.getX()).isEqualTo(lastX);
        assertThat(mMotionEventCaptor.downEvent.getY()).isEqualTo(lastY);
    }

    @Test
    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
    public void onIgnoreCursorMovement_clickNotTriggered_whenMoveIsWithinSlop() {
+23 −0
Original line number Diff line number Diff line
@@ -101,6 +101,29 @@ public class AutoclickIndicatorViewTest {
        verify(mMockCanvas, times(2)).drawCircle(eq(300f), eq(400f), anyFloat(), any(Paint.class));
    }

    @Test
    public void onDraw_ignoreMinorMovementTrue_pointDoesNotMoveWithMouse() {
        mAutoclickIndicatorView.setIgnoreMinorCursorMovement(true);
        mAutoclickIndicatorView.setCoordination(100f, 200f);
        mAutoclickIndicatorView.redrawIndicator();
        // After indicator is shown, mouse moves to a new position.
        mAutoclickIndicatorView.setCoordination(300f, 400f);

        mAutoclickIndicatorView.onDraw(mMockCanvas);

        // Verify ring is drawn at the original snapshot position.
        ArgumentCaptor<RectF> rectCaptor = ArgumentCaptor.forClass(RectF.class);
        verify(mMockCanvas)
                .drawArc(rectCaptor.capture(), eq(-90f), anyFloat(), eq(false), any(Paint.class));
        RectF ringRect = rectCaptor.getValue();
        assertThat(ringRect.centerX()).isEqualTo(100f);
        assertThat(ringRect.centerY()).isEqualTo(200f);

        // Verify point is drawn at the original snapshot position, not the new mouse position.
        verify(mMockCanvas, times(2)).drawCircle(eq(100f), eq(200f), anyFloat(), any(Paint.class));
        verify(mMockCanvas, never()).drawCircle(eq(300f), eq(400f), anyFloat(), any(Paint.class));
    }

    @Test
    public void onDraw_showIndicatorFalse_doesNotDraw() {
        mAutoclickIndicatorView.clearIndicator();