diff options
author | 2025-03-06 21:26:26 +0000 | |
---|---|---|
committer | 2025-03-12 15:50:13 +0000 | |
commit | af17ef561655e2a48477cf65b859fe08b2108f2a (patch) | |
tree | 5f4bbf1d61c0aa2f15c2b5737803eb944f4e7335 /services/accessibility | |
parent | ecfab51084945ac0975fc6a85a85fcd432f88427 (diff) |
a11y: Add countdown when hovering on exit
Right now, the scroll panel hides as soon as the user hovers over the
exit button, which is not ideal because it can close by accident.
This CL fixes it by adding a delay before hiding the panel when the exit
button is hovered. The delay time is the same as the set countdown.
Video: http://shortn/_JxFXNsIXKe
Bug: b/401509893
Test: AutoclickScrollPaneTest
Flag: com.android.server.accessibility.enable_autoclick_indicator
Change-Id: I537131d57e97ef83305fdb492b93423dd36596ce
Diffstat (limited to 'services/accessibility')
2 files changed, 91 insertions, 34 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 84158cf911ad..0b9c45de6e40 100644 --- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java +++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java @@ -25,6 +25,7 @@ import static android.view.accessibility.AccessibilityManager.AUTOCLICK_REVERT_T import static com.android.server.accessibility.autoclick.AutoclickIndicatorView.SHOW_INDICATOR_DELAY_TIME; import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_DOUBLE_CLICK; +import static com.android.server.accessibility.autoclick.AutoclickScrollPanel.DIRECTION_NONE; import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_LEFT_CLICK; import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_RIGHT_CLICK; import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL; @@ -97,6 +98,9 @@ public class AutoclickController extends BaseEventStreamTransformation { // Default click type is left-click. private @AutoclickType int mActiveClickType = AUTOCLICK_TYPE_LEFT_CLICK; + // Default scroll direction is DIRECTION_NONE. + private @AutoclickScrollPanel.ScrollDirection int mHoveredDirection = DIRECTION_NONE; + @VisibleForTesting final ClickPanelControllerInterface clickPanelController = new ClickPanelControllerInterface() { @@ -131,14 +135,26 @@ public class AutoclickController extends BaseEventStreamTransformation { final AutoclickScrollPanel.ScrollPanelControllerInterface mScrollPanelController = new AutoclickScrollPanel.ScrollPanelControllerInterface() { @Override - public void handleScroll(@AutoclickScrollPanel.ScrollDirection int direction) { - // TODO(b/388845721): Perform actual scroll. - } + public void onHoverButtonChange( + @AutoclickScrollPanel.ScrollDirection int direction, + boolean hovered) { + // Update the hover direction. + if (hovered) { + mHoveredDirection = direction; + } else if (mHoveredDirection == direction) { + // Safety check: Only clear hover tracking if this is the same button + // we're currently tracking. + mHoveredDirection = AutoclickScrollPanel.DIRECTION_NONE; + } - @Override - public void exitScrollMode() { - if (mAutoclickScrollPanel != null) { - mAutoclickScrollPanel.hide(); + // For exit button, we only trigger hover state changes, the autoclick system + // will handle the countdown. + if (direction == AutoclickScrollPanel.DIRECTION_EXIT) { + return; + } + // For direction buttons, perform scroll action immediately. + if (hovered && direction != AutoclickScrollPanel.DIRECTION_NONE) { + handleScroll(direction); } } }; @@ -285,6 +301,22 @@ 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. + } + + /** + * Exits scroll mode and hides the scroll panel UI. + */ + public void exitScrollMode() { + if (mAutoclickScrollPanel != null) { + mAutoclickScrollPanel.hide(); + } + } + @VisibleForTesting void onChangeForTesting(boolean selfChange, Uri uri) { mAutoclickSettingsObserver.onChange(selfChange, uri); @@ -776,6 +808,14 @@ public class AutoclickController extends BaseEventStreamTransformation { return; } + if (mAutoclickScrollPanel != null && mAutoclickScrollPanel.isVisible()) { + // 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) { 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 e79be502a6fc..c71443149687 100644 --- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java +++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java @@ -23,6 +23,7 @@ import android.content.Context; import android.graphics.PixelFormat; import android.view.Gravity; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.WindowInsets; import android.view.WindowManager; @@ -41,12 +42,16 @@ public class AutoclickScrollPanel { public static final int DIRECTION_DOWN = 1; public static final int DIRECTION_LEFT = 2; public static final int DIRECTION_RIGHT = 3; + public static final int DIRECTION_EXIT = 4; + public static final int DIRECTION_NONE = 5; @IntDef({ DIRECTION_UP, DIRECTION_DOWN, DIRECTION_LEFT, - DIRECTION_RIGHT + DIRECTION_RIGHT, + DIRECTION_EXIT, + DIRECTION_NONE, }) @Retention(RetentionPolicy.SOURCE) public @interface ScrollDirection {} @@ -70,16 +75,12 @@ public class AutoclickScrollPanel { */ public interface ScrollPanelControllerInterface { /** - * Called when a scroll direction is hovered. + * Called when a button hover state changes. * - * @param direction The direction to scroll: one of {@link ScrollDirection} values. + * @param direction The direction associated with the button. + * @param hovered Whether the button is being hovered or not. */ - void handleScroll(@ScrollDirection int direction); - - /** - * Called when the exit button is hovered. - */ - void exitScrollMode(); + void onHoverButtonChange(@ScrollDirection int direction, boolean hovered); } public AutoclickScrollPanel(Context context, WindowManager windowManager, @@ -104,19 +105,12 @@ public class AutoclickScrollPanel { * Sets up hover listeners for scroll panel buttons. */ private void initializeButtonState() { - // Set up hover listeners for direction buttons. - setupHoverListenerForDirectionButton(mUpButton, DIRECTION_UP); - setupHoverListenerForDirectionButton(mLeftButton, DIRECTION_LEFT); - setupHoverListenerForDirectionButton(mRightButton, DIRECTION_RIGHT); - setupHoverListenerForDirectionButton(mDownButton, DIRECTION_DOWN); - - // Set up hover listener for exit button. - mExitButton.setOnHoverListener((v, event) -> { - if (mScrollPanelController != null) { - mScrollPanelController.exitScrollMode(); - } - return true; - }); + // Set up hover listeners for all buttons. + setupHoverListenerForButton(mUpButton, DIRECTION_UP); + setupHoverListenerForButton(mLeftButton, DIRECTION_LEFT); + setupHoverListenerForButton(mRightButton, DIRECTION_RIGHT); + setupHoverListenerForButton(mDownButton, DIRECTION_DOWN); + setupHoverListenerForButton(mExitButton, DIRECTION_EXIT); } /** @@ -142,14 +136,37 @@ public class AutoclickScrollPanel { } /** - * Sets up a hover listener for a direction button. + * Sets up a hover listener for a button. */ - private void setupHoverListenerForDirectionButton(ImageButton button, - @ScrollDirection int direction) { + private void setupHoverListenerForButton(ImageButton button, @ScrollDirection int direction) { button.setOnHoverListener((v, event) -> { - if (mScrollPanelController != null) { - mScrollPanelController.handleScroll(direction); + if (mScrollPanelController == null) { + return true; + } + + boolean hovered; + switch (event.getAction()) { + case MotionEvent.ACTION_HOVER_ENTER: + hovered = true; + break; + case MotionEvent.ACTION_HOVER_MOVE: + // For direction buttons, continuously trigger scroll on hover move. + if (direction != DIRECTION_EXIT) { + hovered = true; + } else { + // Ignore hover move events for exit button. + return true; + } + break; + case MotionEvent.ACTION_HOVER_EXIT: + hovered = false; + break; + default: + return true; } + + // Notify the controller about the hover change. + mScrollPanelController.onHoverButtonChange(direction, hovered); return true; }); } |