1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.systemui.qs.ui.composable
18
19 import androidx.compose.animation.AnimatedVisibility
20 import androidx.compose.animation.EnterTransition
21 import androidx.compose.animation.ExitTransition
22 import androidx.compose.animation.core.tween
23 import androidx.compose.animation.fadeIn
24 import androidx.compose.animation.fadeOut
25 import androidx.compose.foundation.layout.Arrangement
26 import androidx.compose.foundation.layout.Box
27 import androidx.compose.foundation.layout.Column
28 import androidx.compose.foundation.layout.fillMaxWidth
29 import androidx.compose.foundation.layout.height
30 import androidx.compose.foundation.layout.heightIn
31 import androidx.compose.foundation.layout.padding
32 import androidx.compose.material3.Button
33 import androidx.compose.material3.Text
34 import androidx.compose.runtime.Composable
35 import androidx.compose.runtime.getValue
36 import androidx.compose.ui.Alignment
37 import androidx.compose.ui.Modifier
38 import androidx.compose.ui.unit.dp
39 import androidx.lifecycle.compose.collectAsStateWithLifecycle
40 import com.android.compose.animation.scene.SceneScope
41 import com.android.compose.animation.scene.UserAction
42 import com.android.compose.animation.scene.UserActionResult
43 import com.android.systemui.battery.BatteryMeterViewController
44 import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
45 import com.android.systemui.dagger.SysUISingleton
46 import com.android.systemui.keyguard.ui.composable.LockscreenContent
47 import com.android.systemui.qs.panels.ui.compose.EditMode
48 import com.android.systemui.qs.panels.ui.compose.TileGrid
49 import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneViewModel
50 import com.android.systemui.scene.shared.model.Scenes
51 import com.android.systemui.scene.ui.composable.ComposableScene
52 import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
53 import com.android.systemui.shade.ui.composable.OverlayShade
54 import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
55 import com.android.systemui.statusbar.phone.ui.StatusBarIconController
56 import com.android.systemui.statusbar.phone.ui.TintedIconManager
57 import dagger.Lazy
58 import java.util.Optional
59 import javax.inject.Inject
60 import kotlinx.coroutines.flow.StateFlow
61
62 @SysUISingleton
63 class QuickSettingsShadeScene
64 @Inject
65 constructor(
66 private val viewModel: QuickSettingsShadeSceneViewModel,
67 private val lockscreenContent: Lazy<Optional<LockscreenContent>>,
68 private val shadeHeaderViewModel: ShadeHeaderViewModel,
69 private val tintedIconManagerFactory: TintedIconManager.Factory,
70 private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
71 private val statusBarIconController: StatusBarIconController,
72 ) : ComposableScene {
73
74 override val key = Scenes.QuickSettingsShade
75
76 override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
77 viewModel.destinationScenes
78
79 @Composable
Contentnull80 override fun SceneScope.Content(
81 modifier: Modifier,
82 ) {
83 OverlayShade(
84 viewModel = viewModel.overlayShadeViewModel,
85 panelAlignment = Alignment.TopEnd,
86 lockscreenContent = lockscreenContent,
87 modifier = modifier,
88 ) {
89 Column {
90 ExpandedShadeHeader(
91 viewModel = shadeHeaderViewModel,
92 createTintedIconManager = tintedIconManagerFactory::create,
93 createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
94 statusBarIconController = statusBarIconController,
95 modifier = Modifier.padding(QuickSettingsShade.Dimensions.Padding),
96 )
97
98 ShadeBody(
99 viewModel = viewModel,
100 )
101 }
102 }
103 }
104 }
105
106 @Composable
ShadeBodynull107 private fun ShadeBody(
108 viewModel: QuickSettingsShadeSceneViewModel,
109 ) {
110 val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle()
111
112 Box {
113 // The main Quick Settings grid layout.
114 AnimatedVisibility(
115 visible = !isEditing,
116 enter = QuickSettingsShade.Transitions.QuickSettingsLayoutEnter,
117 exit = QuickSettingsShade.Transitions.QuickSettingsLayoutExit,
118 ) {
119 QuickSettingsLayout(
120 viewModel = viewModel,
121 )
122 }
123
124 // The Quick Settings Editor layout.
125 AnimatedVisibility(
126 visible = isEditing,
127 enter = QuickSettingsShade.Transitions.QuickSettingsEditorEnter,
128 exit = QuickSettingsShade.Transitions.QuickSettingsEditorExit,
129 ) {
130 EditMode(
131 viewModel = viewModel.editModeViewModel,
132 modifier = Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding)
133 )
134 }
135 }
136 }
137
138 @Composable
QuickSettingsLayoutnull139 private fun QuickSettingsLayout(
140 viewModel: QuickSettingsShadeSceneViewModel,
141 modifier: Modifier = Modifier,
142 ) {
143 Column(
144 verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding),
145 horizontalAlignment = Alignment.CenterHorizontally,
146 modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
147 ) {
148 BrightnessSliderContainer(
149 viewModel = viewModel.brightnessSliderViewModel,
150 modifier =
151 Modifier.fillMaxWidth()
152 .height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
153 )
154 TileGrid(
155 viewModel = viewModel.tileGridViewModel,
156 modifier =
157 Modifier.fillMaxWidth().heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
158 )
159 Button(
160 onClick = { viewModel.editModeViewModel.startEditing() },
161 ) {
162 Text("Edit mode")
163 }
164 }
165 }
166
167 object QuickSettingsShade {
168
169 object Dimensions {
170 val Padding = 16.dp
171 val BrightnessSliderHeight = 64.dp
172 val GridMaxHeight = 400.dp
173 }
174
175 object Transitions {
176 val QuickSettingsLayoutEnter: EnterTransition = fadeIn(tween(500))
177 val QuickSettingsLayoutExit: ExitTransition = fadeOut(tween(500))
178 val QuickSettingsEditorEnter: EnterTransition = fadeIn(tween(500))
179 val QuickSettingsEditorExit: ExitTransition = fadeOut(tween(500))
180 }
181 }
182