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

Commit 64ba3982 authored by William Xiao's avatar William Xiao
Browse files

Add SceneTransitionLayout in SysUI

This SceneTransitionLayout contains only two scenes, one default scene
that shows over the keyguard/dream and will be completely blank, then a
communal scene containing the glanceable hub UI. SceneTransitionLayout
handles the gesture detection and animation.

Currently the SceneTransitionLayout covers the whole screen and
prevents swiping up to get the bouncer from working, so I've included a
close button to hide it. Temporary UI will be removed once the bouncer
swipe is fixed.

Change is flagged under the communal_hub flag: b/304584416.

Video: http://screencast/cast/NTc2MTIzNjQ1MTk4MzM2MHw3ZTQyYTU4My0xOA

Bug: 303920627
Bug: 304584416
Test: atest SystemUI:NotificationShadeWindowViewControllerTest
Change-Id: Iecff9f200e2f2e520cdc16a5a2bfad3cc1c5b80c
parent 44ff5531
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -72,6 +72,10 @@ object ComposeFacade : BaseComposeFacade {
        throwComposeUnavailableError()
    }

    override fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View {
        throwComposeUnavailableError()
    }

    private fun throwComposeUnavailableError(): Nothing {
        error(
            "Compose is not available. Make sure to check isComposeAvailable() before calling any" +
+7 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import com.android.compose.theme.PlatformTheme
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutoutProvider
import com.android.systemui.communal.ui.compose.CommunalContainer
import com.android.systemui.communal.ui.compose.CommunalHub
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.people.ui.compose.PeopleScreen
@@ -104,6 +105,12 @@ object ComposeFacade : BaseComposeFacade {
        }
    }

    override fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View {
        return ComposeView(context).apply {
            setContent { PlatformTheme { CommunalContainer(viewModel = viewModel) } }
        }
    }

    // TODO(b/298525212): remove once Compose exposes window inset bounds.
    private fun displayCutoutFromWindowInsets(
        scope: CoroutineScope,
+123 −0
Original line number Diff line number Diff line
package com.android.systemui.communal.ui.compose

import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.transitions
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel

object Scenes {
    val Blank = SceneKey(name = "blank")
    val Communal = SceneKey(name = "communal")
}

object Communal {
    object Elements {
        val Content = ElementKey("CommunalContent")
    }
}

val sceneTransitions = transitions {
    from(Scenes.Blank, to = Scenes.Communal) {
        spec = tween(durationMillis = 500)

        translate(Communal.Elements.Content, Edge.Right)
        fade(Communal.Elements.Content)
    }
}

/**
 * View containing a [SceneTransitionLayout] that shows the communal UI and handles transitions.
 *
 * This is a temporary container to allow the communal UI to use [SceneTransitionLayout] for gesture
 * handling and transitions before the full Flexiglass layout is ready.
 */
@Composable
fun CommunalContainer(modifier: Modifier = Modifier, viewModel: CommunalViewModel) {
    val (currentScene, setCurrentScene) = remember { mutableStateOf(Scenes.Blank) }

    // Failsafe to hide the whole SceneTransitionLayout in case of bugginess.
    var showSceneTransitionLayout by remember { mutableStateOf(true) }
    if (!showSceneTransitionLayout) {
        return
    }

    SceneTransitionLayout(
        modifier = modifier.fillMaxSize(),
        currentScene = currentScene,
        onChangeScene = setCurrentScene,
        transitions = sceneTransitions,
    ) {
        scene(Scenes.Blank, userActions = mapOf(Swipe.Left to Scenes.Communal)) {
            BlankScene { showSceneTransitionLayout = false }
        }

        scene(
            Scenes.Communal,
            userActions = mapOf(Swipe.Right to Scenes.Blank),
        ) {
            CommunalScene(viewModel, modifier = modifier)
        }
    }
}

/**
 * Blank scene that shows over keyguard/dream. This scene will eventually show nothing at all and is
 * only used to allow for transitions to the communal scene.
 */
@Composable
private fun BlankScene(
    modifier: Modifier = Modifier,
    hideSceneTransitionLayout: () -> Unit,
) {
    Box(modifier.fillMaxSize()) {
        Column(
            Modifier.fillMaxHeight()
                .width(100.dp)
                .align(Alignment.CenterEnd)
                .background(Color(0x55e9f2eb)),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text("Default scene")

            IconButton(onClick = hideSceneTransitionLayout) {
                Icon(Icons.Filled.Close, contentDescription = "Close button")
            }
        }
    }
}

/** Scene containing the glanceable hub UI. */
@Composable
private fun SceneScope.CommunalScene(
    viewModel: CommunalViewModel,
    modifier: Modifier = Modifier,
) {
    Box(modifier.element(Communal.Elements.Content)) { CommunalHub(viewModel = viewModel) }
}
+6 −0
Original line number Diff line number Diff line
@@ -83,6 +83,12 @@
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- Placeholder for the communal UI that will be replaced if the feature is enabled. -->
    <ViewStub
        android:id="@+id/communal_ui_stub"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <include layout="@layout/brightness_mirror_container" />

    <com.android.systemui.scrim.ScrimView
+3 −0
Original line number Diff line number Diff line
@@ -79,4 +79,7 @@ interface BaseComposeFacade {
        context: Context,
        viewModel: CommunalViewModel,
    ): View

    /** Creates a container that hosts the communal UI and handles gesture transitions. */
    fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View
}
Loading