Merge changes from topic "gallery-dark"
* changes: Force the app dark/light theme in the Gallery app (1/2) Represent the Gallery screen hierarchy as a tree
This commit is contained in:
committed by
Android (Google) Code Review
commit
7c5ee6c6de
@@ -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)
|
||||
|
||||
@@ -34,6 +34,7 @@ android_library {
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"SystemUI-core",
|
||||
"SystemUIComposeCore",
|
||||
"SystemUIComposeFeatures",
|
||||
|
||||
|
||||
@@ -16,7 +16,40 @@
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.android.systemui.compose.gallery">
|
||||
<!-- To emulate a display size and density. -->
|
||||
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
|
||||
|
||||
<application
|
||||
android:name="android.app.Application"
|
||||
android:appComponentFactory="androidx.core.app.AppComponentFactory"
|
||||
tools:replace="android:name,android:appComponentFactory">
|
||||
<!-- Disable providers from SystemUI -->
|
||||
<provider android:name="com.android.systemui.keyguard.KeyguardSliceProvider"
|
||||
android:authorities="com.android.systemui.test.keyguard.disabled"
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities"
|
||||
tools:node="remove" />
|
||||
<provider android:name="com.google.android.systemui.keyguard.KeyguardSliceProviderGoogle"
|
||||
android:authorities="com.android.systemui.test.keyguard.disabled"
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities"
|
||||
tools:node="remove" />
|
||||
<provider android:name="com.android.keyguard.clock.ClockOptionsProvider"
|
||||
android:authorities="com.android.systemui.test.keyguard.clock.disabled"
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities"
|
||||
tools:node="remove" />
|
||||
<provider android:name="com.android.systemui.people.PeopleProvider"
|
||||
android:authorities="com.android.systemui.test.people.disabled"
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities"
|
||||
tools:node="remove" />
|
||||
<provider android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.android.systemui.test.fileprovider.disabled"
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities"
|
||||
tools:node="remove"/>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.android.systemui.compose.gallery.app">
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -23,7 +24,8 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.SystemUIGallery">
|
||||
android:theme="@style/Theme.SystemUI.Gallery"
|
||||
tools:replace="android:icon,android:theme,android:label">
|
||||
<activity
|
||||
android:name="com.android.systemui.compose.gallery.GalleryActivity"
|
||||
android:exported="true"
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="Theme.SystemUIGallery" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
<item name="android:statusBarColor" tools:targetApi="l">
|
||||
@android:color/transparent
|
||||
</item>
|
||||
<item name="android:navigationBarColor" tools:targetApi="l">
|
||||
@android:color/transparent
|
||||
</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -15,7 +15,10 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="Theme.SystemUIGallery" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
<style name="Theme.SystemUI.Gallery">
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
|
||||
<item name="android:statusBarColor" tools:targetApi="l">
|
||||
@android:color/transparent
|
||||
</item>
|
||||
@@ -24,4 +27,4 @@
|
||||
</item>
|
||||
<item name="android:windowLightStatusBar">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -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<String?>(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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,7 +65,7 @@ private fun MainContent() {
|
||||
*/
|
||||
@Composable
|
||||
fun GalleryApp(
|
||||
isDarkTheme: Boolean,
|
||||
theme: Theme,
|
||||
onChangeTheme: () -> Unit,
|
||||
) {
|
||||
val systemFontScale = LocalDensity.current.fontScale
|
||||
@@ -98,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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String, Screen>,
|
||||
) : 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user