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

Commit e9407837 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Ensure that movable elements are composed even when not in a transition

This CL fixes a bug introduced with overlays: if a movable element is in
an overlay that is not part of any transition, that element would not be
composed at all anymore, even if the overlay is still shown.

Bug: 373799480
Test: atest MovableElementTest
Flag: com.android.systemui.scene_container
Change-Id: Ic875ea39f5351321c58d4c1de3fccda79d94de00
parent 21d78196
Loading
Loading
Loading
Loading
+4 −14
Original line number Diff line number Diff line
@@ -350,8 +350,7 @@ internal class ElementNode(
            val placeInThisContent =
                elementContentWhenIdle(
                    layoutImpl,
                    currentState.currentScene,
                    currentState.currentOverlays,
                    currentState,
                    isInContent = { it in element.stateByContent },
                ) == content.key

@@ -639,20 +638,11 @@ internal inline fun elementState(

internal inline fun elementContentWhenIdle(
    layoutImpl: SceneTransitionLayoutImpl,
    idle: TransitionState.Idle,
    isInContent: (ContentKey) -> Boolean,
): ContentKey {
    val currentScene = idle.currentScene
    val overlays = idle.currentOverlays
    return elementContentWhenIdle(layoutImpl, currentScene, overlays, isInContent)
}

private inline fun elementContentWhenIdle(
    layoutImpl: SceneTransitionLayoutImpl,
    currentScene: SceneKey,
    overlays: Set<OverlayKey>,
    currentState: TransitionState,
    isInContent: (ContentKey) -> Boolean,
): ContentKey {
    val currentScene = currentState.currentScene
    val overlays = currentState.currentOverlays
    if (overlays.isEmpty()) {
        return currentScene
    }
+4 −2
Original line number Diff line number Diff line
@@ -188,7 +188,9 @@ private fun shouldComposeMovableElement(
    return when (
        val elementState = movableElementState(element, layoutImpl.state.transitionStates)
    ) {
        null -> false
        null ->
            movableElementContentWhenIdle(layoutImpl, element, layoutImpl.state.transitionState) ==
                content
        is TransitionState.Idle ->
            movableElementContentWhenIdle(layoutImpl, element, elementState) == content
        is TransitionState.Transition -> {
@@ -217,7 +219,7 @@ private fun movableElementState(
private fun movableElementContentWhenIdle(
    layoutImpl: SceneTransitionLayoutImpl,
    element: MovableElementKey,
    elementState: TransitionState.Idle,
    elementState: TransitionState,
): ContentKey {
    val contents = element.contentPicker.contents
    return elementContentWhenIdle(layoutImpl, elementState, isInContent = { contents.contains(it) })
+37 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertPositionInRootIsEqualTo
import androidx.compose.ui.test.hasParent
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithText
@@ -404,4 +405,40 @@ class MovableElementTest {
        rule.waitForIdle()
        rule.onNodeWithTag(fooParentInOverlayTag).assertSizeIsEqualTo(fooSize)
    }

    @Test
    fun movableElementInOverlayShouldBeComposed() {
        val fooKey = MovableElementKey("foo", contents = setOf(OverlayA))
        val fooContentTag = "fooContentTag"

        @Composable
        fun ContentScope.MovableFoo(modifier: Modifier = Modifier) {
            MovableElement(fooKey, modifier) {
                content { Box(Modifier.testTag(fooContentTag).size(100.dp)) }
            }
        }

        val state =
            rule.runOnUiThread {
                MutableSceneTransitionLayoutState(
                    initialScene = SceneA,
                    initialOverlays = setOf(OverlayA),
                )
            }

        val scope =
            rule.setContentAndCreateMainScope {
                SceneTransitionLayout(state) {
                    scene(SceneA) { Box(Modifier.fillMaxSize()) }
                    overlay(OverlayA) { MovableFoo() }
                    overlay(OverlayB) { Box(Modifier.size(50.dp)) }
                }
            }

        rule.onNode(hasTestTag(fooContentTag)).assertIsDisplayed().assertSizeIsEqualTo(100.dp)

        // Show overlay B. This shouldn't have any impact on Foo that should still be composed in A.
        scope.launch { state.startTransition(transition(SceneA, OverlayB)) }
        rule.onNode(hasTestTag(fooContentTag)).assertIsDisplayed().assertSizeIsEqualTo(100.dp)
    }
}