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

Commit f3a4ad1e authored by Longbo Wei's avatar Longbo Wei Committed by Android (Google) Code Review
Browse files

Merge "autoclick: Support auto-drag for the panel" into main

parents cb986641 98b0b43e
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -277,6 +277,11 @@ public class AutoclickController extends BaseEventStreamTransformation {
                        mAutoclickIndicatorScheduler);
            }

            if (mAutoclickTypePanel != null && mAutoclickTypePanel.getIsDragging()
                    && event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) {
                mAutoclickTypePanel.onDragMove(event);
            }

            if (!isPaused()) {
                scheduleClick(event, policyFlags);

@@ -449,6 +454,9 @@ public class AutoclickController extends BaseEventStreamTransformation {
        if (mAutoclickIndicatorScheduler != null) {
            mAutoclickIndicatorScheduler.cancel();
        }
        if (mAutoclickTypePanel != null && mAutoclickTypePanel.getIsDragging()) {
            mAutoclickTypePanel.onDragEnd();
        }
    }

    /**
@@ -1164,6 +1172,12 @@ public class AutoclickController extends BaseEventStreamTransformation {
                clearLongPressState();
            }

            if (mAutoclickTypePanel.isHoveringDraggableArea()
                    && !mAutoclickTypePanel.getIsDragging()) {
                mAutoclickTypePanel.onDragStart(mLastMotionEvent);
                return;
            }

            // Always triggers left-click when the cursor hovers over the autoclick type panel, to
            // always allow users to change a different click type. Otherwise, if one chooses the
            // right-click, this user won't be able to rely on autoclick to select other click
@@ -1216,6 +1230,12 @@ public class AutoclickController extends BaseEventStreamTransformation {
                    break;
            }
            sendMotionEventsForClick(actionButton);

            // End panel drag operation if one is active (autoclick triggered after user stopped
            // moving during drag).
            if (mAutoclickTypePanel != null && mAutoclickTypePanel.getIsDragging()) {
                mAutoclickTypePanel.onDragEnd();
            }
        }

        /**
+65 −29
Original line number Diff line number Diff line
@@ -214,16 +214,6 @@ public class AutoclickTypePanel {
        // Set up touch event handling for the panel to allow the user to drag and reposition the
        // panel by touching and moving it.
        mContentView.setOnTouchListener(this::onPanelTouch);

        // Set hover behavior for the panel, show grab when hovering.
        mContentView.setOnHoverListener((v, event) -> {
            mCurrentCursor = PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_GRAB);
            v.setPointerIcon(mCurrentCursor);
            return false;
        });

        // Show default cursor when hovering over buttons.
        setDefaultCursorForButtons();
    }

    /**
@@ -263,7 +253,9 @@ public class AutoclickTypePanel {
        int yPosition = params.y;

        // Determine which half of the screen the panel is on.
        boolean isOnLeftHalf = params.x < screenWidth / 2;
        @Corner int visualCorner = getVisualCorner();
        boolean isOnLeftHalf =
                (visualCorner == CORNER_TOP_LEFT || visualCorner == CORNER_BOTTOM_LEFT);

        if (isOnLeftHalf) {
            // Snap to left edge. Set params.gravity to make sure x, y offsets from correct anchor.
@@ -313,6 +305,10 @@ public class AutoclickTypePanel {
        mPauseButton.setOnClickListener(v -> togglePause());

        setSelectedClickType(AUTOCLICK_TYPE_LEFT_CLICK);

        // Set up hover listeners on panel and buttons to dynamically change cursor icons.
        setupHoverListenersForCursor();

        // Remove spacing between buttons when initialized.
        adjustPanelSpacing(/* isExpanded= */ true);
    }
@@ -670,22 +666,6 @@ public class AutoclickTypePanel {
        }
    }

    private void setDefaultCursorForButtons() {
        View[] buttons = {
                mLeftClickButton, mRightClickButton, mDoubleClickButton,
                mScrollButton, mDragButton, mLongPressButton,
                mPauseButton, mPositionButton
        };

        for (View button : buttons) {
            button.setOnHoverListener((v, event) -> {
                mCurrentCursor = PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW);
                v.setPointerIcon(mCurrentCursor);
                return false;
            });
        }
    }

    /**
     * Starts drag operation, capturing initial positions and updating cursor icon.
     */
@@ -754,6 +734,63 @@ public class AutoclickTypePanel {
        }
    }

    /**
     * Returns true if cursor is over content view but not over any buttons.
     */
    public boolean isHoveringDraggableArea() {
        if (!mContentView.isHovered()) {
            return false;
        }

        View[] buttons = {mLeftClickButton, mRightClickButton, mDoubleClickButton,
                mScrollButton, mDragButton, mLongPressButton, mPauseButton, mPositionButton};
        for (View button : buttons) {
            if (button.isHovered()) {
                return false;
            }
        }
        return true;
    }

    /**
     * Sets up hover listeners to update cursor icons (grab for draggable areas, arrow for buttons).
     */
    private void setupHoverListenersForCursor() {
        View[] mAllButtons = new View[]{
                mLeftClickButton, mRightClickButton, mDoubleClickButton,
                mScrollButton, mDragButton, mLongPressButton,
                mPauseButton, mPositionButton
        };

        // Set hover behavior for the panel.
        mContentView.setOnHoverListener((v, event) -> {
            updateCursorIcon();
            return false;
        });

        // Set hover behavior for all buttons.
        for (View button : mAllButtons) {
            button.setOnHoverListener((v, event) -> {
                updateCursorIcon();
                return false;
            });
        }
    }

    /**
     * Updates cursor based on hover state: grab for draggable areas, arrow for buttons.
     */
    private void updateCursorIcon() {
        // Don't update cursor icon while dragging to avoid overriding the grabbing cursor during
        // drag.
        if (mIsDragging) {
            return;
        }
        int cursorType = isHoveringDraggableArea() ? PointerIcon.TYPE_GRAB : PointerIcon.TYPE_ARROW;
        mCurrentCursor = PointerIcon.getSystemIcon(mContext, cursorType);
        mContentView.setPointerIcon(mCurrentCursor);
    }

    @VisibleForTesting
    boolean getExpansionStateForTesting() {
        return mExpanded;
@@ -776,8 +813,7 @@ public class AutoclickTypePanel {
        return mParams;
    }

    @VisibleForTesting
    boolean getIsDraggingForTesting() {
    boolean getIsDragging() {
        return mIsDragging;
    }

+58 −0
Original line number Diff line number Diff line
@@ -41,9 +41,11 @@ import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;

@@ -1434,6 +1436,62 @@ public class AutoclickControllerTest {
        assertThat(scrollCaptor.eventCount).isEqualTo(countBeforeRunnable);
    }

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

        // Store initial position for comparison.
        WindowManager.LayoutParams initialParams =
                mController.mAutoclickTypePanel.getLayoutParamsForTesting();
        int initialX = initialParams.x;
        int initialY = initialParams.y;

        // Test onDragStart - should enable dragging and change cursor.
        MotionEvent dragStartEvent = MotionEvent.obtain(
                /* downTime= */ 0, /* eventTime= */ 0, MotionEvent.ACTION_DOWN,
                /* x= */ 100f, /* y= */ 100f, /* metaState= */ 0);
        mController.mAutoclickTypePanel.onDragStart(dragStartEvent);
        assertThat(mController.mAutoclickTypePanel.getIsDragging()).isTrue();
        assertThat(mController.mAutoclickTypePanel.getCurrentCursorForTesting().getType())
                .isEqualTo(PointerIcon.TYPE_GRABBING);

        // Test onDragMove - should update position and maintain drag state.
        MotionEvent dragMoveEvent = MotionEvent.obtain(
                /* downTime= */ 0, /* eventTime= */ 50, MotionEvent.ACTION_MOVE,
                /* x= */ 150f, /* y= */ 150f, /* metaState= */ 0);
        mController.mAutoclickTypePanel.onDragMove(dragMoveEvent);

        // Verify drag state maintained and gravity changed to absolute positioning
        assertThat(mController.mAutoclickTypePanel.getIsDragging()).isTrue();
        assertThat(mController.mAutoclickTypePanel.getCurrentCursorForTesting().getType())
                .isEqualTo(PointerIcon.TYPE_GRABBING);
        assertThat(mController.mAutoclickTypePanel.getLayoutParamsForTesting().gravity)
                .isEqualTo(Gravity.LEFT | Gravity.TOP);

        // Verify position coordinates actually changed from drag movement.
        WindowManager.LayoutParams dragParams =
                mController.mAutoclickTypePanel.getLayoutParamsForTesting();
        assertThat(dragParams.x).isNotEqualTo(initialX);
        assertThat(dragParams.y).isNotEqualTo(initialY);

        // Test onDragEnd - should reset state, change cursor, and snap to edge.
        mController.mAutoclickTypePanel.onDragEnd();
        assertThat(mController.mAutoclickTypePanel.getIsDragging()).isFalse();
        assertThat(mController.mAutoclickTypePanel.getCurrentCursorForTesting().getType())
                .isEqualTo(PointerIcon.TYPE_GRAB);

        // Verify panel snapped to edge.
        WindowManager.LayoutParams finalParams =
                mController.mAutoclickTypePanel.getLayoutParamsForTesting();
        boolean snappedToLeftEdge = (finalParams.gravity & Gravity.START) == Gravity.START;
        boolean snappedToRightEdge = (finalParams.gravity & Gravity.END) == Gravity.END;
        assertThat(snappedToLeftEdge || snappedToRightEdge).isTrue();

        dragStartEvent.recycle();
        dragMoveEvent.recycle();
    }

    @Test
    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
    public void exitButton_exitsScrollMode() {
+4 −1
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import androidx.annotation.NonNull;
import com.android.internal.R;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -345,7 +346,7 @@ public class AutoclickTypePanelTest {
        contentView.dispatchTouchEvent(moveEvent);

        // Verify position update.
        assertThat(mAutoclickTypePanel.getIsDraggingForTesting()).isTrue();
        assertThat(mAutoclickTypePanel.getIsDragging()).isTrue();
        assertThat(params.gravity).isEqualTo(Gravity.LEFT | Gravity.TOP);
        assertThat(params.x).isEqualTo(panelLocation[0] + delta);
        assertThat(params.y).isEqualTo(
@@ -354,6 +355,7 @@ public class AutoclickTypePanelTest {
    }

    @Test
    @Ignore ("b/424594372")
    public void dragAndEndAtRight_snapsToRightSide() {
        View contentView = mAutoclickTypePanel.getContentViewForTesting();
        WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting();
@@ -377,6 +379,7 @@ public class AutoclickTypePanelTest {
    }

    @Test
    @Ignore ("b/424594372")
    public void dragAndEndAtLeft_snapsToLeftSide() {
        View contentView = mAutoclickTypePanel.getContentViewForTesting();
        WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting();