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

Commit 503055f7 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere Committed by Android (Google) Code Review
Browse files

Merge "Add hideCurrentOverlays to UserActionResult.ShowOverlay()" into main

parents 8682667d d54a506a
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -92,6 +92,10 @@ internal class DraggableHandler(
                else -> null
            } ?: return NoOpDragController

        if (result is UserActionResult.ShowOverlay) {
            layoutImpl.hideOverlays(result.hideCurrentOverlays)
        }

        val swipeAnimation = createSwipeAnimation(swipes, result)
        return updateDragController(swipes, swipeAnimation)
    }
+4 −0
Original line number Diff line number Diff line
@@ -44,6 +44,10 @@ internal fun PredictiveBackHandler(
            return@PredictiveBackHandler
        }

        if (result is ShowOverlay) {
            layoutImpl.hideOverlays(result.hideCurrentOverlays)
        }

        val animation =
            createSwipeAnimation(
                layoutImpl,
+16 −0
Original line number Diff line number Diff line
@@ -607,8 +607,24 @@ sealed class UserActionResult(
        val overlay: OverlayKey,
        override val transitionKey: TransitionKey? = null,
        override val requiresFullDistanceSwipe: Boolean = false,

        /** Specify which overlays (if any) should be hidden when this user action is started. */
        val hideCurrentOverlays: HideCurrentOverlays = HideCurrentOverlays.None,
    ) : UserActionResult(transitionKey, requiresFullDistanceSwipe) {
        override fun toContent(currentScene: SceneKey): ContentKey = overlay

        sealed class HideCurrentOverlays {
            /** Hide none of the current overlays. */
            object None : HideCurrentOverlays()

            /** Hide all current overlays. */
            object All : HideCurrentOverlays()

            /** Hide [overlays], for those in that set that are currently shown. */
            class Some(val overlays: Set<OverlayKey>) : HideCurrentOverlays() {
                constructor(vararg overlays: OverlayKey) : this(overlays.toSet())
            }
        }
    }

    /** A [UserActionResult] that hides [overlay]. */
+16 −1
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastForEachReversed
import androidx.compose.ui.zIndex
import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.Overlay
import com.android.compose.animation.scene.content.Scene
@@ -537,13 +538,27 @@ internal class SceneTransitionLayoutImpl(
            .sortedBy { it.zIndex }
    }

    internal fun hideOverlays(hide: HideCurrentOverlays) {
        fun maybeHide(overlay: OverlayKey) {
            if (state.canHideOverlay(overlay)) {
                state.hideOverlay(overlay, animationScope = this.animationScope)
            }
        }

        when (hide) {
            HideCurrentOverlays.None -> {}
            HideCurrentOverlays.All -> HashSet(state.currentOverlays).forEach { maybeHide(it) }
            is HideCurrentOverlays.Some -> hide.overlays.forEach { maybeHide(it) }
        }
    }

    @VisibleForTesting
    internal fun setContentsAndLayoutTargetSizeForTest(size: IntSize) {
        lastSize = size
        (scenes.values + overlays.values).forEach { it.targetSize = size }
    }

    internal fun overlaysOrNullForTest(): Map<OverlayKey, Overlay>? = _overlays
    @VisibleForTesting internal fun overlaysOrNullForTest(): Map<OverlayKey, Overlay>? = _overlays
}

private data class LayoutElement(private val layoutImpl: SceneTransitionLayoutImpl) :
+63 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assertIsDisplayed
@@ -50,7 +51,10 @@ import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestOverlays.OverlayA
import com.android.compose.animation.scene.TestOverlays.OverlayB
import com.android.compose.animation.scene.TestOverlays.OverlayC
import com.android.compose.animation.scene.TestOverlays.OverlayD
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.UserActionResult.ShowOverlay
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.test.assertSizeIsEqualTo
import com.android.compose.test.setContentAndCreateMainScope
@@ -821,4 +825,63 @@ class OverlayTest {
        assertThat(state.transitionState).isIdle()
        assertThat(state.transitionState).hasCurrentOverlays(/* empty */ )
    }

    @Test
    fun showOverlay_hideAllOverlays() {
        val state =
            rule.runOnUiThread {
                MutableSceneTransitionLayoutStateImpl(
                    SceneA,
                    initialOverlays = setOf(OverlayA, OverlayB, OverlayC),
                    // We don't allow overlay C to be hidden.
                    canHideOverlay = { it != OverlayC },
                )
            }

        var touchSlop = 0f
        rule.setContent {
            touchSlop = LocalViewConfiguration.current.touchSlop
            SceneTransitionLayout(state) {
                scene(SceneA) { Box(Modifier.fillMaxSize()) }
                overlay(OverlayA) { Box(Modifier.fillMaxSize()) }
                overlay(OverlayB) { Box(Modifier.fillMaxSize()) }
                overlay(
                    OverlayC,
                    mapOf(
                        Swipe.Down to
                            ShowOverlay(
                                OverlayD,
                                hideCurrentOverlays = ShowOverlay.HideCurrentOverlays.All,
                            )
                    ),
                ) {
                    Box(Modifier.fillMaxSize())
                }
                overlay(OverlayD) { Box(Modifier.fillMaxSize()) }
            }
        }

        assertThat(state.transitionState).hasCurrentOverlays(OverlayA, OverlayB, OverlayC)

        rule.onRoot().performTouchInput {
            down(center)
            moveBy(Offset(0f, touchSlop))
        }

        // We closed all overlay, but C can not be hidden.
        val transition = assertThat(state.transitionState).isShowOrHideOverlayTransition()
        assertThat(transition).hasCurrentScene(SceneA)
        assertThat(transition).hasCurrentOverlays(OverlayC)
        assertThat(transition).hasProgress(0f)
        assertThat(transition).hasOverlay(OverlayD)

        rule.onRoot().performTouchInput { moveBy(Offset(0f, bottom / 2f)) }
        assertThat(transition).hasProgress(0.5f)

        rule.onRoot().performTouchInput { up() }
        rule.waitForIdle()
        assertThat(state.transitionState).isIdle()
        assertThat(state.transitionState).hasCurrentScene(SceneA)
        assertThat(state.transitionState).hasCurrentOverlays(OverlayC, OverlayD)
    }
}
Loading