summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Riley Jones <jonesriley@google.com> 2025-03-06 16:13:22 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-03-06 16:13:22 -0800
commit0067ed4d9d671b075e4eadcf0c912767040932a0 (patch)
tree5f97562a7fd52f50449190ffeec366cfd785e151
parent41012cb37973378541582a76d35058737110e3db (diff)
parentda65cb2bc18820242646285f0e08c6245b24b0b1 (diff)
Merge "Altering TouchExplorer behavior to support Split Tap" into main
-rw-r--r--services/accessibility/accessibility.aconfig10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java32
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchStateTest.java77
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);
+ }
+}