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

Commit e0605576 authored by William Xiao's avatar William Xiao Committed by Android (Google) Code Review
Browse files

Merge "Fix hub also reacting to lockscreen shade/bouncer gestures" into main

parents 601ca2ff 8b4b3c7d
Loading
Loading
Loading
Loading
+34 −3
Original line number Diff line number Diff line
@@ -181,6 +181,16 @@ constructor(
     */
    private var shadeShowingAndConsumingTouches = false

    /**
     * True anytime the shade is processing user touches, regardless of expansion state.
     *
     * Based on [ShadeInteractor.isUserInteracting].
     */
    private var shadeConsumingTouches = false

    /** True if the keyguard transition state is finished on [KeyguardState.LOCKSCREEN]. */
    private var onLockscreen = false

    /**
     * True if the shade ever fully expands and the user isn't interacting with it (aka finger on
     * screen dragging). In this case, the shade should handle all touch events until it has fully
@@ -336,6 +346,11 @@ constructor(
                updateTouchHandlingState()
            }
        )
        collectFlow(
            containerView,
            keyguardTransitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN),
            { onLockscreen = it }
        )
        collectFlow(
            containerView,
            communalInteractor.isCommunalVisible,
@@ -369,6 +384,7 @@ constructor(
                ::Triple
            ),
            { (isFullyExpanded, isUserInteracting, isShadeFullyCollapsed) ->
                shadeConsumingTouches = isUserInteracting
                val expandedAndNotInteractive = isFullyExpanded && !isUserInteracting

                // If we ever are fully expanded and not interacting, capture this state as we
@@ -497,12 +513,27 @@ constructor(
            return true
        }
        try {
            // On the lock screen, our touch handlers are not active and we rely on the NSWVC's
            // touch handling for gestures on blank areas, which can go up to show the bouncer or
            // down to show the notification shade. We see the touches first and they are not
            // consumed and cancelled like on the dream or hub so we have to gracefully ignore them
            // if the shade or bouncer are handling them. This issue only applies to touches on the
            // keyguard itself, once the bouncer or shade are fully open, our logic stops us from
            // taking touches.
            val touchTaken = onLockscreen && (shadeConsumingTouches || anyBouncerShowing)

            // Only dispatch touches to communal if not already handled or the touch is ending,
            // meaning the event is an up or cancel. This is necessary as the hub always receives at
            // least the initial down even if the shade or bouncer end up handling the touch.
            val dispatchToCommunal = !touchTaken || !isTrackingHubTouch
            var handled = false
            if (dispatchToCommunal) {
                communalContainerWrapper?.dispatchTouchEvent(ev) {
                    if (it) {
                        handled = true
                    }
                }
            }
            return handled || hubShowing
        } finally {
            powerManager.userActivity(
+75 −2
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.controller.keyguardMediaController
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.sceneDataSourceDelegator
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.lockscreen.lockscreenSmartspaceController
import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
@@ -727,7 +728,9 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {

                // Touch event is sent to the container view.
                assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
                verify(containerView).onTouchEvent(any())
                verify(containerView).onTouchEvent(DOWN_EVENT)
                assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
                verify(containerView).onTouchEvent(UP_EVENT)
            }
        }

@@ -774,13 +777,83 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
            }
        }

    @Test
    fun onTouchEvent_shadeInteracting_movesNotDispatched() =
        with(kosmos) {
            testScope.runTest {
                // On lockscreen.
                goToScene(CommunalScenes.Blank)
                whenever(
                        notificationStackScrollLayoutController.isBelowLastNotification(
                            any(),
                            any()
                        )
                    )
                    .thenReturn(true)

                // Touches not consumed by default but are received by containerView.
                assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
                verify(containerView).onTouchEvent(DOWN_EVENT)

                // User is interacting with shade on lockscreen.
                fakeShadeRepository.setLegacyLockscreenShadeTracking(true)
                testableLooper.processAllMessages()

                // A move event is ignored while the user is already interacting.
                assertThat(underTest.onTouchEvent(MOVE_EVENT)).isFalse()
                verify(containerView, never()).onTouchEvent(MOVE_EVENT)

                // An up event is still delivered.
                assertThat(underTest.onTouchEvent(UP_EVENT)).isFalse()
                verify(containerView).onTouchEvent(UP_EVENT)
            }
        }

    @Test
    fun onTouchEvent_bouncerInteracting_movesNotDispatched() =
        with(kosmos) {
            testScope.runTest {
                // On lockscreen.
                goToScene(CommunalScenes.Blank)
                whenever(
                        notificationStackScrollLayoutController.isBelowLastNotification(
                            any(),
                            any()
                        )
                    )
                    .thenReturn(true)

                // Touches not consumed by default but are received by containerView.
                assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
                verify(containerView).onTouchEvent(DOWN_EVENT)

                // User is interacting with bouncer on lockscreen.
                fakeKeyguardBouncerRepository.setPrimaryShow(true)
                testableLooper.processAllMessages()

                // A move event is ignored while the user is already interacting.
                assertThat(underTest.onTouchEvent(MOVE_EVENT)).isFalse()
                verify(containerView, never()).onTouchEvent(MOVE_EVENT)

                // An up event is still delivered.
                assertThat(underTest.onTouchEvent(UP_EVENT)).isFalse()
                verify(containerView).onTouchEvent(UP_EVENT)
            }
        }

    private fun initAndAttachContainerView() {
        val mockInsets =
            mock<WindowInsets> {
                on { getInsets(WindowInsets.Type.systemGestures()) } doReturn FAKE_INSETS
            }

        containerView = spy(View(context)) { on { rootWindowInsets } doReturn mockInsets }
        containerView =
            spy(View(context)) {
                on { rootWindowInsets } doReturn mockInsets
                // Return true to handle touch events or else further events in the gesture will not
                // be received as we are using real View objects.
                onGeneric { onTouchEvent(any()) } doReturn true
            }

        parentView = FrameLayout(context)