summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java68
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java121
3 files changed, 187 insertions, 4 deletions
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 60343e9e81e5..99febd6de60f 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -81,10 +81,16 @@ import com.android.server.accessibility.Flags;
public class AutoclickController extends BaseEventStreamTransformation {
private static final String LOG_TAG = AutoclickController.class.getSimpleName();
+ // TODO(b/393559560): Finalize scroll amount.
+ private static final float SCROLL_AMOUNT = 1.0f;
private final AccessibilityTraceManager mTrace;
private final Context mContext;
private final int mUserId;
+ @VisibleForTesting
+ float mLastCursorX;
+ @VisibleForTesting
+ float mLastCursorY;
// Lazily created on the first mouse motion event.
@VisibleForTesting ClickScheduler mClickScheduler;
@@ -315,8 +321,58 @@ public class AutoclickController extends BaseEventStreamTransformation {
/**
* Handles scroll operations in the specified direction.
*/
- public void handleScroll(@AutoclickScrollPanel.ScrollDirection int direction) {
- // TODO(b/388845721): Perform actual scroll.
+ private void handleScroll(@AutoclickScrollPanel.ScrollDirection int direction) {
+ final long now = SystemClock.uptimeMillis();
+
+ // Create pointer properties.
+ PointerProperties[] pointerProps = new PointerProperties[1];
+ pointerProps[0] = new PointerProperties();
+ pointerProps[0].id = 0;
+ pointerProps[0].toolType = MotionEvent.TOOL_TYPE_MOUSE;
+
+ // Create pointer coordinates at the last cursor position.
+ PointerCoords[] pointerCoords = new PointerCoords[1];
+ pointerCoords[0] = new PointerCoords();
+ pointerCoords[0].x = mLastCursorX;
+ pointerCoords[0].y = mLastCursorY;
+
+ // Set scroll values based on direction.
+ switch (direction) {
+ case AutoclickScrollPanel.DIRECTION_UP:
+ pointerCoords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, SCROLL_AMOUNT);
+ break;
+ case AutoclickScrollPanel.DIRECTION_DOWN:
+ pointerCoords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, -SCROLL_AMOUNT);
+ break;
+ case AutoclickScrollPanel.DIRECTION_LEFT:
+ pointerCoords[0].setAxisValue(MotionEvent.AXIS_HSCROLL, SCROLL_AMOUNT);
+ break;
+ case AutoclickScrollPanel.DIRECTION_RIGHT:
+ pointerCoords[0].setAxisValue(MotionEvent.AXIS_HSCROLL, -SCROLL_AMOUNT);
+ break;
+ case AutoclickScrollPanel.DIRECTION_EXIT:
+ case AutoclickScrollPanel.DIRECTION_NONE:
+ default:
+ return;
+ }
+
+ // Get device ID from last motion event if possible.
+ int deviceId = mClickScheduler != null && mClickScheduler.mLastMotionEvent != null
+ ? mClickScheduler.mLastMotionEvent.getDeviceId() : 0;
+
+ // Create a scroll event.
+ MotionEvent scrollEvent = MotionEvent.obtain(
+ /* downTime= */ now, /* eventTime= */ now,
+ MotionEvent.ACTION_SCROLL, /* pointerCount= */ 1, pointerProps,
+ pointerCoords, /* metaState= */ 0, /* actionButton= */ 0, /* xPrecision= */
+ 1.0f, /* yPrecision= */ 1.0f, deviceId, /* edgeFlags= */ 0,
+ InputDevice.SOURCE_MOUSE, /* flags= */ 0);
+
+ // Send the scroll event.
+ super.onMotionEvent(scrollEvent, scrollEvent, mClickScheduler.mEventPolicyFlags);
+
+ // Clean up.
+ scrollEvent.recycle();
}
/**
@@ -823,13 +879,19 @@ public class AutoclickController extends BaseEventStreamTransformation {
// If exit button is hovered, exit scroll mode after countdown and return early.
if (mHoveredDirection == AutoclickScrollPanel.DIRECTION_EXIT) {
exitScrollMode();
+ return;
}
- return;
}
// Handle scroll type specially, show scroll panel instead of sending click events.
if (mActiveClickType == AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL) {
if (mAutoclickScrollPanel != null) {
+ // Save the last cursor position at the moment when sendClick() is called.
+ if (mClickScheduler != null && mClickScheduler.mLastMotionEvent != null) {
+ final int pointerIndex = mClickScheduler.mLastMotionEvent.getActionIndex();
+ mLastCursorX = mClickScheduler.mLastMotionEvent.getX(pointerIndex);
+ mLastCursorY = mClickScheduler.mLastMotionEvent.getY(pointerIndex);
+ }
mAutoclickScrollPanel.show();
}
return;
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java
index c71443149687..025423078da1 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java
@@ -179,7 +179,7 @@ public class AutoclickScrollPanel {
private WindowManager.LayoutParams getLayoutParams() {
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
- layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
layoutParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
layoutParams.setFitInsetsTypes(WindowInsets.Type.statusBars());
layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
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 df77866b5e7f..0f418ab5d19c 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
@@ -88,6 +88,23 @@ public class AutoclickControllerTest {
}
}
+ public static class ScrollEventCaptor extends BaseEventStreamTransformation {
+ public MotionEvent scrollEvent;
+ public int eventCount = 0;
+
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (event.getAction() == MotionEvent.ACTION_SCROLL) {
+ if (scrollEvent != null) {
+ scrollEvent.recycle();
+ }
+ scrollEvent = MotionEvent.obtain(event);
+ eventCount++;
+ }
+ super.onMotionEvent(event, rawEvent, policyFlags);
+ }
+ }
+
@Before
public void setUp() {
mTestableLooper = TestableLooper.get(this);
@@ -918,6 +935,110 @@ public class AutoclickControllerTest {
MotionEvent.BUTTON_PRIMARY);
}
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void sendClick_updateLastCursorAndScrollAtThatLocation() {
+ // Set up event capturer to track scroll events.
+ ScrollEventCaptor scrollCaptor = new ScrollEventCaptor();
+ mController.setNext(scrollCaptor);
+
+ // Initialize controller with mouse event.
+ injectFakeMouseActionHoverMoveEvent();
+
+ // Mock the scroll panel.
+ AutoclickScrollPanel mockScrollPanel = mock(AutoclickScrollPanel.class);
+ mController.mAutoclickScrollPanel = mockScrollPanel;
+
+ // Set click type to scroll.
+ mController.clickPanelController.handleAutoclickTypeChange(
+ AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL);
+
+ // Set cursor position.
+ float expectedX = 75f;
+ float expectedY = 125f;
+ mController.mLastCursorX = expectedX;
+ mController.mLastCursorY = expectedY;
+
+ // Trigger scroll action in up direction.
+ mController.mScrollPanelController.onHoverButtonChange(
+ AutoclickScrollPanel.DIRECTION_UP, true);
+
+ // Verify scroll event happens at last cursor location.
+ assertThat(scrollCaptor.scrollEvent).isNotNull();
+ assertThat(scrollCaptor.scrollEvent.getX()).isEqualTo(expectedX);
+ assertThat(scrollCaptor.scrollEvent.getY()).isEqualTo(expectedY);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void handleScroll_generatesCorrectScrollEvents() {
+ ScrollEventCaptor scrollCaptor = new ScrollEventCaptor();
+ mController.setNext(scrollCaptor);
+
+ // Initialize controller.
+ injectFakeMouseActionHoverMoveEvent();
+
+ // Set cursor position.
+ final float expectedX = 100f;
+ final float expectedY = 200f;
+ mController.mLastCursorX = expectedX;
+ mController.mLastCursorY = expectedY;
+
+ // Test UP direction.
+ mController.mScrollPanelController.onHoverButtonChange(
+ AutoclickScrollPanel.DIRECTION_UP, true);
+
+ // Verify basic event properties.
+ assertThat(scrollCaptor.eventCount).isEqualTo(1);
+ assertThat(scrollCaptor.scrollEvent).isNotNull();
+ assertThat(scrollCaptor.scrollEvent.getAction()).isEqualTo(MotionEvent.ACTION_SCROLL);
+ assertThat(scrollCaptor.scrollEvent.getX()).isEqualTo(expectedX);
+ assertThat(scrollCaptor.scrollEvent.getY()).isEqualTo(expectedY);
+
+ // Verify UP direction uses correct axis values.
+ float vScrollUp = scrollCaptor.scrollEvent.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ float hScrollUp = scrollCaptor.scrollEvent.getAxisValue(MotionEvent.AXIS_HSCROLL);
+ assertThat(vScrollUp).isGreaterThan(0);
+ assertThat(hScrollUp).isEqualTo(0);
+
+ // Test DOWN direction.
+ mController.mScrollPanelController.onHoverButtonChange(
+ AutoclickScrollPanel.DIRECTION_DOWN, true);
+
+ // Verify DOWN direction uses correct axis values.
+ assertThat(scrollCaptor.eventCount).isEqualTo(2);
+ float vScrollDown = scrollCaptor.scrollEvent.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ float hScrollDown = scrollCaptor.scrollEvent.getAxisValue(MotionEvent.AXIS_HSCROLL);
+ assertThat(vScrollDown).isLessThan(0);
+ assertThat(hScrollDown).isEqualTo(0);
+
+ // Test LEFT direction.
+ mController.mScrollPanelController.onHoverButtonChange(
+ AutoclickScrollPanel.DIRECTION_LEFT, true);
+
+ // Verify LEFT direction uses correct axis values.
+ assertThat(scrollCaptor.eventCount).isEqualTo(3);
+ float vScrollLeft = scrollCaptor.scrollEvent.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ float hScrollLeft = scrollCaptor.scrollEvent.getAxisValue(MotionEvent.AXIS_HSCROLL);
+ assertThat(hScrollLeft).isGreaterThan(0);
+ assertThat(vScrollLeft).isEqualTo(0);
+
+ // Test RIGHT direction.
+ mController.mScrollPanelController.onHoverButtonChange(
+ AutoclickScrollPanel.DIRECTION_RIGHT, true);
+
+ // Verify RIGHT direction uses correct axis values.
+ assertThat(scrollCaptor.eventCount).isEqualTo(4);
+ float vScrollRight = scrollCaptor.scrollEvent.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ float hScrollRight = scrollCaptor.scrollEvent.getAxisValue(MotionEvent.AXIS_HSCROLL);
+ assertThat(hScrollRight).isLessThan(0);
+ assertThat(vScrollRight).isEqualTo(0);
+
+ // Verify scroll cursor position is preserved.
+ assertThat(scrollCaptor.scrollEvent.getX()).isEqualTo(expectedX);
+ assertThat(scrollCaptor.scrollEvent.getY()).isEqualTo(expectedY);
+ }
+
private void injectFakeMouseActionHoverMoveEvent() {
MotionEvent event = getFakeMotionHoverMoveEvent();
event.setSource(InputDevice.SOURCE_MOUSE);