diff options
author | 2025-03-06 16:13:22 -0800 | |
---|---|---|
committer | 2025-03-06 16:13:22 -0800 | |
commit | 0067ed4d9d671b075e4eadcf0c912767040932a0 (patch) | |
tree | 5f97562a7fd52f50449190ffeec366cfd785e151 | |
parent | 41012cb37973378541582a76d35058737110e3db (diff) | |
parent | da65cb2bc18820242646285f0e08c6245b24b0b1 (diff) |
Merge "Altering TouchExplorer behavior to support Split Tap" into main
5 files changed, 146 insertions, 4 deletions
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index b52b3dabd47d..64a4a4ae3c0d 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -260,6 +260,16 @@ flag { } flag { + name: "pointer_up_motion_event_in_touch_exploration" + namespace: "accessibility" + description: "Allows POINTER_UP motionEvents to trigger during touch exploration." + bug: "374930391" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "proxy_use_apps_on_virtual_device_listener" namespace: "accessibility" description: "Fixes race condition described in b/286587811" diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index fb329430acb2..b02fe2752a62 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -653,6 +653,14 @@ public class TouchExplorer extends BaseEventStreamTransformation case ACTION_UP: handleActionUp(event, rawEvent, policyFlags); break; + case ACTION_POINTER_UP: + if (com.android.server.accessibility.Flags + .pointerUpMotionEventInTouchExploration()) { + if (mState.isServiceDetectingGestures()) { + mAms.sendMotionEventToListeningServices(rawEvent); + } + } + break; default: break; } diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java index cd46b38272c2..568abd196735 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java @@ -26,6 +26,8 @@ import android.view.Display; import android.view.MotionEvent; import android.view.accessibility.AccessibilityEvent; +import androidx.annotation.VisibleForTesting; + import com.android.server.accessibility.AccessibilityManagerService; /** @@ -73,7 +75,8 @@ public class TouchState { private int mState = STATE_CLEAR; // Helper class to track received pointers. // Todo: collapse or hide this class so multiple classes don't modify it. - private final ReceivedPointerTracker mReceivedPointerTracker; + @VisibleForTesting + public final ReceivedPointerTracker mReceivedPointerTracker; // The most recently received motion event. private MotionEvent mLastReceivedEvent; // The accompanying raw event without any transformations. @@ -219,8 +222,19 @@ public class TouchState { startTouchInteracting(); break; case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END: - setState(STATE_CLEAR); - // We will clear when we actually handle the next ACTION_DOWN. + // When interaction ends, check if there are still down pointers. + // If there are any down pointers, go directly to TouchExploring instead. + if (com.android.server.accessibility.Flags + .pointerUpMotionEventInTouchExploration()) { + if (mReceivedPointerTracker.mReceivedPointersDown > 0) { + startTouchExploring(); + } else { + setState(STATE_CLEAR); + // We will clear when we actually handle the next ACTION_DOWN. + } + } else { + setState(STATE_CLEAR); + } break; case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: startTouchExploring(); @@ -419,7 +433,8 @@ public class TouchState { private final PointerDownInfo[] mReceivedPointers = new PointerDownInfo[MAX_POINTER_COUNT]; // Which pointers are down. - private int mReceivedPointersDown; + @VisibleForTesting + public int mReceivedPointersDown; // The edge flags of the last received down event. private int mLastReceivedDownEdgeFlags; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java index 1af59daa9c78..5922b12edc1e 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java @@ -37,6 +37,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -46,6 +47,7 @@ import android.content.Context; import android.graphics.PointF; import android.os.Looper; import android.os.SystemClock; +import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.testing.DexmakerShareClassLoaderRule; @@ -504,6 +506,36 @@ public class TouchExplorerTest { assertThat(sentRawEvent.getDisplayId()).isEqualTo(rawDisplayId); } + @Test + @DisableFlags(Flags.FLAG_POINTER_UP_MOTION_EVENT_IN_TOUCH_EXPLORATION) + public void handleMotionEventStateTouchExploring_pointerUp_doesNotSendToManager() { + mTouchExplorer.getState().setServiceDetectsGestures(true); + mTouchExplorer.getState().clear(); + + mLastEvent = pointerDownEvent(); + mTouchExplorer.getState().startTouchExploring(); + MotionEvent event = fromTouchscreen(pointerUpEvent()); + + mTouchExplorer.onMotionEvent(event, event, /*policyFlags=*/0); + + verify(mMockAms, never()).sendMotionEventToListeningServices(event); + } + + @Test + @EnableFlags(Flags.FLAG_POINTER_UP_MOTION_EVENT_IN_TOUCH_EXPLORATION) + public void handleMotionEventStateTouchExploring_pointerUp_sendsToManager() { + mTouchExplorer.getState().setServiceDetectsGestures(true); + mTouchExplorer.getState().clear(); + + mLastEvent = pointerDownEvent(); + mTouchExplorer.getState().startTouchExploring(); + MotionEvent event = fromTouchscreen(pointerUpEvent()); + + mTouchExplorer.onMotionEvent(event, event, /*policyFlags=*/0); + + verify(mMockAms).sendMotionEventToListeningServices(event); + } + /** * Used to play back event data of a gesture by parsing the log into MotionEvents and sending * them to TouchExplorer. diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchStateTest.java new file mode 100644 index 000000000000..3e7d9fd05327 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchStateTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 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.gestures; + +import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_INTERACTION_END; + +import static com.android.server.accessibility.gestures.TouchState.STATE_CLEAR; +import static com.android.server.accessibility.gestures.TouchState.STATE_TOUCH_EXPLORING; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import android.view.Display; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.accessibility.AccessibilityManagerService; +import com.android.server.accessibility.Flags; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +@RunWith(AndroidJUnit4.class) +public class TouchStateTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + private TouchState mTouchState; + @Mock private AccessibilityManagerService mMockAms; + + @Before + public void setup() { + mTouchState = new TouchState(Display.DEFAULT_DISPLAY, mMockAms); + } + + @EnableFlags(Flags.FLAG_POINTER_UP_MOTION_EVENT_IN_TOUCH_EXPLORATION) + @Test + public void injectedEvent_interactionEnd_pointerDown_startsTouchExploring() { + mTouchState.mReceivedPointerTracker.mReceivedPointersDown = 1; + mTouchState.onInjectedAccessibilityEvent(TYPE_TOUCH_INTERACTION_END); + assertThat(mTouchState.getState()).isEqualTo(STATE_TOUCH_EXPLORING); + } + + @EnableFlags(Flags.FLAG_POINTER_UP_MOTION_EVENT_IN_TOUCH_EXPLORATION) + @Test + public void injectedEvent_interactionEnd_pointerUp_clears() { + mTouchState.mReceivedPointerTracker.mReceivedPointersDown = 0; + mTouchState.onInjectedAccessibilityEvent(TYPE_TOUCH_INTERACTION_END); + assertThat(mTouchState.getState()).isEqualTo(STATE_CLEAR); + } + + @DisableFlags(Flags.FLAG_POINTER_UP_MOTION_EVENT_IN_TOUCH_EXPLORATION) + @Test + public void injectedEvent_interactionEnd_clears() { + mTouchState.onInjectedAccessibilityEvent(TYPE_TOUCH_INTERACTION_END); + assertThat(mTouchState.getState()).isEqualTo(STATE_CLEAR); + } +} |