a11y: Pause auto click with pause button

Pause autoclick when the pause button is clicked by canceling
any pending clicks and preventing scheduling of new clicks.

Demo: http://b/388872274#comment4

Bug: b/388872274
Test: AutoclickTypePanelTest
Flag: com.android.server.accessibility.enable_autoclick_indicator
Change-Id: I1cabc63a2656ea4e962a5565b84e5db70eb115ae
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index 4fa0d50..aa82df4 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -101,8 +101,15 @@
                 }
 
                 @Override
-                public void toggleAutoclickPause() {
-                    // TODO(b/388872274): allows users to pause the autoclick.
+                public void toggleAutoclickPause(boolean paused) {
+                    if (paused) {
+                        if (mClickScheduler != null) {
+                            mClickScheduler.cancel();
+                        }
+                        if (mAutoclickIndicatorScheduler != null) {
+                            mAutoclickIndicatorScheduler.cancel();
+                        }
+                    }
                 }
             };
 
@@ -133,7 +140,9 @@
                         mAutoclickIndicatorScheduler);
             }
 
-            handleMouseMotion(event, policyFlags);
+            if (!isPaused()) {
+                handleMouseMotion(event, policyFlags);
+            }
         } else if (mClickScheduler != null) {
             mClickScheduler.cancel();
         }
@@ -216,6 +225,11 @@
         }
     }
 
+    private boolean isPaused() {
+        // TODO (b/397460424): Unpause when hovering over panel.
+        return Flags.enableAutoclickIndicator() && mAutoclickTypePanel.isPaused();
+    }
+
     /**
      * Observes autoclick setting values, and updates ClickScheduler delay and indicator size
      * whenever the setting value changes.
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
index 23c5cc4..ba3e3d1 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
@@ -79,11 +79,20 @@
     // 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.
+        /**
+         * Allows users to change a different autoclick type.
+         *
+         * @param clickType The new autoclick type to use. Should be one of the values defined in
+         *                  {@link AutoclickType}.
+         */
         void handleAutoclickTypeChange(@AutoclickType int clickType);
 
-        // Allows users to pause/resume the autoclick.
-        void toggleAutoclickPause();
+        /**
+         * Allows users to pause or resume autoclick.
+         *
+         * @param paused {@code true} to pause autoclick, {@code false} to resume.
+         */
+        void toggleAutoclickPause(boolean paused);
     }
 
     private final Context mContext;
@@ -211,6 +220,10 @@
         mWindowManager.removeView(mContentView);
     }
 
+    public boolean isPaused() {
+        return mPaused;
+    }
+
     /** Toggles the panel expanded or collapsed state. */
     private void togglePanelExpansion(@AutoclickType int clickType) {
         final LinearLayout button = getButtonFromClickType(clickType);
@@ -234,6 +247,7 @@
 
     private void togglePause() {
         mPaused = !mPaused;
+        mClickPanelController.toggleAutoclickPause(mPaused);
 
         ImageButton imageButton = (ImageButton) mPauseButton.getChildAt(/* index= */ 0);
         if (mPaused) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index 7f60caa..0745c68 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.platform.test.annotations.DisableFlags;
@@ -400,6 +401,46 @@
                 .isNotEqualTo(initialScheduledTime);
     }
 
+    @Test
+    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+    public void pauseButton_flagOn_clickNotTriggeredWhenPaused() {
+        injectFakeMouseActionHoverMoveEvent();
+
+        // Pause autoclick.
+        AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+        when(mockAutoclickTypePanel.isPaused()).thenReturn(true);
+        mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+        // Send hover move event.
+        MotionEvent hoverMove = MotionEvent.obtain(
+                /* downTime= */ 0,
+                /* eventTime= */ 100,
+                /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+                /* x= */ 30f,
+                /* y= */ 0f,
+                /* metaState= */ 0);
+        hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+        mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+
+        // Verify there is not a pending click.
+        assertThat(mController.mClickScheduler.getIsActiveForTesting()).isFalse();
+        assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isEqualTo(-1);
+
+        // Resume autoclick.
+        when(mockAutoclickTypePanel.isPaused()).thenReturn(false);
+
+        // Send initial move event again. Because this is the first recorded move, a click won't be
+        // scheduled.
+        injectFakeMouseActionHoverMoveEvent();
+        assertThat(mController.mClickScheduler.getIsActiveForTesting()).isFalse();
+        assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isEqualTo(-1);
+
+        // Send move again to trigger click and verify there is now a pending click.
+        mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+        assertThat(mController.mClickScheduler.getIsActiveForTesting()).isTrue();
+        assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isNotEqualTo(-1);
+    }
+
     private void injectFakeMouseActionHoverMoveEvent() {
         MotionEvent event = getFakeMotionHoverMoveEvent();
         event.setSource(InputDevice.SOURCE_MOUSE);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
index f3016f4..ba672dc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
@@ -68,6 +68,7 @@
     private LinearLayout mPositionButton;
 
     private @AutoclickType int mActiveClickType = AUTOCLICK_TYPE_LEFT_CLICK;
+    private boolean mPaused;
 
     private final ClickPanelControllerInterface clickPanelController =
             new ClickPanelControllerInterface() {
@@ -77,7 +78,9 @@
                 }
 
                 @Override
-                public void toggleAutoclickPause() {}
+                public void toggleAutoclickPause(boolean paused) {
+                    mPaused = paused;
+                }
             };
 
     @Before
@@ -199,6 +202,17 @@
         }
     }
 
+    @Test
+    public void pauseButton_onClick() {
+        mPauseButton.callOnClick();
+        assertThat(mPaused).isTrue();
+        assertThat(mAutoclickTypePanel.isPaused()).isTrue();
+
+        mPauseButton.callOnClick();
+        assertThat(mPaused).isFalse();
+        assertThat(mAutoclickTypePanel.isPaused()).isFalse();
+    }
+
     private void verifyButtonHasSelectedStyle(@NonNull LinearLayout button) {
         GradientDrawable gradientDrawable = (GradientDrawable) button.getBackground();
         assertThat(gradientDrawable.getColor().getDefaultColor())