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

Commit f296aa6c authored by Bryce Lee's avatar Bryce Lee
Browse files

Allow in-progress touch sessions to continue outside resume.

Currently, touch sessions are terminated outside the resume lifecycle
state. This behavior causes issues when a touch behavior is directly
leading to another state, such as dragging down the notification shade.
This changelist allows in-progress touch sessions to continue until
the session is popped. Note that lifecycle states here are used
abstractly and do not correspond to Activity usage. Also, touch
sessions are still forcefully ended in the case of resetting the touch
monitor due to the destroy lifecycle state or from starting monitoring
again.

Test: atest DreamOverlayTouchMonitorTest#testPauseWithNoActiveSessions
      atest DreamOverlayTouchMonitorTest#testDeferredPauseWithActiveSessions
      atest DreamOverlayTouchMonitorTest#testDestroyWithActiveSessions
Bug: 267565290
Change-Id: Ibac6e94bcf6b8c4d751349edfbcb04b2fe53285a
Merged-In: Ibac6e94bcf6b8c4d751349edfbcb04b2fe53285a
parent 3d8957b1
Loading
Loading
Loading
Loading
+20 −3
Original line number Diff line number Diff line
@@ -101,6 +101,10 @@ public class DreamOverlayTouchMonitor {

                    completer.set(predecessor);
                }

                if (mActiveTouchSessions.isEmpty() && mStopMonitoringPending) {
                    stopMonitoring(false);
                }
            });

            return "DreamOverlayTouchMonitor::pop";
@@ -214,7 +218,12 @@ public class DreamOverlayTouchMonitor {

        @Override
        public void onPause(@NonNull LifecycleOwner owner) {
            stopMonitoring();
            stopMonitoring(false);
        }

        @Override
        public void onDestroy(LifecycleOwner owner) {
            stopMonitoring(true);
        }
    };

@@ -222,7 +231,7 @@ public class DreamOverlayTouchMonitor {
     * When invoked, instantiates a new {@link InputSession} to monitor touch events.
     */
    private void startMonitoring() {
        stopMonitoring();
        stopMonitoring(true);
        mCurrentInputSession = mInputSessionFactory.create(
                "dreamOverlay",
                mInputEventListener,
@@ -234,11 +243,16 @@ public class DreamOverlayTouchMonitor {
    /**
     * Destroys any active {@link InputSession}.
     */
    private void stopMonitoring() {
    private void stopMonitoring(boolean force) {
        if (mCurrentInputSession == null) {
            return;
        }

        if (!mActiveTouchSessions.isEmpty() && !force) {
            mStopMonitoringPending = true;
            return;
        }

        // When we stop monitoring touches, we must ensure that all active touch sessions and
        // descendants informed of the removal so any cleanup for active tracking can proceed.
        mExecutor.execute(() -> mActiveTouchSessions.forEach(touchSession -> {
@@ -250,6 +264,7 @@ public class DreamOverlayTouchMonitor {

        mCurrentInputSession.dispose();
        mCurrentInputSession = null;
        mStopMonitoringPending = false;
    }


@@ -257,6 +272,8 @@ public class DreamOverlayTouchMonitor {
    private final Collection<DreamTouchHandler> mHandlers;
    private final DisplayHelper mDisplayHelper;

    private boolean mStopMonitoringPending;

    private InputChannelCompat.InputEventListener mInputEventListener =
            new InputChannelCompat.InputEventListener() {
        @Override
+61 −2
Original line number Diff line number Diff line
@@ -399,7 +399,21 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase {
    }

    @Test
    public void testPause() {
    public void testPauseWithNoActiveSessions() {
        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);

        final Environment environment = new Environment(Stream.of(touchHandler)
                .collect(Collectors.toCollection(HashSet::new)));

        environment.updateLifecycle(observerOwnerPair -> {
            observerOwnerPair.first.onPause(observerOwnerPair.second);
        });

        environment.verifyInputSessionDispose();
    }

    @Test
    public void testDeferredPauseWithActiveSessions() {
        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);

        final Environment environment = new Environment(Stream.of(touchHandler)
@@ -417,13 +431,58 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase {
        environment.publishInputEvent(event);
        verify(eventListener).onInputEvent(eq(event));

        final ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionArgumentCaptor =
                ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);

        verify(touchHandler).onSessionStart(touchSessionArgumentCaptor.capture());

        environment.updateLifecycle(observerOwnerPair -> {
            observerOwnerPair.first.onPause(observerOwnerPair.second);
        });

        verify(environment.mInputSession, never()).dispose();

        // End session
        touchSessionArgumentCaptor.getValue().pop();
        environment.executeAll();

        // Check to make sure the input session is now disposed.
        environment.verifyInputSessionDispose();
    }

    @Test
    public void testDestroyWithActiveSessions() {
        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);

        final Environment environment = new Environment(Stream.of(touchHandler)
                .collect(Collectors.toCollection(HashSet::new)));

        final InputEvent initialEvent = Mockito.mock(InputEvent.class);
        environment.publishInputEvent(initialEvent);

        // Ensure session started
        final InputChannelCompat.InputEventListener eventListener =
                registerInputEventListener(touchHandler);

        // First event will be missed since we register after the execution loop,
        final InputEvent event = Mockito.mock(InputEvent.class);
        environment.publishInputEvent(event);
        verify(eventListener).onInputEvent(eq(event));

        final ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionArgumentCaptor =
                ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);

        verify(touchHandler).onSessionStart(touchSessionArgumentCaptor.capture());

        environment.updateLifecycle(observerOwnerPair -> {
            observerOwnerPair.first.onDestroy(observerOwnerPair.second);
        });

        // Check to make sure the input session is now disposed.
        environment.verifyInputSessionDispose();
    }


    @Test
    public void testPilfering() {
        final DreamTouchHandler touchHandler1 = Mockito.mock(DreamTouchHandler.class);
@@ -476,7 +535,7 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase {
        environment.executeAll();

        environment.updateLifecycle(observerOwnerPair -> {
            observerOwnerPair.first.onPause(observerOwnerPair.second);
            observerOwnerPair.first.onDestroy(observerOwnerPair.second);
        });

        environment.executeAll();