From 78b3e889ff0a3570fc32838426052f9704d17688 Mon Sep 17 00:00:00 2001 From: Jordan Demeulenaere Date: Mon, 13 Jun 2022 12:09:47 +0200 Subject: [PATCH] Introduce LocalAndroidColorScheme to expose Android colors This CL introduces the AndroidColorScheme and its CompositionLocal to access any Android color from a Composable. The list of colors was taken from [1]. Ideally we would only use M3 colors but given that M3 and Android colors have not been aligned yet, our current UI specs are still using the Android colors. In the long term, this class will be deleted or slimmed down to contain only the few colors missing from M3. [1] http://cs/android-internal/frameworks/base/core/res/res/values/themes_device_defaults.xml;l=214;rcl=fa86dc4ca511d27ae2d32749d180466404c18218 Bug: 230605885 Test: atest SystemUIThemeTest Change-Id: I45927f23eb8256987b4e5c6ae48e33ce581aabb3 --- .../compose/theme/AndroidColorScheme.kt | 74 ++++++++++++ .../systemui/compose/theme/SystemUITheme.kt | 10 +- .../compose/theme/SystemUIThemeTest.kt | 19 ++++ .../systemui/compose/gallery/ColorsScreen.kt | 107 +++++++++++++----- .../systemui/compose/gallery/GalleryApp.kt | 6 +- 5 files changed, 185 insertions(+), 31 deletions(-) create mode 100644 packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt 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 new file mode 100644 index 0000000000000..4d5014cdbba77 --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt @@ -0,0 +1,74 @@ +/* + * 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.theme + +import android.annotation.ColorInt +import android.content.Context +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.Color +import com.android.internal.R + +/** CompositionLocal used to pass [AndroidColorScheme] down the tree. */ +val LocalAndroidColorScheme = + staticCompositionLocalOf { + throw IllegalStateException( + "No AndroidColorScheme configured. Make sure to use LocalAndroidColorScheme in a " + + "Composable surrounded by a SystemUITheme {}." + ) + } + +/** + * The Android color scheme. + * + * 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) { + val colorPrimary = getColor(context, R.attr.colorPrimary) + val colorPrimaryDark = getColor(context, R.attr.colorPrimaryDark) + val colorAccent = getColor(context, R.attr.colorAccent) + val colorAccentPrimary = getColor(context, R.attr.colorAccentPrimary) + val colorAccentSecondary = getColor(context, R.attr.colorAccentSecondary) + val colorAccentTertiary = getColor(context, R.attr.colorAccentTertiary) + val colorAccentPrimaryVariant = getColor(context, R.attr.colorAccentPrimaryVariant) + val colorAccentSecondaryVariant = getColor(context, R.attr.colorAccentSecondaryVariant) + val colorAccentTertiaryVariant = getColor(context, R.attr.colorAccentTertiaryVariant) + val colorSurface = getColor(context, R.attr.colorSurface) + val colorSurfaceHighlight = getColor(context, R.attr.colorSurfaceHighlight) + val colorSurfaceVariant = getColor(context, R.attr.colorSurfaceVariant) + val colorSurfaceHeader = getColor(context, R.attr.colorSurfaceHeader) + val colorError = getColor(context, R.attr.colorError) + val colorBackground = getColor(context, R.attr.colorBackground) + val colorBackgroundFloating = getColor(context, R.attr.colorBackgroundFloating) + val panelColorBackground = getColor(context, R.attr.panelColorBackground) + val textColorPrimary = getColor(context, R.attr.textColorPrimary) + val textColorSecondary = getColor(context, R.attr.textColorSecondary) + val textColorTertiary = getColor(context, R.attr.textColorTertiary) + val textColorPrimaryInverse = getColor(context, R.attr.textColorPrimaryInverse) + val textColorSecondaryInverse = getColor(context, R.attr.textColorSecondaryInverse) + val textColorTertiaryInverse = getColor(context, R.attr.textColorTertiaryInverse) + val textColorOnAccent = getColor(context, R.attr.textColorOnAccent) + val colorForeground = getColor(context, R.attr.colorForeground) + val colorForegroundInverse = getColor(context, R.attr.colorForegroundInverse) + + private fun getColor(context: Context, attr: Int): Color { + val ta = context.obtainStyledAttributes(intArrayOf(attr)) + @ColorInt val color = ta.getColor(0, 0) + ta.recycle() + return Color(color) + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/SystemUITheme.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/SystemUITheme.kt index de53c05c7f675..79e3d3d475a89 100644 --- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/SystemUITheme.kt +++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/SystemUITheme.kt @@ -22,6 +22,7 @@ import androidx.compose.material3.Typography import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalContext /** The Material 3 theme that should wrap all SystemUI Composables. */ @@ -39,7 +40,14 @@ fun SystemUITheme( } else { dynamicLightColorScheme(context) } + val androidColorScheme = AndroidColorScheme(context) val typography = Typography() - MaterialTheme(colorScheme, typography = typography) { content() } + MaterialTheme(colorScheme, typography = typography) { + CompositionLocalProvider( + LocalAndroidColorScheme provides androidColorScheme, + ) { + content() + } + } } diff --git a/packages/SystemUI/compose/core/tests/src/com/android/systemui/compose/theme/SystemUIThemeTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/systemui/compose/theme/SystemUIThemeTest.kt index 155b66c071100..20249f66d0d0b 100644 --- a/packages/SystemUI/compose/core/tests/src/com/android/systemui/compose/theme/SystemUIThemeTest.kt +++ b/packages/SystemUI/compose/core/tests/src/com/android/systemui/compose/theme/SystemUIThemeTest.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert.assertThrows import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -35,4 +36,22 @@ class SystemUIThemeTest { composeRule.onNodeWithText("foo").assertIsDisplayed() } + + @Test + fun testAndroidColorsAreAvailableInsideTheme() { + composeRule.setContent { + SystemUITheme { Text("foo", color = LocalAndroidColorScheme.current.colorAccent) } + } + + composeRule.onNodeWithText("foo").assertIsDisplayed() + } + + @Test + fun testAccessingAndroidColorsWithoutThemeThrows() { + assertThrows(IllegalStateException::class.java) { + composeRule.setContent { + Text("foo", color = LocalAndroidColorScheme.current.colorAccent) + } + } + } } diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt index b616753e423f9..dfa1b26f464ef 100644 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt +++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt @@ -20,6 +20,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -32,38 +33,88 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import com.android.systemui.compose.theme.LocalAndroidColorScheme /** The screen that shows all the Material 3 colors. */ @Composable -fun ColorsScreen() { +fun MaterialColorsScreen() { val colors = MaterialTheme.colorScheme - LazyColumn { - item { ColorTile(colors.primary, "primary") } - item { ColorTile(colors.onPrimary, "onPrimary") } - item { ColorTile(colors.primaryContainer, "primaryContainer") } - item { ColorTile(colors.onPrimaryContainer, "onPrimaryContainer") } - item { ColorTile(colors.inversePrimary, "inversePrimary") } - item { ColorTile(colors.secondary, "secondary") } - item { ColorTile(colors.onSecondary, "onSecondary") } - item { ColorTile(colors.secondaryContainer, "secondaryContainer") } - item { ColorTile(colors.onSecondaryContainer, "onSecondaryContainer") } - item { ColorTile(colors.tertiary, "tertiary") } - item { ColorTile(colors.onTertiary, "onTertiary") } - item { ColorTile(colors.tertiaryContainer, "tertiaryContainer") } - item { ColorTile(colors.onTertiaryContainer, "onTertiaryContainer") } - item { ColorTile(colors.background, "background") } - item { ColorTile(colors.onBackground, "onBackground") } - item { ColorTile(colors.surface, "surface") } - item { ColorTile(colors.onSurface, "onSurface") } - item { ColorTile(colors.surfaceVariant, "surfaceVariant") } - item { ColorTile(colors.onSurfaceVariant, "onSurfaceVariant") } - item { ColorTile(colors.inverseSurface, "inverseSurface") } - item { ColorTile(colors.inverseOnSurface, "inverseOnSurface") } - item { ColorTile(colors.error, "error") } - item { ColorTile(colors.onError, "onError") } - item { ColorTile(colors.errorContainer, "errorContainer") } - item { ColorTile(colors.onErrorContainer, "onErrorContainer") } - item { ColorTile(colors.outline, "outline") } + ColorsScreen( + listOf( + "primary" to colors.primary, + "onPrimary" to colors.onPrimary, + "primaryContainer" to colors.primaryContainer, + "onPrimaryContainer" to colors.onPrimaryContainer, + "inversePrimary" to colors.inversePrimary, + "secondary" to colors.secondary, + "onSecondary" to colors.onSecondary, + "secondaryContainer" to colors.secondaryContainer, + "onSecondaryContainer" to colors.onSecondaryContainer, + "tertiary" to colors.tertiary, + "onTertiary" to colors.onTertiary, + "tertiaryContainer" to colors.tertiaryContainer, + "onTertiaryContainer" to colors.onTertiaryContainer, + "background" to colors.background, + "onBackground" to colors.onBackground, + "surface" to colors.surface, + "onSurface" to colors.onSurface, + "surfaceVariant" to colors.surfaceVariant, + "onSurfaceVariant" to colors.onSurfaceVariant, + "inverseSurface" to colors.inverseSurface, + "inverseOnSurface" to colors.inverseOnSurface, + "error" to colors.error, + "onError" to colors.onError, + "errorContainer" to colors.errorContainer, + "onErrorContainer" to colors.onErrorContainer, + "outline" to colors.outline, + ) + ) +} + +/** The screen that shows all the Android colors. */ +@Composable +fun AndroidColorsScreen() { + val colors = LocalAndroidColorScheme.current + ColorsScreen( + listOf( + "colorPrimary" to colors.colorPrimary, + "colorPrimaryDark" to colors.colorPrimaryDark, + "colorAccent" to colors.colorAccent, + "colorAccentPrimary" to colors.colorAccentPrimary, + "colorAccentSecondary" to colors.colorAccentSecondary, + "colorAccentTertiary" to colors.colorAccentTertiary, + "colorAccentPrimaryVariant" to colors.colorAccentPrimaryVariant, + "colorAccentSecondaryVariant" to colors.colorAccentSecondaryVariant, + "colorAccentTertiaryVariant" to colors.colorAccentTertiaryVariant, + "colorSurface" to colors.colorSurface, + "colorSurfaceHighlight" to colors.colorSurfaceHighlight, + "colorSurfaceVariant" to colors.colorSurfaceVariant, + "colorSurfaceHeader" to colors.colorSurfaceHeader, + "colorError" to colors.colorError, + "colorBackground" to colors.colorBackground, + "colorBackgroundFloating" to colors.colorBackgroundFloating, + "panelColorBackground" to colors.panelColorBackground, + "textColorPrimary" to colors.textColorPrimary, + "textColorSecondary" to colors.textColorSecondary, + "textColorTertiary" to colors.textColorTertiary, + "textColorPrimaryInverse" to colors.textColorPrimaryInverse, + "textColorSecondaryInverse" to colors.textColorSecondaryInverse, + "textColorTertiaryInverse" to colors.textColorTertiaryInverse, + "textColorOnAccent" to colors.textColorOnAccent, + "colorForeground" to colors.colorForeground, + "colorForegroundInverse" to colors.colorForegroundInverse, + ) + ) +} + +@Composable +private fun ColorsScreen( + colors: List>, +) { + LazyColumn( + Modifier.fillMaxWidth(), + ) { + colors.forEach { (name, color) -> item { ColorTile(color, name) } } } } 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 c8146b8ccc9ce..0ac3a63aa9972 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 @@ -30,7 +30,8 @@ import com.android.systemui.compose.theme.SystemUITheme enum class Screen { Home, Typography, - Colors, + MaterialColors, + AndroidColors, ExampleFeature, } @@ -49,7 +50,8 @@ private fun MainContent() { ) } composable(Screen.Typography.name) { TypographyScreen() } - composable(Screen.Colors.name) { ColorsScreen() } + composable(Screen.MaterialColors.name) { MaterialColorsScreen() } + composable(Screen.AndroidColors.name) { AndroidColorsScreen() } composable(Screen.ExampleFeature.name) { ExampleFeatureScreen() } } }