Loading services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java +65 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); } /** Loading Loading @@ -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; } } // 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; Loading services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java +1 −1 Original line number Diff line number Diff line Loading @@ -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; Loading services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java +121 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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); Loading Loading
services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java +65 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); } /** Loading Loading @@ -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; } } // 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; Loading
services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java +1 −1 Original line number Diff line number Diff line Loading @@ -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; Loading
services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java +121 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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); Loading