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

Commit 44b299fe authored by Marcelo Arteiro's avatar Marcelo Arteiro Committed by Android (Google) Code Review
Browse files

Merge "Aligning Android color tokens with Material" into main

parents d7734ae2 b55f71a3
Loading
Loading
Loading
Loading
+18 −12
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ public class ColorScheme {
    private final TonalPalette mAccent3;
    private final TonalPalette mNeutral1;
    private final TonalPalette mNeutral2;
    private final TonalPalette mError;
    private final Hct mProposedSeedHct;


@@ -96,6 +97,7 @@ public class ColorScheme {
        mAccent3 = new TonalPalette(mMaterialScheme.tertiaryPalette);
        mNeutral1 = new TonalPalette(mMaterialScheme.neutralPalette);
        mNeutral2 = new TonalPalette(mMaterialScheme.neutralVariantPalette);
        mError = new TonalPalette(mMaterialScheme.errorPalette);
    }

    public ColorScheme(@ColorInt int seed, boolean darkTheme) {
@@ -162,6 +164,10 @@ public class ColorScheme {
        return mNeutral2;
    }

    public TonalPalette getError() {
        return mError;
    }

    @Override
    public String toString() {
        return "ColorScheme {\n"
+28 −0
Original line number Diff line number Diff line
@@ -22,11 +22,39 @@ import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;
import com.google.ux.material.libmonet.dynamiccolor.ToneDeltaPair;
import com.google.ux.material.libmonet.dynamiccolor.TonePolarity;

import java.util.function.Supplier;

class CustomDynamicColors {
    private final MaterialDynamicColors mMdc;
    public final Supplier<DynamicColor>[] allColors;

    CustomDynamicColors(boolean isExtendedFidelity) {
        this.mMdc = new MaterialDynamicColors(isExtendedFidelity);

        allColors = new Supplier[]{
                this::widgetBackground,
                this::clockHour,
                this::clockMinute,
                this::clockSecond,
                this::weatherTemp,
                this::themeApp,
                this::onThemeApp,
                this::themeAppRing,
                this::themeNotif,
                this::brandA,
                this::brandB,
                this::brandC,
                this::brandD,
                this::underSurface,
                this::shadeActive,
                this::onShadeActive,
                this::onShadeActiveVariant,
                this::shadeInactive,
                this::onShadeInactive,
                this::onShadeInactiveVariant,
                this::shadeDisabled,
                this::overviewBackground
        };
    }

    // CLOCK COLORS
+96 −84
Original line number Diff line number Diff line
@@ -22,7 +22,9 @@ import com.google.ux.material.libmonet.dynamiccolor.DynamicColor;
import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Supplier;

public class DynamicColors {

@@ -35,55 +37,60 @@ public class DynamicColors {
    public static List<Pair<String, DynamicColor>> getAllDynamicColorsMapped(
            boolean isExtendedFidelity) {
        MaterialDynamicColors mdc = new MaterialDynamicColors(isExtendedFidelity);
        List<Pair<String, DynamicColor>> list = new ArrayList<>();
        list.add(Pair.create("primary_container", mdc.primaryContainer()));
        list.add(Pair.create("on_primary_container", mdc.onPrimaryContainer()));
        list.add(Pair.create("primary", mdc.primary()));
        list.add(Pair.create("on_primary", mdc.onPrimary()));
        list.add(Pair.create("secondary_container", mdc.secondaryContainer()));
        list.add(Pair.create("on_secondary_container", mdc.onSecondaryContainer()));
        list.add(Pair.create("secondary", mdc.secondary()));
        list.add(Pair.create("on_secondary", mdc.onSecondary()));
        list.add(Pair.create("tertiary_container", mdc.tertiaryContainer()));
        list.add(Pair.create("on_tertiary_container", mdc.onTertiaryContainer()));
        list.add(Pair.create("tertiary", mdc.tertiary()));
        list.add(Pair.create("on_tertiary", mdc.onTertiary()));
        list.add(Pair.create("background", mdc.background()));
        list.add(Pair.create("on_background", mdc.onBackground()));
        list.add(Pair.create("surface", mdc.surface()));
        list.add(Pair.create("on_surface", mdc.onSurface()));
        list.add(Pair.create("surface_container_low", mdc.surfaceContainerLow()));
        list.add(Pair.create("surface_container_lowest", mdc.surfaceContainerLowest()));
        list.add(Pair.create("surface_container", mdc.surfaceContainer()));
        list.add(Pair.create("surface_container_high", mdc.surfaceContainerHigh()));
        list.add(Pair.create("surface_container_highest", mdc.surfaceContainerHighest()));
        list.add(Pair.create("surface_bright", mdc.surfaceBright()));
        list.add(Pair.create("surface_dim", mdc.surfaceDim()));
        list.add(Pair.create("surface_variant", mdc.surfaceVariant()));
        list.add(Pair.create("on_surface_variant", mdc.onSurfaceVariant()));
        list.add(Pair.create("outline", mdc.outline()));
        list.add(Pair.create("outline_variant", mdc.outlineVariant()));
        list.add(Pair.create("error", mdc.error()));
        list.add(Pair.create("on_error", mdc.onError()));
        list.add(Pair.create("error_container", mdc.errorContainer()));
        list.add(Pair.create("on_error_container", mdc.onErrorContainer()));
        list.add(Pair.create("control_activated", mdc.controlActivated()));
        list.add(Pair.create("control_normal", mdc.controlNormal()));
        list.add(Pair.create("control_highlight", mdc.controlHighlight()));
        list.add(Pair.create("text_primary_inverse", mdc.textPrimaryInverse()));
        list.add(Pair.create("text_secondary_and_tertiary_inverse",
                mdc.textSecondaryAndTertiaryInverse()));
        list.add(Pair.create("text_primary_inverse_disable_only",
                mdc.textPrimaryInverseDisableOnly()));
        list.add(Pair.create("text_secondary_and_tertiary_inverse_disabled",
                mdc.textSecondaryAndTertiaryInverseDisabled()));
        list.add(Pair.create("text_hint_inverse", mdc.textHintInverse()));
        list.add(Pair.create("palette_key_color_primary", mdc.primaryPaletteKeyColor()));
        list.add(Pair.create("palette_key_color_secondary", mdc.secondaryPaletteKeyColor()));
        list.add(Pair.create("palette_key_color_tertiary", mdc.tertiaryPaletteKeyColor()));
        list.add(Pair.create("palette_key_color_neutral", mdc.neutralPaletteKeyColor()));
        list.add(Pair.create("palette_key_color_neutral_variant",
                mdc.neutralVariantPaletteKeyColor()));
        final Supplier<DynamicColor>[] allColors = new Supplier[]{
                mdc::primaryPaletteKeyColor,
                mdc::secondaryPaletteKeyColor,
                mdc::tertiaryPaletteKeyColor,
                mdc::neutralPaletteKeyColor,
                mdc::neutralVariantPaletteKeyColor,
                mdc::background,
                mdc::onBackground,
                mdc::surface,
                mdc::surfaceDim,
                mdc::surfaceBright,
                mdc::surfaceContainerLowest,
                mdc::surfaceContainerLow,
                mdc::surfaceContainer,
                mdc::surfaceContainerHigh,
                mdc::surfaceContainerHighest,
                mdc::onSurface,
                mdc::surfaceVariant,
                mdc::onSurfaceVariant,
                mdc::inverseSurface,
                mdc::inverseOnSurface,
                mdc::outline,
                mdc::outlineVariant,
                mdc::shadow,
                mdc::scrim,
                mdc::surfaceTint,
                mdc::primary,
                mdc::onPrimary,
                mdc::primaryContainer,
                mdc::onPrimaryContainer,
                mdc::inversePrimary,
                mdc::secondary,
                mdc::onSecondary,
                mdc::secondaryContainer,
                mdc::onSecondaryContainer,
                mdc::tertiary,
                mdc::onTertiary,
                mdc::tertiaryContainer,
                mdc::onTertiaryContainer,
                mdc::error,
                mdc::onError,
                mdc::errorContainer,
                mdc::onErrorContainer,
                mdc::controlActivated,
                mdc::controlNormal,
                mdc::controlHighlight,
                mdc::textPrimaryInverse,
                mdc::textSecondaryAndTertiaryInverse,
                mdc::textPrimaryInverseDisableOnly,
                mdc::textSecondaryAndTertiaryInverseDisabled,
                mdc::textHintInverse
        };

        List<Pair<String, DynamicColor>> list = generateSysUINames(allColors);
        return list;
    }

@@ -96,19 +103,23 @@ public class DynamicColors {
    public static List<Pair<String, DynamicColor>> getFixedColorsMapped(
            boolean isExtendedFidelity) {
        MaterialDynamicColors mdc = new MaterialDynamicColors(isExtendedFidelity);
        List<Pair<String, DynamicColor>> list = new ArrayList<>();
        list.add(Pair.create("primary_fixed", mdc.primaryFixed()));
        list.add(Pair.create("primary_fixed_dim", mdc.primaryFixedDim()));
        list.add(Pair.create("on_primary_fixed", mdc.onPrimaryFixed()));
        list.add(Pair.create("on_primary_fixed_variant", mdc.onPrimaryFixedVariant()));
        list.add(Pair.create("secondary_fixed", mdc.secondaryFixed()));
        list.add(Pair.create("secondary_fixed_dim", mdc.secondaryFixedDim()));
        list.add(Pair.create("on_secondary_fixed", mdc.onSecondaryFixed()));
        list.add(Pair.create("on_secondary_fixed_variant", mdc.onSecondaryFixedVariant()));
        list.add(Pair.create("tertiary_fixed", mdc.tertiaryFixed()));
        list.add(Pair.create("tertiary_fixed_dim", mdc.tertiaryFixedDim()));
        list.add(Pair.create("on_tertiary_fixed", mdc.onTertiaryFixed()));
        list.add(Pair.create("on_tertiary_fixed_variant", mdc.onTertiaryFixedVariant()));

        final Supplier<DynamicColor>[] allColors = new Supplier[]{
                mdc::primaryFixed,
                mdc::primaryFixedDim,
                mdc::onPrimaryFixed,
                mdc::onPrimaryFixedVariant,
                mdc::secondaryFixed,
                mdc::secondaryFixedDim,
                mdc::onSecondaryFixed,
                mdc::onSecondaryFixedVariant,
                mdc::tertiaryFixed,
                mdc::tertiaryFixedDim,
                mdc::onTertiaryFixed,
                mdc::onTertiaryFixedVariant
        };

        List<Pair<String, DynamicColor>> list = generateSysUINames(allColors);
        return list;
    }

@@ -122,29 +133,30 @@ public class DynamicColors {
    public static List<Pair<String, DynamicColor>> getCustomColorsMapped(
            boolean isExtendedFidelity) {
        CustomDynamicColors customMdc = new CustomDynamicColors(isExtendedFidelity);
        List<Pair<String, DynamicColor>> list = new ArrayList<>();
        list.add(Pair.create("widget_background", customMdc.widgetBackground()));
        list.add(Pair.create("clock_hour", customMdc.clockHour()));
        list.add(Pair.create("clock_minute", customMdc.clockMinute()));
        list.add(Pair.create("clock_second", customMdc.weatherTemp()));
        list.add(Pair.create("theme_app", customMdc.themeApp()));
        list.add(Pair.create("on_theme_app", customMdc.onThemeApp()));
        list.add(Pair.create("theme_app_ring", customMdc.themeAppRing()));
        list.add(Pair.create("theme_notif", customMdc.themeNotif()));
        list.add(Pair.create("brand_a", customMdc.brandA()));
        list.add(Pair.create("brand_b", customMdc.brandB()));
        list.add(Pair.create("brand_c", customMdc.brandC()));
        list.add(Pair.create("brand_d", customMdc.brandD()));
        list.add(Pair.create("under_surface", customMdc.underSurface()));
        list.add(Pair.create("shade_active", customMdc.shadeActive()));
        list.add(Pair.create("on_shade_active", customMdc.onShadeActive()));
        list.add(Pair.create("on_shade_active_variant", customMdc.onShadeActiveVariant()));
        list.add(Pair.create("shade_inactive", customMdc.shadeInactive()));
        list.add(Pair.create("on_shade_inactive", customMdc.onShadeInactive()));
        list.add(Pair.create("on_shade_inactive_variant", customMdc.onShadeInactiveVariant()));
        list.add(Pair.create("shade_disabled", customMdc.shadeDisabled()));
        list.add(Pair.create("overview_background", customMdc.overviewBackground()));
        List<Pair<String, DynamicColor>> list = generateSysUINames(customMdc.allColors);
        return list;
    }

    private static List<Pair<String, DynamicColor>> generateSysUINames(
            Supplier<DynamicColor>[] allColors) {
        List<Pair<String, DynamicColor>> list = new ArrayList<>();

        for (Supplier<DynamicColor> supplier : allColors) {
            DynamicColor dynamicColor = supplier.get();
            String name = dynamicColor.name;

            // Fix tokens containing `palette_key_color` for SysUI requirements:
            // In SysUI palette_key_color should come first in the token name;
            String paletteMark = "palette_key_color";
            if (name.contains("_" + paletteMark)) {
                name = paletteMark + "_" + name.replace("_" + paletteMark, "");
            }

            list.add(new Pair(name, dynamicColor));
        }

        list.sort(Comparator.comparing(pair -> pair.first));
        return list;
    }
}
+166 −14
Original line number Diff line number Diff line
@@ -38,6 +38,10 @@ import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.Node

private const val CONTRAST = 0.0

private const val IS_FIDELITY_ENABLED = false

private const val fileHeader =
    """
  ~ Copyright (C) 2022 The Android Open Source Project
@@ -116,7 +120,8 @@ class ColorSchemeTest {
                                colorScheme.accent2,
                                colorScheme.accent3,
                                colorScheme.neutral1,
                                colorScheme.neutral2
                                colorScheme.neutral2,
                                colorScheme.error,
                            )
                            .flatMap { a -> listOf(*a.allShades.toTypedArray()) }
                            .joinToString(",", transform = Int::toRGBHex)
@@ -128,7 +133,7 @@ class ColorSchemeTest {
            hue += 60
        }

        saveFile(document, "current_themes.xml")
        saveFile(document, "themes.xml")
    }

    @Test
@@ -145,11 +150,14 @@ class ColorSchemeTest {
                Triple("accent2", "Secondary", colorScheme.accent2),
                Triple("accent3", "Tertiary", colorScheme.accent3),
                Triple("neutral1", "Neutral", colorScheme.neutral1),
                Triple("neutral2", "Secondary Neutral", colorScheme.neutral2)
                Triple("neutral2", "Secondary Neutral", colorScheme.neutral2),
                Triple("error", "Error", colorScheme.error),
            )
            .forEach {
                val (paletteName, readable, palette) = it
                palette.allShadesMapped.entries.forEachIndexed { index, (shade, colorValue) ->
                palette.allShadesMapped.toSortedMap().entries.forEachIndexed {
                    index,
                    (shade, colorValue) ->
                    val comment =
                        when (index) {
                            0 -> commentWhite(readable)
@@ -165,22 +173,145 @@ class ColorSchemeTest {
        // dynamic colors
        arrayOf(false, true).forEach { isDark ->
            val suffix = if (isDark) "_dark" else "_light"
            val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), isDark, 0.5)
            DynamicColors.getAllDynamicColorsMapped(false).forEach {
            val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), isDark, CONTRAST)
            DynamicColors.getAllDynamicColorsMapped(IS_FIDELITY_ENABLED).forEach {
                resources.createColorEntry(
                    "system_${it.first}$suffix",
                    it.second.getArgb(dynamicScheme)
                    it.second.getArgb(dynamicScheme),
                )
            }
        }

        // fixed colors
        val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), false, 0.5)
        DynamicColors.getFixedColorsMapped(false).forEach {
        val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), false, CONTRAST)
        DynamicColors.getFixedColorsMapped(IS_FIDELITY_ENABLED).forEach {
            resources.createColorEntry("system_${it.first}", it.second.getArgb(dynamicScheme))
        }

        saveFile(document, "role_values.xml")
        // custom colors
        arrayOf(false, true).forEach { isDark ->
            val suffix = if (isDark) "_dark" else "_light"
            val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), isDark, CONTRAST)
            DynamicColors.getCustomColorsMapped(IS_FIDELITY_ENABLED).forEach {
                resources.createColorEntry(
                    "system_${it.first}$suffix",
                    it.second.getArgb(dynamicScheme),
                )
            }
        }

        saveFile(document, "colors.xml")
    }

    @Test
    fun generateSymbols() {
        val document = buildDoc<Any>()

        val resources = document.createElement("resources")
        document.appendWithBreak(resources)

        (DynamicColors.getAllDynamicColorsMapped(IS_FIDELITY_ENABLED) +
                DynamicColors.getFixedColorsMapped(IS_FIDELITY_ENABLED))
            .forEach {
                val newName = ("material_color_" + it.first).snakeToLowerCamelCase()

                resources.createEntry(
                    "java-symbol",
                    arrayOf(Pair("name", newName), Pair("type", "color")),
                    null,
                )
            }

        DynamicColors.getCustomColorsMapped(IS_FIDELITY_ENABLED).forEach {
            val newName = ("custom_color_" + it.first).snakeToLowerCamelCase()

            resources.createEntry(
                "java-symbol",
                arrayOf(Pair("name", newName), Pair("type", "color")),
                null,
            )
        }

        arrayOf("_light", "_dark").forEach { suffix ->
            DynamicColors.getCustomColorsMapped(IS_FIDELITY_ENABLED).forEach {
                val newName = "system_" + it.first + suffix

                resources.createEntry(
                    "java-symbol",
                    arrayOf(Pair("name", newName), Pair("type", "color")),
                    null,
                )
            }
        }

        saveFile(document, "symbols.xml")
    }

    @Test
    fun generateDynamicColors() {
        arrayOf(false, true).forEach { isDark ->
            val document = buildDoc<Any>()

            val resources = document.createElement("resources")
            document.appendWithBreak(resources)

            (DynamicColors.getAllDynamicColorsMapped(IS_FIDELITY_ENABLED) +
                    DynamicColors.getFixedColorsMapped(IS_FIDELITY_ENABLED))
                .forEach {
                    val newName = ("material_color_" + it.first).snakeToLowerCamelCase()

                    val suffix = if (isDark) "_dark" else "_light"
                    val colorValue =
                        "@color/system_" + it.first + if (it.first.contains("fixed")) "" else suffix

                    resources.createColorEntry(newName, colorValue)
                }

            val suffix = if (isDark) "_dark" else "_light"

            DynamicColors.getCustomColorsMapped(IS_FIDELITY_ENABLED).forEach {
                val newName = ("custom_color_" + it.first).snakeToLowerCamelCase()
                resources.createColorEntry(newName, "@color/system_" + it.first + suffix)
            }

            saveFile(document, "colors_dynamic_$suffix.xml")
        }
    }

    @Test
    fun generatePublic() {
        val document = buildDoc<Any>()

        val resources = document.createElement("resources")

        val group = document.createElement("staging-public-group")
        resources.appendChild(group)

        document.appendWithBreak(resources)

        val context = InstrumentationRegistry.getInstrumentation().targetContext
        val res = context.resources

        val rClass = com.android.internal.R.color::class.java
        val existingFields = rClass.declaredFields.map { it.name }.toSet()

        arrayOf("_light", "_dark").forEach { suffix ->
            DynamicColors.getAllDynamicColorsMapped(IS_FIDELITY_ENABLED).forEach {
                val name = "system_" + it.first + suffix
                if (!existingFields.contains(name)) {
                    group.createEntry("public", arrayOf(Pair("name", name)), null)
                }
            }
        }

        DynamicColors.getFixedColorsMapped(IS_FIDELITY_ENABLED).forEach {
            val name = "system_${it.first}"
            if (!existingFields.contains(name)) {
                group.createEntry("public", arrayOf(Pair("name", name)), null)
            }
        }

        saveFile(document, "public.xml")
    }

    // Helper Functions
@@ -225,17 +356,33 @@ class ColorSchemeTest {
}

private fun Element.createColorEntry(name: String, value: Int, comment: String? = null) {
    this.createColorEntry(name, "#" + value.toRGBHex(), comment)
}

private fun Element.createColorEntry(name: String, value: String, comment: String? = null) {
    this.createEntry("color", arrayOf(Pair("name", name)), value, comment)
}

private fun Element.createEntry(
    tagName: String,
    attrs: Array<Pair<String, String>>,
    value: String?,
    comment: String? = null,
) {
    val doc = this.ownerDocument

    if (comment != null) {
        this.appendChild(doc.createComment(comment))
    }

    val color = doc.createElement("color")
    this.appendChild(color)
    val child = doc.createElement(tagName)
    this.appendChild(child)

    attrs.forEach { child.setAttribute(it.first, it.second) }

    color.setAttribute("name", name)
    color.appendChild(doc.createTextNode("#" + value.toRGBHex()))
    if (value !== null) {
        child.appendChild(doc.createTextNode(value))
    }
}

private fun Node.appendWithBreak(child: Node, lineBreaks: Int = 1): Node {
@@ -248,3 +395,8 @@ private fun Node.appendWithBreak(child: Node, lineBreaks: Int = 1): Node {
private fun Int.toRGBHex(): String {
    return "%06X".format(0xFFFFFF and this)
}

private fun String.snakeToLowerCamelCase(): String {
    val pattern = "_[a-z]".toRegex()
    return replace(pattern) { it.value.last().uppercase() }
}