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

Commit b3a90ef6 authored by burakov's avatar burakov
Browse files

[Dual Shade] Do not close QS edit mode on swipe up, except from bottom.

It is currently too easy to accidentally close the QS shade while in
edit mode by scrolling vertically.

Fix: 417943358
Test: Manually tested by opening the QS shade on edit mode and swiping
 up from the center, as well as from the bottom, and verifying the
 expected behavior.
Test: Added unit tests.
Flag: com.android.systemui.scene_container
Change-Id: Ib5867d887ba7f949dc864682fa4943975bde14cc
parent 036e0e50
Loading
Loading
Loading
Loading
+26 −3
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
import com.android.systemui.scene.shared.model.Overlays
@@ -43,7 +44,7 @@ import org.junit.runner.RunWith
@EnableSceneContainer
class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope

    private val underTest = kosmos.quickSettingsShadeOverlayActionsViewModel
@@ -67,8 +68,7 @@ class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() {
            underTest.activateIn(this)
            assertThat(isEditing).isFalse()

            assertThat((actions?.get(Back) as? HideOverlay)?.overlay)
                .isEqualTo(Overlays.QuickSettingsShade)
            assertThat(actions?.get(Back)).isEqualTo(HideOverlay(Overlays.QuickSettingsShade))
        }

    @Test
@@ -82,6 +82,29 @@ class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() {
            assertThat(actions?.get(Back)).isNull()
        }

    @Test
    fun upAboveEdge_whileEditing_doesNotHideShade() =
        testScope.runTest {
            val actions by collectLastValue(underTest.actions)
            underTest.activateIn(this)

            kosmos.editModeViewModel.startEditing()

            assertThat(actions?.get(Swipe.Up)).isNull()
        }

    @Test
    fun upFromEdge_whileEditing_hidesShade() =
        testScope.runTest {
            val actions by collectLastValue(underTest.actions)
            underTest.activateIn(this)

            kosmos.editModeViewModel.startEditing()

            val userAction = Swipe.Up(fromSource = SceneContainerArea.BottomEdge)
            assertThat(actions?.get(userAction)).isEqualTo(HideOverlay(Overlays.QuickSettingsShade))
        }

    @Test
    fun downFromTopStart_switchesToNotificationsShade() =
        testScope.runTest {
+32 −29
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
@@ -33,14 +32,16 @@ import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintA
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneBackInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.enableSingleShade
import com.android.systemui.shade.domain.interactor.enableSplitShade
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
@@ -54,7 +55,7 @@ import org.junit.runner.RunWith
@EnableSceneContainer
class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope
    private val qsFlexiglassAdapter = kosmos.fakeQsSceneAdapter

@@ -78,7 +79,7 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
    @Test
    fun destinations_whenNotCustomizing_unlocked() =
        testScope.runTest {
            overrideResource(R.bool.config_use_split_notification_shade, false)
            kosmos.enableSingleShade()
            val actions by collectLastValue(underTest.actions)
            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
            qsFlexiglassAdapter.setCustomizing(false)
@@ -92,9 +93,9 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
            assertThat(actions)
                .isEqualTo(
                    mapOf(
                        Back to UserActionResult(Scenes.Shade),
                        Swipe.Up to UserActionResult(Scenes.Shade),
                        Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
                        Back to Scenes.Shade,
                        Swipe.Up to Scenes.Shade,
                        Swipe.Up(fromSource = Edge.Bottom) to SceneFamilies.Home,
                    )
                )
            assertThat(homeScene).isEqualTo(Scenes.Gone)
@@ -103,7 +104,7 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
    @Test
    fun destinations_whenNotCustomizing_withPreviousSceneLockscreen() =
        testScope.runTest {
            overrideResource(R.bool.config_use_split_notification_shade, false)
            kosmos.enableSingleShade()
            qsFlexiglassAdapter.setCustomizing(false)
            val actions by collectLastValue(underTest.actions)

@@ -118,9 +119,9 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
            assertThat(actions)
                .isEqualTo(
                    mapOf(
                        Back to UserActionResult(Scenes.Lockscreen),
                        Swipe.Up to UserActionResult(Scenes.Lockscreen),
                        Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
                        Back to Scenes.Lockscreen,
                        Swipe.Up to Scenes.Lockscreen,
                        Swipe.Up(fromSource = Edge.Bottom) to SceneFamilies.Home,
                    )
                )
            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
@@ -129,7 +130,7 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
    @Test
    fun destinations_whenNotCustomizing_withPreviousSceneLockscreen_butLockscreenDisabled() =
        testScope.runTest {
            overrideResource(R.bool.config_use_split_notification_shade, false)
            kosmos.enableSingleShade()
            qsFlexiglassAdapter.setCustomizing(false)
            val actions by collectLastValue(underTest.actions)

@@ -146,9 +147,9 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
            assertThat(actions)
                .isEqualTo(
                    mapOf(
                        Back to UserActionResult(Scenes.Shade),
                        Swipe.Up to UserActionResult(Scenes.Shade),
                        Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
                        Back to Scenes.Shade,
                        Swipe.Up to Scenes.Shade,
                        Swipe.Up(fromSource = Edge.Bottom) to SceneFamilies.Home,
                    )
                )
            assertThat(homeScene).isEqualTo(Scenes.Gone)
@@ -157,7 +158,7 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
    @Test
    fun destinations_whenNotCustomizing_authMethodSwipe_lockscreenNotDismissed() =
        testScope.runTest {
            overrideResource(R.bool.config_use_split_notification_shade, false)
            kosmos.enableSingleShade()
            val actions by collectLastValue(underTest.actions)
            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
            qsFlexiglassAdapter.setCustomizing(false)
@@ -169,28 +170,29 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
            assertThat(actions)
                .isEqualTo(
                    mapOf(
                        Back to UserActionResult(Scenes.Shade),
                        Swipe.Up to UserActionResult(Scenes.Shade),
                        Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
                        Back to Scenes.Shade,
                        Swipe.Up to Scenes.Shade,
                        Swipe.Up(fromSource = Edge.Bottom) to SceneFamilies.Home,
                    )
                )
            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
        }

    @Test
    fun destinations_whenCustomizing_noDestinations() =
    fun destinations_whenCustomizing_canDismissOnlyFromBottomEdge() =
        testScope.runTest {
            overrideResource(R.bool.config_use_split_notification_shade, false)
            kosmos.enableSingleShade()
            val actions by collectLastValue(underTest.actions)
            qsFlexiglassAdapter.setCustomizing(true)

            assertThat(actions).isEmpty()
            assertThat(actions)
                .isEqualTo(mapOf(Swipe.Up(fromSource = Edge.Bottom) to SceneFamilies.Home))
        }

    @Test
    fun destinations_whenNotCustomizing_inSplitShade_unlocked() =
        testScope.runTest {
            overrideResource(R.bool.config_use_split_notification_shade, true)
            kosmos.enableSplitShade()
            val actions by collectLastValue(underTest.actions)
            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
            qsFlexiglassAdapter.setCustomizing(false)
@@ -204,21 +206,22 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
            assertThat(actions)
                .isEqualTo(
                    mapOf(
                        Back to UserActionResult(Scenes.Shade),
                        Swipe.Up to UserActionResult(Scenes.Shade),
                        Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
                        Back to Scenes.Shade,
                        Swipe.Up to Scenes.Shade,
                        Swipe.Up(fromSource = Edge.Bottom) to SceneFamilies.Home,
                    )
                )
            assertThat(homeScene).isEqualTo(Scenes.Gone)
        }

    @Test
    fun destinations_whenCustomizing_inSplitShade_noDestinations() =
    fun destinations_whenCustomizing_inSplitShade_canDismissOnlyFromBottomEdge() =
        testScope.runTest {
            overrideResource(R.bool.config_use_split_notification_shade, true)
            kosmos.enableSplitShade()
            val actions by collectLastValue(underTest.actions)
            qsFlexiglassAdapter.setCustomizing(true)

            assertThat(actions).isEmpty()
            assertThat(actions)
                .isEqualTo(mapOf(Swipe.Up(fromSource = Edge.Bottom) to SceneFamilies.Home))
        }
}
+7 −1
Original line number Diff line number Diff line
@@ -111,6 +111,12 @@ class SceneContainerSwipeDetectorTest : SysuiTestCase() {
        assertThat(detectedEdge).isEqualTo(BottomEdge)
    }

    @Test
    fun source_swipeVerticallyAboveBottomEdge_detectsLeftHalf() {
        val detectedEdge = swipeVerticallyFrom(x = screenWidth / 3, y = screenHeight - edgeSize - 1)
        assertThat(detectedEdge).isEqualTo(LeftHalf)
    }

    @Test
    fun source_swipeHorizontallyOnBottom_detectsLeftHalf() {
        val detectedEdge =
@@ -199,7 +205,7 @@ class SceneContainerSwipeDetectorTest : SysuiTestCase() {
        return swipeFrom(x, y, Orientation.Horizontal)
    }

    private fun swipeFrom(x: Int, y: Int, orientation: Orientation): SceneContainerArea.Resolved? {
    private fun swipeFrom(x: Int, y: Int, orientation: Orientation): SceneContainerArea.Resolved {
        return underTest.source(
            layoutSize = IntSize(width = screenWidth, height = screenHeight),
            position = IntOffset(x, y),
+8 −5
Original line number Diff line number Diff line
@@ -38,12 +38,15 @@ constructor(private val editModeViewModel: EditModeViewModel) : UserActionsViewM
    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
        editModeViewModel.isEditing
            .map { isEditing ->
                val hideQuickSettings = HideOverlay(Overlays.QuickSettingsShade)
                buildMap {
                    put(Swipe.Up, HideOverlay(Overlays.QuickSettingsShade))
                    // When editing, back should go back to QS from edit mode (i.e. remain in the
                    // same overlay).
                    if (!isEditing) {
                        put(Back, HideOverlay(Overlays.QuickSettingsShade))
                    if (isEditing) {
                        // When editing, the back gesture is handled outside of this view-model.
                        // TODO(b/418003378): Back should go back to the QS grid layout.
                        put(Swipe.Up(fromSource = SceneContainerArea.BottomEdge), hideQuickSettings)
                    } else {
                        put(Back, hideQuickSettings)
                        put(Swipe.Up, hideQuickSettings)
                    }
                    put(
                        Swipe.Down(fromSource = SceneContainerArea.TopEdgeStartHalf),
+4 −13
Original line number Diff line number Diff line
@@ -53,22 +53,13 @@ constructor(private val qsSceneAdapter: QSSceneAdapter, sceneBackInteractor: Sce

    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
        combine(qsSceneAdapter.isCustomizerShowing, backScene) { isCustomizing, backScene ->
                buildMap<UserAction, UserActionResult> {
                    if (isCustomizing) {
                        // TODO(b/332749288) Empty map so there are no back handlers and back can
                        // close
                        // customizer

                        // TODO(b/330200163) Add an Up from Bottom to be able to collapse the shade
                        // while customizing
                    } else {
                buildMap {
                    // Disable "back" and "swipe up to dismiss" gestures while customizing.
                    if (!isCustomizing) {
                        put(Back, UserActionResult(backScene))
                        put(Swipe.Up, UserActionResult(backScene))
                        put(
                            Swipe.Up(fromSource = Edge.Bottom),
                            UserActionResult(SceneFamilies.Home),
                        )
                    }
                    put(Swipe.Up(fromSource = Edge.Bottom), UserActionResult(SceneFamilies.Home))
                }
            }
            .collect { actions -> setActions(actions) }
Loading