Loading services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java +20 −0 Original line number Diff line number Diff line Loading @@ -277,6 +277,11 @@ public class AutoclickController extends BaseEventStreamTransformation { mAutoclickIndicatorScheduler); } if (mAutoclickTypePanel != null && mAutoclickTypePanel.getIsDragging() && event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) { mAutoclickTypePanel.onDragMove(event); } if (!isPaused()) { scheduleClick(event, policyFlags); Loading Loading @@ -449,6 +454,9 @@ public class AutoclickController extends BaseEventStreamTransformation { if (mAutoclickIndicatorScheduler != null) { mAutoclickIndicatorScheduler.cancel(); } if (mAutoclickTypePanel != null && mAutoclickTypePanel.getIsDragging()) { mAutoclickTypePanel.onDragEnd(); } } /** Loading Loading @@ -1164,6 +1172,12 @@ public class AutoclickController extends BaseEventStreamTransformation { clearLongPressState(); } if (mAutoclickTypePanel.isHoveringDraggableArea() && !mAutoclickTypePanel.getIsDragging()) { mAutoclickTypePanel.onDragStart(mLastMotionEvent); return; } // 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 Loading Loading @@ -1216,6 +1230,12 @@ public class AutoclickController extends BaseEventStreamTransformation { break; } sendMotionEventsForClick(actionButton); // End panel drag operation if one is active (autoclick triggered after user stopped // moving during drag). if (mAutoclickTypePanel != null && mAutoclickTypePanel.getIsDragging()) { mAutoclickTypePanel.onDragEnd(); } } /** Loading services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java +65 −29 Original line number Diff line number Diff line Loading @@ -214,16 +214,6 @@ public class AutoclickTypePanel { // Set up touch event handling for the panel to allow the user to drag and reposition the // panel by touching and moving it. mContentView.setOnTouchListener(this::onPanelTouch); // Set hover behavior for the panel, show grab when hovering. mContentView.setOnHoverListener((v, event) -> { mCurrentCursor = PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_GRAB); v.setPointerIcon(mCurrentCursor); return false; }); // Show default cursor when hovering over buttons. setDefaultCursorForButtons(); } /** Loading Loading @@ -263,7 +253,9 @@ public class AutoclickTypePanel { int yPosition = params.y; // Determine which half of the screen the panel is on. boolean isOnLeftHalf = params.x < screenWidth / 2; @Corner int visualCorner = getVisualCorner(); boolean isOnLeftHalf = (visualCorner == CORNER_TOP_LEFT || visualCorner == CORNER_BOTTOM_LEFT); if (isOnLeftHalf) { // Snap to left edge. Set params.gravity to make sure x, y offsets from correct anchor. Loading Loading @@ -313,6 +305,10 @@ public class AutoclickTypePanel { mPauseButton.setOnClickListener(v -> togglePause()); setSelectedClickType(AUTOCLICK_TYPE_LEFT_CLICK); // Set up hover listeners on panel and buttons to dynamically change cursor icons. setupHoverListenersForCursor(); // Remove spacing between buttons when initialized. adjustPanelSpacing(/* isExpanded= */ true); } Loading Loading @@ -670,22 +666,6 @@ public class AutoclickTypePanel { } } private void setDefaultCursorForButtons() { View[] buttons = { mLeftClickButton, mRightClickButton, mDoubleClickButton, mScrollButton, mDragButton, mLongPressButton, mPauseButton, mPositionButton }; for (View button : buttons) { button.setOnHoverListener((v, event) -> { mCurrentCursor = PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW); v.setPointerIcon(mCurrentCursor); return false; }); } } /** * Starts drag operation, capturing initial positions and updating cursor icon. */ Loading Loading @@ -754,6 +734,63 @@ public class AutoclickTypePanel { } } /** * Returns true if cursor is over content view but not over any buttons. */ public boolean isHoveringDraggableArea() { if (!mContentView.isHovered()) { return false; } View[] buttons = {mLeftClickButton, mRightClickButton, mDoubleClickButton, mScrollButton, mDragButton, mLongPressButton, mPauseButton, mPositionButton}; for (View button : buttons) { if (button.isHovered()) { return false; } } return true; } /** * Sets up hover listeners to update cursor icons (grab for draggable areas, arrow for buttons). */ private void setupHoverListenersForCursor() { View[] mAllButtons = new View[]{ mLeftClickButton, mRightClickButton, mDoubleClickButton, mScrollButton, mDragButton, mLongPressButton, mPauseButton, mPositionButton }; // Set hover behavior for the panel. mContentView.setOnHoverListener((v, event) -> { updateCursorIcon(); return false; }); // Set hover behavior for all buttons. for (View button : mAllButtons) { button.setOnHoverListener((v, event) -> { updateCursorIcon(); return false; }); } } /** * Updates cursor based on hover state: grab for draggable areas, arrow for buttons. */ private void updateCursorIcon() { // Don't update cursor icon while dragging to avoid overriding the grabbing cursor during // drag. if (mIsDragging) { return; } int cursorType = isHoveringDraggableArea() ? PointerIcon.TYPE_GRAB : PointerIcon.TYPE_ARROW; mCurrentCursor = PointerIcon.getSystemIcon(mContext, cursorType); mContentView.setPointerIcon(mCurrentCursor); } @VisibleForTesting boolean getExpansionStateForTesting() { return mExpanded; Loading @@ -776,8 +813,7 @@ public class AutoclickTypePanel { return mParams; } @VisibleForTesting boolean getIsDraggingForTesting() { boolean getIsDragging() { return mIsDragging; } Loading services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java +58 −0 Original line number Diff line number Diff line Loading @@ -41,9 +41,11 @@ import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableContext; import android.testing.TestableLooper; import android.view.Gravity; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; Loading Loading @@ -1434,6 +1436,62 @@ public class AutoclickControllerTest { assertThat(scrollCaptor.eventCount).isEqualTo(countBeforeRunnable); } @Test @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) public void typePanelDrag_completeLifeCycle() { injectFakeMouseActionHoverMoveEvent(); // Store initial position for comparison. WindowManager.LayoutParams initialParams = mController.mAutoclickTypePanel.getLayoutParamsForTesting(); int initialX = initialParams.x; int initialY = initialParams.y; // Test onDragStart - should enable dragging and change cursor. MotionEvent dragStartEvent = MotionEvent.obtain( /* downTime= */ 0, /* eventTime= */ 0, MotionEvent.ACTION_DOWN, /* x= */ 100f, /* y= */ 100f, /* metaState= */ 0); mController.mAutoclickTypePanel.onDragStart(dragStartEvent); assertThat(mController.mAutoclickTypePanel.getIsDragging()).isTrue(); assertThat(mController.mAutoclickTypePanel.getCurrentCursorForTesting().getType()) .isEqualTo(PointerIcon.TYPE_GRABBING); // Test onDragMove - should update position and maintain drag state. MotionEvent dragMoveEvent = MotionEvent.obtain( /* downTime= */ 0, /* eventTime= */ 50, MotionEvent.ACTION_MOVE, /* x= */ 150f, /* y= */ 150f, /* metaState= */ 0); mController.mAutoclickTypePanel.onDragMove(dragMoveEvent); // Verify drag state maintained and gravity changed to absolute positioning assertThat(mController.mAutoclickTypePanel.getIsDragging()).isTrue(); assertThat(mController.mAutoclickTypePanel.getCurrentCursorForTesting().getType()) .isEqualTo(PointerIcon.TYPE_GRABBING); assertThat(mController.mAutoclickTypePanel.getLayoutParamsForTesting().gravity) .isEqualTo(Gravity.LEFT | Gravity.TOP); // Verify position coordinates actually changed from drag movement. WindowManager.LayoutParams dragParams = mController.mAutoclickTypePanel.getLayoutParamsForTesting(); assertThat(dragParams.x).isNotEqualTo(initialX); assertThat(dragParams.y).isNotEqualTo(initialY); // Test onDragEnd - should reset state, change cursor, and snap to edge. mController.mAutoclickTypePanel.onDragEnd(); assertThat(mController.mAutoclickTypePanel.getIsDragging()).isFalse(); assertThat(mController.mAutoclickTypePanel.getCurrentCursorForTesting().getType()) .isEqualTo(PointerIcon.TYPE_GRAB); // Verify panel snapped to edge. WindowManager.LayoutParams finalParams = mController.mAutoclickTypePanel.getLayoutParamsForTesting(); boolean snappedToLeftEdge = (finalParams.gravity & Gravity.START) == Gravity.START; boolean snappedToRightEdge = (finalParams.gravity & Gravity.END) == Gravity.END; assertThat(snappedToLeftEdge || snappedToRightEdge).isTrue(); dragStartEvent.recycle(); dragMoveEvent.recycle(); } @Test @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) public void exitButton_exitsScrollMode() { Loading services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java +4 −1 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ import androidx.annotation.NonNull; import com.android.internal.R; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; Loading Loading @@ -345,7 +346,7 @@ public class AutoclickTypePanelTest { contentView.dispatchTouchEvent(moveEvent); // Verify position update. assertThat(mAutoclickTypePanel.getIsDraggingForTesting()).isTrue(); assertThat(mAutoclickTypePanel.getIsDragging()).isTrue(); assertThat(params.gravity).isEqualTo(Gravity.LEFT | Gravity.TOP); assertThat(params.x).isEqualTo(panelLocation[0] + delta); assertThat(params.y).isEqualTo( Loading @@ -354,6 +355,7 @@ public class AutoclickTypePanelTest { } @Test @Ignore ("b/424594372") public void dragAndEndAtRight_snapsToRightSide() { View contentView = mAutoclickTypePanel.getContentViewForTesting(); WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting(); Loading @@ -377,6 +379,7 @@ public class AutoclickTypePanelTest { } @Test @Ignore ("b/424594372") public void dragAndEndAtLeft_snapsToLeftSide() { View contentView = mAutoclickTypePanel.getContentViewForTesting(); WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting(); Loading Loading
services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java +20 −0 Original line number Diff line number Diff line Loading @@ -277,6 +277,11 @@ public class AutoclickController extends BaseEventStreamTransformation { mAutoclickIndicatorScheduler); } if (mAutoclickTypePanel != null && mAutoclickTypePanel.getIsDragging() && event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) { mAutoclickTypePanel.onDragMove(event); } if (!isPaused()) { scheduleClick(event, policyFlags); Loading Loading @@ -449,6 +454,9 @@ public class AutoclickController extends BaseEventStreamTransformation { if (mAutoclickIndicatorScheduler != null) { mAutoclickIndicatorScheduler.cancel(); } if (mAutoclickTypePanel != null && mAutoclickTypePanel.getIsDragging()) { mAutoclickTypePanel.onDragEnd(); } } /** Loading Loading @@ -1164,6 +1172,12 @@ public class AutoclickController extends BaseEventStreamTransformation { clearLongPressState(); } if (mAutoclickTypePanel.isHoveringDraggableArea() && !mAutoclickTypePanel.getIsDragging()) { mAutoclickTypePanel.onDragStart(mLastMotionEvent); return; } // 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 Loading Loading @@ -1216,6 +1230,12 @@ public class AutoclickController extends BaseEventStreamTransformation { break; } sendMotionEventsForClick(actionButton); // End panel drag operation if one is active (autoclick triggered after user stopped // moving during drag). if (mAutoclickTypePanel != null && mAutoclickTypePanel.getIsDragging()) { mAutoclickTypePanel.onDragEnd(); } } /** Loading
services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java +65 −29 Original line number Diff line number Diff line Loading @@ -214,16 +214,6 @@ public class AutoclickTypePanel { // Set up touch event handling for the panel to allow the user to drag and reposition the // panel by touching and moving it. mContentView.setOnTouchListener(this::onPanelTouch); // Set hover behavior for the panel, show grab when hovering. mContentView.setOnHoverListener((v, event) -> { mCurrentCursor = PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_GRAB); v.setPointerIcon(mCurrentCursor); return false; }); // Show default cursor when hovering over buttons. setDefaultCursorForButtons(); } /** Loading Loading @@ -263,7 +253,9 @@ public class AutoclickTypePanel { int yPosition = params.y; // Determine which half of the screen the panel is on. boolean isOnLeftHalf = params.x < screenWidth / 2; @Corner int visualCorner = getVisualCorner(); boolean isOnLeftHalf = (visualCorner == CORNER_TOP_LEFT || visualCorner == CORNER_BOTTOM_LEFT); if (isOnLeftHalf) { // Snap to left edge. Set params.gravity to make sure x, y offsets from correct anchor. Loading Loading @@ -313,6 +305,10 @@ public class AutoclickTypePanel { mPauseButton.setOnClickListener(v -> togglePause()); setSelectedClickType(AUTOCLICK_TYPE_LEFT_CLICK); // Set up hover listeners on panel and buttons to dynamically change cursor icons. setupHoverListenersForCursor(); // Remove spacing between buttons when initialized. adjustPanelSpacing(/* isExpanded= */ true); } Loading Loading @@ -670,22 +666,6 @@ public class AutoclickTypePanel { } } private void setDefaultCursorForButtons() { View[] buttons = { mLeftClickButton, mRightClickButton, mDoubleClickButton, mScrollButton, mDragButton, mLongPressButton, mPauseButton, mPositionButton }; for (View button : buttons) { button.setOnHoverListener((v, event) -> { mCurrentCursor = PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW); v.setPointerIcon(mCurrentCursor); return false; }); } } /** * Starts drag operation, capturing initial positions and updating cursor icon. */ Loading Loading @@ -754,6 +734,63 @@ public class AutoclickTypePanel { } } /** * Returns true if cursor is over content view but not over any buttons. */ public boolean isHoveringDraggableArea() { if (!mContentView.isHovered()) { return false; } View[] buttons = {mLeftClickButton, mRightClickButton, mDoubleClickButton, mScrollButton, mDragButton, mLongPressButton, mPauseButton, mPositionButton}; for (View button : buttons) { if (button.isHovered()) { return false; } } return true; } /** * Sets up hover listeners to update cursor icons (grab for draggable areas, arrow for buttons). */ private void setupHoverListenersForCursor() { View[] mAllButtons = new View[]{ mLeftClickButton, mRightClickButton, mDoubleClickButton, mScrollButton, mDragButton, mLongPressButton, mPauseButton, mPositionButton }; // Set hover behavior for the panel. mContentView.setOnHoverListener((v, event) -> { updateCursorIcon(); return false; }); // Set hover behavior for all buttons. for (View button : mAllButtons) { button.setOnHoverListener((v, event) -> { updateCursorIcon(); return false; }); } } /** * Updates cursor based on hover state: grab for draggable areas, arrow for buttons. */ private void updateCursorIcon() { // Don't update cursor icon while dragging to avoid overriding the grabbing cursor during // drag. if (mIsDragging) { return; } int cursorType = isHoveringDraggableArea() ? PointerIcon.TYPE_GRAB : PointerIcon.TYPE_ARROW; mCurrentCursor = PointerIcon.getSystemIcon(mContext, cursorType); mContentView.setPointerIcon(mCurrentCursor); } @VisibleForTesting boolean getExpansionStateForTesting() { return mExpanded; Loading @@ -776,8 +813,7 @@ public class AutoclickTypePanel { return mParams; } @VisibleForTesting boolean getIsDraggingForTesting() { boolean getIsDragging() { return mIsDragging; } Loading
services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java +58 −0 Original line number Diff line number Diff line Loading @@ -41,9 +41,11 @@ import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableContext; import android.testing.TestableLooper; import android.view.Gravity; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; Loading Loading @@ -1434,6 +1436,62 @@ public class AutoclickControllerTest { assertThat(scrollCaptor.eventCount).isEqualTo(countBeforeRunnable); } @Test @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) public void typePanelDrag_completeLifeCycle() { injectFakeMouseActionHoverMoveEvent(); // Store initial position for comparison. WindowManager.LayoutParams initialParams = mController.mAutoclickTypePanel.getLayoutParamsForTesting(); int initialX = initialParams.x; int initialY = initialParams.y; // Test onDragStart - should enable dragging and change cursor. MotionEvent dragStartEvent = MotionEvent.obtain( /* downTime= */ 0, /* eventTime= */ 0, MotionEvent.ACTION_DOWN, /* x= */ 100f, /* y= */ 100f, /* metaState= */ 0); mController.mAutoclickTypePanel.onDragStart(dragStartEvent); assertThat(mController.mAutoclickTypePanel.getIsDragging()).isTrue(); assertThat(mController.mAutoclickTypePanel.getCurrentCursorForTesting().getType()) .isEqualTo(PointerIcon.TYPE_GRABBING); // Test onDragMove - should update position and maintain drag state. MotionEvent dragMoveEvent = MotionEvent.obtain( /* downTime= */ 0, /* eventTime= */ 50, MotionEvent.ACTION_MOVE, /* x= */ 150f, /* y= */ 150f, /* metaState= */ 0); mController.mAutoclickTypePanel.onDragMove(dragMoveEvent); // Verify drag state maintained and gravity changed to absolute positioning assertThat(mController.mAutoclickTypePanel.getIsDragging()).isTrue(); assertThat(mController.mAutoclickTypePanel.getCurrentCursorForTesting().getType()) .isEqualTo(PointerIcon.TYPE_GRABBING); assertThat(mController.mAutoclickTypePanel.getLayoutParamsForTesting().gravity) .isEqualTo(Gravity.LEFT | Gravity.TOP); // Verify position coordinates actually changed from drag movement. WindowManager.LayoutParams dragParams = mController.mAutoclickTypePanel.getLayoutParamsForTesting(); assertThat(dragParams.x).isNotEqualTo(initialX); assertThat(dragParams.y).isNotEqualTo(initialY); // Test onDragEnd - should reset state, change cursor, and snap to edge. mController.mAutoclickTypePanel.onDragEnd(); assertThat(mController.mAutoclickTypePanel.getIsDragging()).isFalse(); assertThat(mController.mAutoclickTypePanel.getCurrentCursorForTesting().getType()) .isEqualTo(PointerIcon.TYPE_GRAB); // Verify panel snapped to edge. WindowManager.LayoutParams finalParams = mController.mAutoclickTypePanel.getLayoutParamsForTesting(); boolean snappedToLeftEdge = (finalParams.gravity & Gravity.START) == Gravity.START; boolean snappedToRightEdge = (finalParams.gravity & Gravity.END) == Gravity.END; assertThat(snappedToLeftEdge || snappedToRightEdge).isTrue(); dragStartEvent.recycle(); dragMoveEvent.recycle(); } @Test @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) public void exitButton_exitsScrollMode() { Loading
services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java +4 −1 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ import androidx.annotation.NonNull; import com.android.internal.R; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; Loading Loading @@ -345,7 +346,7 @@ public class AutoclickTypePanelTest { contentView.dispatchTouchEvent(moveEvent); // Verify position update. assertThat(mAutoclickTypePanel.getIsDraggingForTesting()).isTrue(); assertThat(mAutoclickTypePanel.getIsDragging()).isTrue(); assertThat(params.gravity).isEqualTo(Gravity.LEFT | Gravity.TOP); assertThat(params.x).isEqualTo(panelLocation[0] + delta); assertThat(params.y).isEqualTo( Loading @@ -354,6 +355,7 @@ public class AutoclickTypePanelTest { } @Test @Ignore ("b/424594372") public void dragAndEndAtRight_snapsToRightSide() { View contentView = mAutoclickTypePanel.getContentViewForTesting(); WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting(); Loading @@ -377,6 +379,7 @@ public class AutoclickTypePanelTest { } @Test @Ignore ("b/424594372") public void dragAndEndAtLeft_snapsToLeftSide() { View contentView = mAutoclickTypePanel.getContentViewForTesting(); WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting(); Loading