Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0067ed4d authored by Riley Jones's avatar Riley Jones Committed by Android (Google) Code Review
Browse files

Merge "Altering TouchExplorer behavior to support Split Tap" into main

parents 41012cb3 da65cb2b
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -259,6 +259,16 @@ flag {
    bug: "295327792"
}

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"
+8 −0
Original line number Diff line number Diff line
@@ -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;
        }
+19 −4
Original line number Diff line number Diff line
@@ -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:
                // 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;
+32 −0
Original line number Diff line number Diff line
@@ -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.
+77 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}