From f9e90fd2abf490c455a4b33fe8725ace62f083fc Mon Sep 17 00:00:00 2001 From: Jordan Demeulenaere Date: Mon, 25 Jul 2022 17:30:20 +0200 Subject: [PATCH 1/2] Represent the Gallery screen hierarchy as a tree This CL changes the Gallery app screens to be a tree instead of a list. This will allow adding sub-menus to the gallery app in case we want multiple different screens for a single feature, which is the case for the People activity/screens. Bug: 238993727 Test: Builds Change-Id: Idd28fd5a0fa79fd32ab1f8a95ed8c16fa2f465ac --- .../systemui/compose/gallery/GalleryApp.kt | 38 ++++---- .../systemui/compose/gallery/HomeScreen.kt | 59 ------------ .../systemui/compose/gallery/Screen.kt | 95 +++++++++++++++++++ 3 files changed, 115 insertions(+), 77 deletions(-) delete mode 100644 packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/HomeScreen.kt create mode 100644 packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt index 0ac3a63aa9972..23badd0b44ba3 100644 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt +++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt @@ -23,36 +23,38 @@ import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import com.android.systemui.compose.theme.SystemUITheme -enum class Screen { - Home, - Typography, - MaterialColors, - AndroidColors, - ExampleFeature, +/** The gallery app screens. */ +object GalleryAppScreens { + val Typography = ChildScreen("typography") { TypographyScreen() } + val MaterialColors = ChildScreen("material_colors") { MaterialColorsScreen() } + val AndroidColors = ChildScreen("android_colors") { AndroidColorsScreen() } + val ExampleFeature = ChildScreen("example_feature") { ExampleFeatureScreen() } + + val Home = + ParentScreen( + "home", + mapOf( + "Typography" to Typography, + "Material colors" to MaterialColors, + "Android colors" to AndroidColors, + "Example feature" to ExampleFeature, + ) + ) } -/** The main content of the app, that shows the [HomeScreen] by default. */ +/** The main content of the app, that shows [GalleryAppScreens.Home] by default. */ @Composable private fun MainContent() { Box(Modifier.fillMaxSize()) { val navController = rememberNavController() NavHost( navController = navController, - startDestination = Screen.Home.name, + startDestination = GalleryAppScreens.Home.identifier, ) { - composable(Screen.Home.name) { - HomeScreen( - onScreenSelected = { navController.navigate(it.name) }, - ) - } - composable(Screen.Typography.name) { TypographyScreen() } - composable(Screen.MaterialColors.name) { MaterialColorsScreen() } - composable(Screen.AndroidColors.name) { AndroidColorsScreen() } - composable(Screen.ExampleFeature.name) { ExampleFeatureScreen() } + screen(GalleryAppScreens.Home, navController) } } } diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/HomeScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/HomeScreen.kt deleted file mode 100644 index b1869da981367..0000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/HomeScreen.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2022 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.compose.gallery - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -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 - -/** The home screen shown when starting the app. */ -@Composable -fun HomeScreen(onScreenSelected: (Screen) -> Unit) { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - Screen.values() - .filter { it != Screen.Home } - .forEach { screen -> - item { - Surface( - Modifier.fillMaxWidth(), - color = MaterialTheme.colorScheme.secondaryContainer, - shape = CircleShape, - ) { - Column( - Modifier.clickable { onScreenSelected(screen) }.padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text(screen.name) - } - } - } - } - } -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt new file mode 100644 index 0000000000000..467dac044b79c --- /dev/null +++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2022 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.compose.gallery + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +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 androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import androidx.navigation.compose.navigation + +/** + * A screen in an app. It is either an [ParentScreen] which lists its child screens to navigate to + * them or a [ChildScreen] which shows some content. + */ +sealed class Screen(val identifier: String) + +class ParentScreen( + identifier: String, + val children: Map, +) : Screen(identifier) + +class ChildScreen( + identifier: String, + val content: @Composable (NavController) -> Unit, +) : Screen(identifier) + +/** Create the navigation graph for [screen]. */ +fun NavGraphBuilder.screen(screen: Screen, navController: NavController) { + when (screen) { + is ChildScreen -> composable(screen.identifier) { screen.content(navController) } + is ParentScreen -> { + val menuRoute = "${screen.identifier}_menu" + navigation(startDestination = menuRoute, route = screen.identifier) { + // The menu to navigate to one of the children screens. + composable(menuRoute) { ScreenMenu(screen, navController) } + + // The content of the child screens. + screen.children.forEach { (_, child) -> screen(child, navController) } + } + } + } +} + +@Composable +private fun ScreenMenu( + screen: ParentScreen, + navController: NavController, +) { + LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) { + screen.children.forEach { (name, child) -> + item { + Surface( + Modifier.fillMaxWidth(), + color = MaterialTheme.colorScheme.secondaryContainer, + shape = CircleShape, + ) { + Column( + Modifier.clickable { navController.navigate(child.identifier) } + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text(name) + } + } + } + } + } +} From 33470b902e25b7e4ae6c4ade4c2120b49bd5364d Mon Sep 17 00:00:00 2001 From: Jordan Demeulenaere Date: Mon, 25 Jul 2022 17:45:38 +0200 Subject: [PATCH 2/2] Force the app dark/light theme in the Gallery app (1/2) Before this CL, the Gallery app would use a boolean isDarkTheme that would be toggled and passed to the SystemUITheme to force set the dark/light theme of the app. However, this does not work for Views and the AndroidColors, for which we need to set the app dark/light theme using UiModeManager. Bug: 238993727 Test: Builds Change-Id: Ic3efb42d371c6aac8c2a6297a0cb192a780903cd --- .../compose/theme/AndroidColorScheme.kt | 2 +- packages/SystemUI/compose/gallery/Android.bp | 1 + .../compose/gallery/AndroidManifest.xml | 33 ++++++++++++++++ .../compose/gallery/app/AndroidManifest.xml | 4 +- .../gallery/res/values-night/themes.xml | 26 ------------- .../compose/gallery/res/values/themes.xml | 7 +++- .../compose/gallery/ConfigurationControls.kt | 30 ++++++++++----- .../compose/gallery/GalleryActivity.kt | 38 ++++++++++++++++--- .../systemui/compose/gallery/GalleryApp.kt | 6 +-- 9 files changed, 98 insertions(+), 49 deletions(-) delete mode 100644 packages/SystemUI/compose/gallery/res/values-night/themes.xml diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt index 4d5014cdbba77..b8639e64e0020 100644 --- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt +++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt @@ -37,7 +37,7 @@ val LocalAndroidColorScheme = * Important: Use M3 colors from MaterialTheme.colorScheme whenever possible instead. In the future, * most of the colors in this class will be removed in favor of their M3 counterpart. */ -class AndroidColorScheme internal constructor(private val context: Context) { +class AndroidColorScheme internal constructor(context: Context) { val colorPrimary = getColor(context, R.attr.colorPrimary) val colorPrimaryDark = getColor(context, R.attr.colorPrimaryDark) val colorAccent = getColor(context, R.attr.colorAccent) diff --git a/packages/SystemUI/compose/gallery/Android.bp b/packages/SystemUI/compose/gallery/Android.bp index bddc1e63fee8f..40504dc30c338 100644 --- a/packages/SystemUI/compose/gallery/Android.bp +++ b/packages/SystemUI/compose/gallery/Android.bp @@ -34,6 +34,7 @@ android_library { ], static_libs: [ + "SystemUI-core", "SystemUIComposeCore", "SystemUIComposeFeatures", diff --git a/packages/SystemUI/compose/gallery/AndroidManifest.xml b/packages/SystemUI/compose/gallery/AndroidManifest.xml index 4fcce0bf6968b..2f30651a6acf9 100644 --- a/packages/SystemUI/compose/gallery/AndroidManifest.xml +++ b/packages/SystemUI/compose/gallery/AndroidManifest.xml @@ -16,7 +16,40 @@ --> + + + + + + + + + diff --git a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml b/packages/SystemUI/compose/gallery/app/AndroidManifest.xml index e7d496c2cb355..1f3fd8c312d9e 100644 --- a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml +++ b/packages/SystemUI/compose/gallery/app/AndroidManifest.xml @@ -16,6 +16,7 @@ --> + android:theme="@style/Theme.SystemUI.Gallery" + tools:replace="android:icon,android:theme,android:label"> - - - - \ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/res/values/themes.xml b/packages/SystemUI/compose/gallery/res/values/themes.xml index 6e5e99832f094..45fa1f5dfb5ca 100644 --- a/packages/SystemUI/compose/gallery/res/values/themes.xml +++ b/packages/SystemUI/compose/gallery/res/values/themes.xml @@ -15,7 +15,10 @@ limitations under the License. --> - - \ No newline at end of file + diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt index 06bdad8a67351..990d060207df1 100644 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt +++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt @@ -9,11 +9,12 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.BrightnessHigh -import androidx.compose.material.icons.filled.BrightnessLow +import androidx.compose.material.icons.filled.DarkMode import androidx.compose.material.icons.filled.FormatSize import androidx.compose.material.icons.filled.FormatTextdirectionLToR import androidx.compose.material.icons.filled.FormatTextdirectionRToL +import androidx.compose.material.icons.filled.InvertColors +import androidx.compose.material.icons.filled.LightMode import androidx.compose.material.icons.filled.Smartphone import androidx.compose.material.icons.filled.Tablet import androidx.compose.material3.Button @@ -44,12 +45,13 @@ enum class FontScale(val scale: Float) { /** A configuration panel that allows to toggle the theme, font scale and layout direction. */ @Composable fun ConfigurationControls( - isDarkTheme: Boolean, + theme: Theme, fontScale: FontScale, layoutDirection: LayoutDirection, onChangeTheme: () -> Unit, onChangeLayoutDirection: () -> Unit, onChangeFontScale: () -> Unit, + modifier: Modifier = Modifier, ) { // The display we are emulating, if any. var emulatedDisplayName by rememberSaveable { mutableStateOf(null) } @@ -84,18 +86,26 @@ fun ConfigurationControls( // TODO(b/231131244): Fork FlowRow from Accompanist and use that instead to make sure that users // don't miss any available configuration. - LazyRow { + LazyRow(modifier) { // Dark/light theme. item { TextButton(onChangeTheme) { val text: String val icon: ImageVector - if (isDarkTheme) { - icon = Icons.Default.BrightnessHigh - text = "Dark" - } else { - icon = Icons.Default.BrightnessLow - text = "Light" + + when (theme) { + Theme.System -> { + icon = Icons.Default.InvertColors + text = "System" + } + Theme.Dark -> { + icon = Icons.Default.DarkMode + text = "Dark" + } + Theme.Light -> { + icon = Icons.Default.LightMode + text = "Light" + } } Icon(icon, null) diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt index d18b454bc78fb..bb2d2feba39fa 100644 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt +++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt @@ -16,6 +16,8 @@ package com.android.systemui.compose.gallery +import android.app.UiModeManager +import android.content.Context import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -23,7 +25,7 @@ import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.Color import androidx.core.view.WindowCompat @@ -33,22 +35,46 @@ class GalleryActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) + val uiModeManager = getSystemService(Context.UI_MODE_SERVICE) as UiModeManager setContent { - val isSystemInDarkTheme = isSystemInDarkTheme() - var isDarkTheme by remember { mutableStateOf(isSystemInDarkTheme) } - val onChangeTheme = { isDarkTheme = !isDarkTheme } + var theme by rememberSaveable { mutableStateOf(Theme.System) } + val onChangeTheme = { + // Change to the next theme for a toggle behavior. + theme = + when (theme) { + Theme.System -> Theme.Dark + Theme.Dark -> Theme.Light + Theme.Light -> Theme.System + } + } + val isSystemInDarkTheme = isSystemInDarkTheme() + val isDark = theme == Theme.Dark || (theme == Theme.System && isSystemInDarkTheme) + val useDarkIcons = !isDark val systemUiController = rememberSystemUiController() - val useDarkIcons = !isDarkTheme SideEffect { systemUiController.setSystemBarsColor( color = Color.Transparent, darkIcons = useDarkIcons, ) + + uiModeManager.setApplicationNightMode( + when (theme) { + Theme.System -> UiModeManager.MODE_NIGHT_AUTO + Theme.Dark -> UiModeManager.MODE_NIGHT_YES + Theme.Light -> UiModeManager.MODE_NIGHT_NO + } + ) } - GalleryApp(isDarkTheme, onChangeTheme) + GalleryApp(theme, onChangeTheme) } } } + +enum class Theme { + System, + Dark, + Light, +} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt index 23badd0b44ba3..c341867bfb591 100644 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt +++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt @@ -65,7 +65,7 @@ private fun MainContent() { */ @Composable fun GalleryApp( - isDarkTheme: Boolean, + theme: Theme, onChangeTheme: () -> Unit, ) { val systemFontScale = LocalDensity.current.fontScale @@ -100,14 +100,14 @@ fun GalleryApp( LocalDensity provides density, LocalLayoutDirection provides layoutDirection, ) { - SystemUITheme(isDarkTheme) { + SystemUITheme { Surface( Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background, ) { Column(Modifier.fillMaxSize().systemBarsPadding().padding(16.dp)) { ConfigurationControls( - isDarkTheme, + theme, fontScale, layoutDirection, onChangeTheme,