Merge "[flexiglass] Composables for scenes and scene container." into udc-dev
This commit is contained in:
@@ -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>
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,6 +40,7 @@ import com.android.systemui.qs.tileimpl.QSFactoryImpl;
|
|||||||
import com.android.systemui.recents.Recents;
|
import com.android.systemui.recents.Recents;
|
||||||
import com.android.systemui.recents.RecentsImplementation;
|
import com.android.systemui.recents.RecentsImplementation;
|
||||||
import com.android.systemui.rotationlock.RotationLockModule;
|
import com.android.systemui.rotationlock.RotationLockModule;
|
||||||
|
import com.android.systemui.scene.SceneContainerFrameworkModule;
|
||||||
import com.android.systemui.screenshot.ReferenceScreenshotModule;
|
import com.android.systemui.screenshot.ReferenceScreenshotModule;
|
||||||
import com.android.systemui.settings.dagger.MultiUserUtilsModule;
|
import com.android.systemui.settings.dagger.MultiUserUtilsModule;
|
||||||
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
|
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
|
||||||
@@ -103,6 +104,7 @@ import javax.inject.Named;
|
|||||||
QSModule.class,
|
QSModule.class,
|
||||||
ReferenceScreenshotModule.class,
|
ReferenceScreenshotModule.class,
|
||||||
RotationLockModule.class,
|
RotationLockModule.class,
|
||||||
|
SceneContainerFrameworkModule.class,
|
||||||
StatusBarEventsModule.class,
|
StatusBarEventsModule.class,
|
||||||
StartCentralSurfacesModule.class,
|
StartCentralSurfacesModule.class,
|
||||||
VolumeModule.class,
|
VolumeModule.class,
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user