diff options
11 files changed, 727 insertions, 0 deletions
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/shared/page/SceneModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/shared/page/SceneModule.kt new file mode 100644 index 000000000000..18c9513acf2a --- /dev/null +++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/shared/page/SceneModule.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.scene.shared.page + +import com.android.systemui.scene.shared.model.Scene +import dagger.Module +import dagger.multibindings.Multibinds + +@Module +interface SceneModule { + @Multibinds fun scenes(): Set<Scene> +} diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt new file mode 100644 index 000000000000..530706e47dcc --- /dev/null +++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.scene.ui.composable + +import com.android.systemui.bouncer.ui.composable.BouncerScene +import com.android.systemui.keyguard.ui.composable.LockScreenScene +import com.android.systemui.qs.ui.composable.QuickSettingsScene +import com.android.systemui.scene.shared.model.Scene +import com.android.systemui.shade.ui.composable.ShadeScene +import dagger.Module +import dagger.Provides + +@Module +object SceneModule { + @Provides + fun scenes( + bouncer: BouncerScene, + gone: GoneScene, + lockScreen: LockScreenScene, + qs: QuickSettingsScene, + shade: ShadeScene, + ): Set<Scene> { + return setOf( + bouncer, + gone, + lockScreen, + qs, + shade, + ) + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt new file mode 100644 index 000000000000..853300cf9508 --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.bouncer.ui.composable + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.scene.shared.model.UserAction +import com.android.systemui.scene.ui.composable.ComposableScene +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** The bouncer scene displays authentication challenges like PIN, password, or pattern. */ +@SysUISingleton +class BouncerScene +@Inject +constructor( + private val viewModel: BouncerViewModel, +) : ComposableScene { + override val key = SceneKey.Bouncer + + override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> = + MutableStateFlow<Map<UserAction, SceneModel>>( + mapOf( + UserAction.Back to SceneModel(SceneKey.LockScreen), + ) + ) + .asStateFlow() + + @Composable + override fun Content( + modifier: Modifier, + ) { + // TODO(b/280877228): implement the real UI. + + Box(modifier = modifier) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.align(Alignment.Center) + ) { + Text("Bouncer", style = MaterialTheme.typography.headlineLarge) + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Button(onClick = { viewModel.onAuthenticateButtonClicked() }) { + Text("Authenticate") + } + } + } + } + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockScreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockScreenScene.kt new file mode 100644 index 000000000000..ad33eb518f91 --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockScreenScene.kt @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.composable + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.common.ui.compose.Icon +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.ui.viewmodel.LockScreenSceneViewModel +import com.android.systemui.scene.shared.model.Direction +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.scene.shared.model.UserAction +import com.android.systemui.scene.ui.composable.ComposableScene +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +/** The lock screen scene shows when the device is locked. */ +@SysUISingleton +class LockScreenScene +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + private val viewModel: LockScreenSceneViewModel, +) : ComposableScene { + override val key = SceneKey.LockScreen + + override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> = + viewModel.upDestinationSceneKey + .map { pageKey -> destinationScenes(up = pageKey) } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = destinationScenes(up = viewModel.upDestinationSceneKey.value) + ) + + @Composable + override fun Content( + modifier: Modifier, + ) { + LockScreenScene( + viewModel = viewModel, + modifier = modifier, + ) + } + + private fun destinationScenes( + up: SceneKey, + ): Map<UserAction, SceneModel> { + return mapOf( + UserAction.Swipe(Direction.UP) to SceneModel(up), + UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.Shade) + ) + } +} + +@Composable +private fun LockScreenScene( + viewModel: LockScreenSceneViewModel, + modifier: Modifier = Modifier, +) { + // TODO(b/280879610): implement the real UI. + + val lockButtonIcon: Icon by viewModel.lockButtonIcon.collectAsState() + + Box(modifier = modifier) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.align(Alignment.Center) + ) { + Text("Lock screen", style = MaterialTheme.typography.headlineMedium) + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Button(onClick = { viewModel.onLockButtonClicked() }) { Icon(lockButtonIcon) } + + Button(onClick = { viewModel.onContentClicked() }) { Text("Open some content") } + } + } + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt new file mode 100644 index 000000000000..130395a38512 --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.ui.composable + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel +import com.android.systemui.scene.shared.model.Direction +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.scene.shared.model.UserAction +import com.android.systemui.scene.ui.composable.ComposableScene +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** The Quick Settings (AKA "QS") scene shows the quick setting tiles. */ +@SysUISingleton +class QuickSettingsScene +@Inject +constructor( + private val viewModel: QuickSettingsSceneViewModel, +) : ComposableScene { + override val key = SceneKey.QuickSettings + + override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> = + MutableStateFlow<Map<UserAction, SceneModel>>( + mapOf( + UserAction.Swipe(Direction.UP) to SceneModel(SceneKey.Shade), + ) + ) + .asStateFlow() + + @Composable + override fun Content( + modifier: Modifier, + ) { + QuickSettingsScene( + viewModel = viewModel, + modifier = modifier, + ) + } +} + +@Composable +private fun QuickSettingsScene( + viewModel: QuickSettingsSceneViewModel, + modifier: Modifier = Modifier, +) { + // TODO(b/280887232): implement the real UI. + + Box(modifier = modifier) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.align(Alignment.Center) + ) { + Text("Quick settings", style = MaterialTheme.typography.headlineMedium) + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Button(onClick = { viewModel.onContentClicked() }) { Text("Open some content") } + } + } + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt new file mode 100644 index 000000000000..a21366695f66 --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.scene.ui.composable + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.android.systemui.scene.shared.model.Scene + +/** Compose-capable extension of [Scene]. */ +interface ComposableScene : Scene { + @Composable fun Content(modifier: Modifier) +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt new file mode 100644 index 000000000000..007055221691 --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.scene.ui.composable + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.scene.shared.model.Direction +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.scene.shared.model.UserAction +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** + * "Gone" is not a real scene but rather the absence of scenes when we want to skip showing any + * content from the scene framework. + */ +@SysUISingleton +class GoneScene @Inject constructor() : ComposableScene { + override val key = SceneKey.Gone + + override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> = + MutableStateFlow<Map<UserAction, SceneModel>>( + mapOf( + UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.Shade), + ) + ) + .asStateFlow() + + @Composable + override fun Content( + modifier: Modifier, + ) { + /* + * TODO(b/279501596): once we start testing with the real Content Dynamics Framework, + * replace this with an error to make sure it doesn't get rendered. + */ + Box(modifier = modifier) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.align(Alignment.Center) + ) { + Text("Gone", style = MaterialTheme.typography.headlineMedium) + } + } + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt new file mode 100644 index 000000000000..f8a73d5294bc --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalAnimationApi::class) + +package com.android.systemui.scene.ui.composable + +import androidx.activity.compose.BackHandler +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.android.systemui.scene.shared.model.Direction +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.scene.shared.model.UserAction +import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel +import java.util.Locale + +/** + * Renders a container of a collection of "scenes" that the user can switch between using certain + * user actions (for instance, swiping up and down) or that can be switched automatically based on + * application business logic in response to certain events (for example, the device unlocking). + * + * It's possible for the application to host several such scene containers, the configuration system + * allows configuring each container with its own set of scenes. Scenes can be present in multiple + * containers. + * + * @param viewModel The UI state holder for this container. + * @param sceneByKey Mapping of [ComposableScene] by [SceneKey], ordered by z-order such that the + * last scene is rendered on top of all other scenes. It's critical that this map contains exactly + * and only the scenes on this container. In other words: (a) there should be no scene in this map + * that is not in the configuration for this container and (b) all scenes in the configuration + * must have entries in this map. + * @param modifier A modifier. + */ +@Composable +fun SceneContainer( + viewModel: SceneContainerViewModel, + sceneByKey: Map<SceneKey, ComposableScene>, + modifier: Modifier = Modifier, +) { + val currentScene: SceneModel by viewModel.currentScene.collectAsState() + + AnimatedContent( + targetState = currentScene.key, + label = "scene container", + modifier = modifier, + ) { currentSceneKey -> + sceneByKey.forEach { (key, composableScene) -> + if (key == currentSceneKey) { + Scene( + scene = composableScene, + onSceneChanged = viewModel::setCurrentScene, + modifier = Modifier.fillMaxSize(), + ) + } + } + } +} + +/** Renders the given [ComposableScene]. */ +@Composable +private fun Scene( + scene: ComposableScene, + onSceneChanged: (SceneModel) -> Unit, + modifier: Modifier = Modifier, +) { + // TODO(b/280880714): replace with the real UI and make sure to call onTransitionProgress. + Box(modifier) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.align(Alignment.Center), + ) { + scene.Content( + modifier = Modifier, + ) + + val destinationScenes: Map<UserAction, SceneModel> by + scene.destinationScenes().collectAsState() + val swipeLeftDestinationScene = destinationScenes[UserAction.Swipe(Direction.LEFT)] + val swipeUpDestinationScene = destinationScenes[UserAction.Swipe(Direction.UP)] + val swipeRightDestinationScene = destinationScenes[UserAction.Swipe(Direction.RIGHT)] + val swipeDownDestinationScene = destinationScenes[UserAction.Swipe(Direction.DOWN)] + val backDestinationScene = destinationScenes[UserAction.Back] + + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + DirectionalButton(Direction.LEFT, swipeLeftDestinationScene, onSceneChanged) + DirectionalButton(Direction.UP, swipeUpDestinationScene, onSceneChanged) + DirectionalButton(Direction.RIGHT, swipeRightDestinationScene, onSceneChanged) + DirectionalButton(Direction.DOWN, swipeDownDestinationScene, onSceneChanged) + } + + if (backDestinationScene != null) { + BackHandler { onSceneChanged.invoke(backDestinationScene) } + } + } + } +} + +@Composable +private fun DirectionalButton( + direction: Direction, + destinationScene: SceneModel?, + onSceneChanged: (SceneModel) -> Unit, + modifier: Modifier = Modifier, +) { + Button( + onClick = { destinationScene?.let { onSceneChanged.invoke(it) } }, + enabled = destinationScene != null, + modifier = modifier, + ) { + Text(direction.name.lowercase(Locale.getDefault())) + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt new file mode 100644 index 000000000000..5a092041df93 --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade.ui.composable + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.scene.shared.model.Direction +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.scene.shared.model.UserAction +import com.android.systemui.scene.ui.composable.ComposableScene +import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +/** The shade scene shows scrolling list of notifications and some of the quick setting tiles. */ +@SysUISingleton +class ShadeScene +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + private val viewModel: ShadeSceneViewModel, +) : ComposableScene { + override val key = SceneKey.Shade + + override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> = + viewModel.upDestinationSceneKey + .map { sceneKey -> destinationScenes(up = sceneKey) } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = destinationScenes(up = viewModel.upDestinationSceneKey.value), + ) + + @Composable + override fun Content( + modifier: Modifier, + ) { + ShadeScene( + viewModel = viewModel, + modifier = modifier, + ) + } + + private fun destinationScenes( + up: SceneKey, + ): Map<UserAction, SceneModel> { + return mapOf( + UserAction.Swipe(Direction.UP) to SceneModel(up), + UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.QuickSettings), + ) + } +} + +@Composable +private fun ShadeScene( + viewModel: ShadeSceneViewModel, + modifier: Modifier = Modifier, +) { + // TODO(b/280887022): implement the real UI. + + Box(modifier = modifier) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.align(Alignment.Center) + ) { + Text("Shade", style = MaterialTheme.typography.headlineMedium) + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Button( + onClick = { viewModel.onContentClicked() }, + ) { + Text("Open some content") + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index f68bd49230d9..2262d8ab2000 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -40,6 +40,7 @@ import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImplementation; import com.android.systemui.rotationlock.RotationLockModule; +import com.android.systemui.scene.SceneContainerFrameworkModule; import com.android.systemui.screenshot.ReferenceScreenshotModule; import com.android.systemui.settings.dagger.MultiUserUtilsModule; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; @@ -103,6 +104,7 @@ import javax.inject.Named; QSModule.class, ReferenceScreenshotModule.class, RotationLockModule.class, + SceneContainerFrameworkModule.class, StatusBarEventsModule.class, StartCentralSurfacesModule.class, VolumeModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt new file mode 100644 index 000000000000..0ed8b21c100e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.scene + +import com.android.systemui.scene.shared.page.SceneModule +import dagger.Module + +@Module( + includes = + [ + SceneModule::class, + ], +) +object SceneContainerFrameworkModule |