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

Commit 53ae4c63 authored by Wenyu Zhang's avatar Wenyu Zhang Committed by Android (Google) Code Review
Browse files

Merge "a11y: Support auto right-click" into main

parents ae5d014b afc83334
Loading
Loading
Loading
Loading
+36 −4
Original line number Diff line number Diff line
@@ -17,11 +17,16 @@
package com.android.server.accessibility.autoclick;

import static android.view.MotionEvent.BUTTON_PRIMARY;
import static android.view.MotionEvent.BUTTON_SECONDARY;
import static android.view.accessibility.AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
import static android.view.accessibility.AccessibilityManager.AUTOCLICK_DELAY_DEFAULT;
import static android.view.accessibility.AccessibilityManager.AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT_DEFAULT;

import static com.android.server.accessibility.autoclick.AutoclickIndicatorView.SHOW_INDICATOR_DELAY_TIME;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_LEFT_CLICK;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_RIGHT_CLICK;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AutoclickType;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.ClickPanelControllerInterface;

import android.accessibilityservice.AccessibilityTrace;
import android.annotation.NonNull;
@@ -84,6 +89,23 @@ public class AutoclickController extends BaseEventStreamTransformation {
    @VisibleForTesting AutoclickTypePanel mAutoclickTypePanel;
    private WindowManager mWindowManager;

    // Default click type is left-click.
    private @AutoclickType int mActiveClickType = AUTOCLICK_TYPE_LEFT_CLICK;

    @VisibleForTesting
    final ClickPanelControllerInterface clickPanelController =
            new ClickPanelControllerInterface() {
                @Override
                public void handleAutoclickTypeChange(@AutoclickType int clickType) {
                    mActiveClickType = clickType;
                }

                @Override
                public void toggleAutoclickPause() {
                    // TODO(b/388872274): allows users to pause the autoclick.
                }
            };

    public AutoclickController(Context context, int userId, AccessibilityTraceManager trace) {
        mTrace = trace;
        mContext = context;
@@ -124,7 +146,8 @@ public class AutoclickController extends BaseEventStreamTransformation {
        mAutoclickIndicatorView = new AutoclickIndicatorView(mContext);

        mWindowManager = mContext.getSystemService(WindowManager.class);
        mAutoclickTypePanel = new AutoclickTypePanel(mContext, mWindowManager);
        mAutoclickTypePanel =
                new AutoclickTypePanel(mContext, mWindowManager, clickPanelController);

        mAutoclickTypePanel.show();
        mWindowManager.addView(mAutoclickIndicatorView, mAutoclickIndicatorView.getLayoutParams());
@@ -644,6 +667,15 @@ public class AutoclickController extends BaseEventStreamTransformation {

            final long now = SystemClock.uptimeMillis();

            // TODO(b/395094903): 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 types.
            final int actionButton =
                    mActiveClickType == AUTOCLICK_TYPE_RIGHT_CLICK
                            ? BUTTON_SECONDARY
                            : BUTTON_PRIMARY;

            MotionEvent downEvent =
                    MotionEvent.obtain(
                            /* downTime= */ now,
@@ -653,7 +685,7 @@ public class AutoclickController extends BaseEventStreamTransformation {
                            mTempPointerProperties,
                            mTempPointerCoords,
                            mMetaState,
                            BUTTON_PRIMARY,
                            actionButton,
                            /* xPrecision= */ 1.0f,
                            /* yPrecision= */ 1.0f,
                            mLastMotionEvent.getDeviceId(),
@@ -663,11 +695,11 @@ public class AutoclickController extends BaseEventStreamTransformation {

            MotionEvent pressEvent = MotionEvent.obtain(downEvent);
            pressEvent.setAction(MotionEvent.ACTION_BUTTON_PRESS);
            pressEvent.setActionButton(BUTTON_PRIMARY);
            pressEvent.setActionButton(actionButton);

            MotionEvent releaseEvent = MotionEvent.obtain(downEvent);
            releaseEvent.setAction(MotionEvent.ACTION_BUTTON_RELEASE);
            releaseEvent.setActionButton(BUTTON_PRIMARY);
            releaseEvent.setActionButton(actionButton);
            releaseEvent.setButtonState(0);

            MotionEvent upEvent = MotionEvent.obtain(downEvent);
+65 −10
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.accessibility.autoclick;

import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;

import android.annotation.IntDef;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
@@ -39,12 +40,40 @@ public class AutoclickTypePanel {

    private final String TAG = AutoclickTypePanel.class.getSimpleName();

    public static final int AUTOCLICK_TYPE_LEFT_CLICK = 0;
    public static final int AUTOCLICK_TYPE_RIGHT_CLICK = 1;
    public static final int AUTOCLICK_TYPE_DOUBLE_CLICK = 2;
    public static final int AUTOCLICK_TYPE_DRAG = 3;
    public static final int AUTOCLICK_TYPE_SCROLL = 4;

    // Types of click the AutoclickTypePanel supports.
    @IntDef({
        AUTOCLICK_TYPE_LEFT_CLICK,
        AUTOCLICK_TYPE_RIGHT_CLICK,
        AUTOCLICK_TYPE_DOUBLE_CLICK,
        AUTOCLICK_TYPE_DRAG,
        AUTOCLICK_TYPE_SCROLL,
    })
    public @interface AutoclickType {}

    // An interface exposed to {@link AutoclickController) to handle different actions on the panel,
    // including changing autoclick type, pausing/resuming autoclick.
    public interface ClickPanelControllerInterface {
        // Allows users to change a different autoclick type.
        void handleAutoclickTypeChange(@AutoclickType int clickType);

        // Allows users to pause/resume the autoclick.
        void toggleAutoclickPause();
    }

    private final Context mContext;

    private final View mContentView;

    private final WindowManager mWindowManager;

    private final ClickPanelControllerInterface mClickPanelController;

    // Whether the panel is expanded or not.
    private boolean mExpanded = false;

@@ -56,9 +85,13 @@ public class AutoclickTypePanel {

    private LinearLayout mSelectedButton;

    public AutoclickTypePanel(Context context, WindowManager windowManager) {
    public AutoclickTypePanel(
            Context context,
            WindowManager windowManager,
            ClickPanelControllerInterface clickPanelController) {
        mContext = context;
        mWindowManager = windowManager;
        mClickPanelController = clickPanelController;

        mContentView =
                LayoutInflater.from(context)
@@ -76,26 +109,35 @@ public class AutoclickTypePanel {
    }

    private void initializeButtonState() {
        mLeftClickButton.setOnClickListener(v -> togglePanelExpansion(mLeftClickButton));
        mRightClickButton.setOnClickListener(v -> togglePanelExpansion(mRightClickButton));
        mDoubleClickButton.setOnClickListener(v -> togglePanelExpansion(mDoubleClickButton));
        mScrollButton.setOnClickListener(v -> togglePanelExpansion(mScrollButton));
        mDragButton.setOnClickListener(v -> togglePanelExpansion(mDragButton));
        mLeftClickButton.setOnClickListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_LEFT_CLICK));
        mRightClickButton.setOnClickListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_RIGHT_CLICK));
        mDoubleClickButton.setOnClickListener(
                v -> togglePanelExpansion(AUTOCLICK_TYPE_DOUBLE_CLICK));
        mScrollButton.setOnClickListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_SCROLL));
        mDragButton.setOnClickListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_DRAG));

        // TODO(b/388872274): registers listener for pause button and allows users to pause/resume
        // the autoclick.
        // TODO(b/388847771): registers listener for position button and allows users to move the
        // panel to a different position.

        // Initializes panel as collapsed state and only displays the left click button.
        hideAllClickTypeButtons();
        mLeftClickButton.setVisibility(View.VISIBLE);
        setSelectedButton(/* selectedButton= */ mLeftClickButton);
        setSelectedClickType(AUTOCLICK_TYPE_LEFT_CLICK);
    }

    /** Sets the selected button and updates the newly and previously selected button styling. */
    private void setSelectedButton(@NonNull LinearLayout selectedButton) {
    private void setSelectedClickType(@AutoclickType int clickType) {
        final LinearLayout selectedButton = getButtonFromClickType(clickType);

        // Updates the previously selected button styling.
        if (mSelectedButton != null) {
            toggleSelectedButtonStyle(mSelectedButton, /* isSelected= */ false);
        }

        mSelectedButton = selectedButton;
        mClickPanelController.handleAutoclickTypeChange(clickType);

        // Updates the newly selected button styling.
        toggleSelectedButtonStyle(selectedButton, /* isSelected= */ true);
@@ -130,7 +172,9 @@ public class AutoclickTypePanel {
    }

    /** Toggles the panel expanded or collapsed state. */
    private void togglePanelExpansion(LinearLayout button) {
    private void togglePanelExpansion(@AutoclickType int clickType) {
        final LinearLayout button = getButtonFromClickType(clickType);

        if (mExpanded) {
            // If the panel is already in expanded state, we should collapse it by hiding all
            // buttons except the one user selected.
@@ -138,7 +182,7 @@ public class AutoclickTypePanel {
            button.setVisibility(View.VISIBLE);

            // Sets the newly selected button.
            setSelectedButton(/* selectedButton= */ button);
            setSelectedClickType(clickType);
        } else {
            // If the panel is already collapsed, we just need to expand it.
            showAllClickTypeButtons();
@@ -166,6 +210,17 @@ public class AutoclickTypePanel {
        mScrollButton.setVisibility(View.VISIBLE);
    }

    private LinearLayout getButtonFromClickType(@AutoclickType int clickType) {
        return switch (clickType) {
            case AUTOCLICK_TYPE_LEFT_CLICK -> mLeftClickButton;
            case AUTOCLICK_TYPE_RIGHT_CLICK -> mRightClickButton;
            case AUTOCLICK_TYPE_DOUBLE_CLICK -> mDoubleClickButton;
            case AUTOCLICK_TYPE_DRAG -> mDragButton;
            case AUTOCLICK_TYPE_SCROLL -> mScrollButton;
            default -> throw new IllegalArgumentException("Unknown clickType " + clickType);
        };
    }

    @VisibleForTesting
    boolean getExpansionStateForTesting() {
        return mExpanded;
+31 −1
Original line number Diff line number Diff line
@@ -18,6 +18,11 @@ package com.android.server.accessibility.autoclick;

import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;

import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_LEFT_CLICK;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AutoclickType;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.ClickPanelControllerInterface;

import static com.google.common.truth.Truth.assertThat;

import android.content.Context;
@@ -59,11 +64,25 @@ public class AutoclickTypePanelTest {
    private LinearLayout mDragButton;
    private LinearLayout mScrollButton;

    private @AutoclickType int mActiveClickType = AUTOCLICK_TYPE_LEFT_CLICK;

    private final ClickPanelControllerInterface clickPanelController =
            new ClickPanelControllerInterface() {
                @Override
                public void handleAutoclickTypeChange(@AutoclickType int clickType) {
                    mActiveClickType = clickType;
                }

                @Override
                public void toggleAutoclickPause() {}
            };

    @Before
    public void setUp() {
        mTestableContext.addMockSystemService(Context.WINDOW_SERVICE, mMockWindowManager);

        mAutoclickTypePanel = new AutoclickTypePanel(mTestableContext, mMockWindowManager);
        mAutoclickTypePanel =
                new AutoclickTypePanel(mTestableContext, mMockWindowManager, clickPanelController);
        View contentView = mAutoclickTypePanel.getContentViewForTesting();
        mLeftClickButton = contentView.findViewById(R.id.accessibility_autoclick_left_click_layout);
        mRightClickButton =
@@ -136,6 +155,17 @@ public class AutoclickTypePanelTest {
        verifyButtonHasSelectedStyle(mScrollButton);
    }

    @Test
    public void togglePanelExpansion_selectButton_correctActiveClickType() {
        // By first click, the panel is expanded.
        mLeftClickButton.callOnClick();

        // Clicks any button in the expanded state to select a type button.
        mScrollButton.callOnClick();

        assertThat(mActiveClickType).isEqualTo(AUTOCLICK_TYPE_SCROLL);
    }

    private void verifyButtonHasSelectedStyle(@NonNull LinearLayout button) {
        GradientDrawable gradientDrawable = (GradientDrawable) button.getBackground();
        assertThat(gradientDrawable.getColor().getDefaultColor())