Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 78b3e889 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

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
parent a3b7bb18
Loading
Loading
Loading
Loading
+74 −0
Original line number Diff line number Diff line
/*
 * 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<AndroidColorScheme> {
        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)
    }
}
+9 −1
Original line number Diff line number Diff line
@@ -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()
        }
    }
}
+19 −0
Original line number Diff line number Diff line
@@ -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)
            }
        }
    }
}
+79 −28
Original line number Diff line number Diff line
@@ -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<Pair<String, Color>>,
) {
    LazyColumn(
        Modifier.fillMaxWidth(),
    ) {
        colors.forEach { (name, color) -> item { ColorTile(color, name) } }
    }
}

+4 −2
Original line number Diff line number Diff line
@@ -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() }
        }
    }