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

Commit b55f71a3 authored by Marcelo Arteiro's avatar Marcelo Arteiro
Browse files

Aligning Android color tokens with Material

Original change was reverted because of a flag misplacement. Fixed now.

Script also updated to generate update resource files.

Bug: 376195115
Test: presubmit
Flag: EXEMPT update color generation script
Change-Id: Ia877210ffe5cd755d4432a46a47419c95dae8ef0
parent 9676415a
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() }
}