summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Wenyu Zhang <zhangwenyu@google.com> 2025-01-16 10:22:57 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-01-16 10:22:57 -0800
commit46225032e5126bfef6df36a10392cc15c60b7a51 (patch)
tree1623aa7e0060ebc634f2b561bd4eda841d1a642f
parent554eab73848a40d98fccdc152b7acec961268380 (diff)
parentfbea199bf45f90151b2d424190084f2a7a6792a6 (diff)
Merge "a11y: Add tests for AutoclickController" into main
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickController.java18
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java242
3 files changed, 263 insertions, 6 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index b94fa2f59162..8b758d29a2ac 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -39,6 +39,8 @@ import android.view.MotionEvent.PointerProperties;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import androidx.annotation.VisibleForTesting;
+
/**
* Implements "Automatically click on mouse stop" feature.
*
@@ -69,10 +71,10 @@ public class AutoclickController extends BaseEventStreamTransformation {
private final int mUserId;
// Lazily created on the first mouse motion event.
- private ClickScheduler mClickScheduler;
- private AutoclickSettingsObserver mAutoclickSettingsObserver;
- private AutoclickIndicatorScheduler mAutoclickIndicatorScheduler;
- private AutoclickIndicatorView mAutoclickIndicatorView;
+ @VisibleForTesting ClickScheduler mClickScheduler;
+ @VisibleForTesting AutoclickSettingsObserver mAutoclickSettingsObserver;
+ @VisibleForTesting AutoclickIndicatorScheduler mAutoclickIndicatorScheduler;
+ @VisibleForTesting AutoclickIndicatorView mAutoclickIndicatorView;
private WindowManager mWindowManager;
public AutoclickController(Context context, int userId, AccessibilityTraceManager trace) {
@@ -360,7 +362,8 @@ public class AutoclickController extends BaseEventStreamTransformation {
* moving. The click is first scheduled when a mouse movement is detected, and then further
* delayed on every sufficient mouse movement.
*/
- final private class ClickScheduler implements Runnable {
+ @VisibleForTesting
+ final class ClickScheduler implements Runnable {
/**
* Minimal distance pointer has to move relative to anchor in order for movement not to be
* discarded as noise. Anchor is the position of the last MOVE event that was not considered
@@ -474,6 +477,11 @@ public class AutoclickController extends BaseEventStreamTransformation {
}
}
+ @VisibleForTesting
+ int getDelayForTesting() {
+ return mDelay;
+ }
+
/**
* Updates the time at which click sequence should occur.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
index bf5015176f8c..f87dcdb200bb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
@@ -28,6 +28,8 @@ import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.LinearInterpolator;
+import androidx.annotation.VisibleForTesting;
+
// A visual indicator for the autoclick feature.
public class AutoclickIndicatorView extends View {
private static final String TAG = AutoclickIndicatorView.class.getSimpleName();
@@ -37,7 +39,7 @@ public class AutoclickIndicatorView extends View {
static final int MINIMAL_ANIMATION_DURATION = 50;
- private float mRadius = AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
+ private int mRadius = AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
private final Paint mPaint;
@@ -112,6 +114,11 @@ public class AutoclickIndicatorView extends View {
mRadius = radius;
}
+ @VisibleForTesting
+ int getRadiusForTesting() {
+ return mRadius;
+ }
+
public void redrawIndicator() {
showIndicator = true;
invalidate();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java
new file mode 100644
index 000000000000..acce813ff659
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.testutils.MockitoUtilsKt.eq;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertNull;
+
+import android.content.Context;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/** Test cases for {@link AutoclickController}. */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class AutoclickControllerTest {
+
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Rule
+ public TestableContext mTestableContext =
+ new TestableContext(getInstrumentation().getContext());
+
+ private TestableLooper mTestableLooper;
+ @Mock private AccessibilityTraceManager mMockTrace;
+ @Mock private WindowManager mMockWindowManager;
+ private AutoclickController mController;
+
+ @Before
+ public void setUp() {
+ mTestableLooper = TestableLooper.get(this);
+ mTestableContext.addMockSystemService(Context.WINDOW_SERVICE, mMockWindowManager);
+ mController =
+ new AutoclickController(mTestableContext, mTestableContext.getUserId(), mMockTrace);
+ }
+
+ @After
+ public void tearDown() {
+ mTestableLooper.processAllMessages();
+ }
+
+ @Test
+ public void onMotionEvent_lazyInitClickScheduler() {
+ assertNull(mController.mClickScheduler);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNotNull(mController.mClickScheduler);
+ }
+
+ @Test
+ public void onMotionEvent_nonMouseSource_notInitClickScheduler() {
+ assertNull(mController.mClickScheduler);
+
+ injectFakeNonMouseActionDownEvent();
+
+ assertNull(mController.mClickScheduler);
+ }
+
+ @Test
+ public void onMotionEvent_lazyInitAutoclickSettingsObserver() {
+ assertNull(mController.mAutoclickSettingsObserver);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNotNull(mController.mAutoclickSettingsObserver);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOn_lazyInitAutoclickIndicatorScheduler() {
+ assertNull(mController.mAutoclickIndicatorScheduler);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNotNull(mController.mAutoclickIndicatorScheduler);
+ }
+
+ @Test
+ @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOff_notInitAutoclickIndicatorScheduler() {
+ assertNull(mController.mAutoclickIndicatorScheduler);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNull(mController.mAutoclickIndicatorScheduler);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOn_lazyInitAutoclickIndicatorView() {
+ assertNull(mController.mAutoclickIndicatorView);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNotNull(mController.mAutoclickIndicatorView);
+ }
+
+ @Test
+ @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOff_notInitAutoclickIndicatorView() {
+ assertNull(mController.mAutoclickIndicatorView);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNull(mController.mAutoclickIndicatorView);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOn_addAutoclickIndicatorViewToWindowManager() {
+ injectFakeMouseActionDownEvent();
+
+ verify(mMockWindowManager).addView(eq(mController.mAutoclickIndicatorView), any());
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onDestroy_flagOn_removeAutoclickIndicatorViewToWindowManager() {
+ injectFakeMouseActionDownEvent();
+
+ mController.onDestroy();
+
+ verify(mMockWindowManager).removeView(mController.mAutoclickIndicatorView);
+ }
+
+ @Test
+ public void onMotionEvent_initClickSchedulerDelayFromSetting() {
+ injectFakeMouseActionDownEvent();
+
+ int delay =
+ Settings.Secure.getIntForUser(
+ mTestableContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
+ AccessibilityManager.AUTOCLICK_DELAY_DEFAULT,
+ mTestableContext.getUserId());
+ assertEquals(delay, mController.mClickScheduler.getDelayForTesting());
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOn_initCursorAreaSizeFromSetting() {
+ injectFakeMouseActionDownEvent();
+
+ int size =
+ Settings.Secure.getIntForUser(
+ mTestableContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
+ AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT,
+ mTestableContext.getUserId());
+ assertEquals(size, mController.mAutoclickIndicatorView.getRadiusForTesting());
+ }
+
+ @Test
+ public void onDestroy_clearClickScheduler() {
+ injectFakeMouseActionDownEvent();
+
+ mController.onDestroy();
+
+ assertNull(mController.mClickScheduler);
+ }
+
+ @Test
+ public void onDestroy_clearAutoclickSettingsObserver() {
+ injectFakeMouseActionDownEvent();
+
+ mController.onDestroy();
+
+ assertNull(mController.mAutoclickSettingsObserver);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onDestroy_flagOn_clearAutoclickIndicatorScheduler() {
+ injectFakeMouseActionDownEvent();
+
+ mController.onDestroy();
+
+ assertNull(mController.mAutoclickIndicatorScheduler);
+ }
+
+ private void injectFakeMouseActionDownEvent() {
+ MotionEvent event = getFakeMotionDownEvent();
+ event.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(event, event, /* policyFlags= */ 0);
+ }
+
+ private void injectFakeNonMouseActionDownEvent() {
+ MotionEvent event = getFakeMotionDownEvent();
+ event.setSource(InputDevice.SOURCE_KEYBOARD);
+ mController.onMotionEvent(event, event, /* policyFlags= */ 0);
+ }
+
+ private MotionEvent getFakeMotionDownEvent() {
+ return MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 0,
+ /* action= */ MotionEvent.ACTION_DOWN,
+ /* x= */ 0,
+ /* y= */ 0,
+ /* metaState= */ 0);
+ }
+}