From 2f053554874191a2f07f5a3381c7bf3240db4c6a Mon Sep 17 00:00:00 2001
From: David Perez <david@livefront.com>
Date: Mon, 30 Sep 2024 12:56:32 -0500
Subject: [PATCH] PM-12322: New color scheme (#3995)

---
 .../theme/color/BitwardenColorScheme.kt       | 139 +++++++++
 .../ui/platform/theme/color/ColorScheme.kt    | 278 ++++++++++++++++++
 2 files changed, 417 insertions(+)
 create mode 100644 app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/BitwardenColorScheme.kt
 create mode 100644 app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/ColorScheme.kt

diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/BitwardenColorScheme.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/BitwardenColorScheme.kt
new file mode 100644
index 000000000..20409dc46
--- /dev/null
+++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/BitwardenColorScheme.kt
@@ -0,0 +1,139 @@
+package com.x8bit.bitwarden.ui.platform.theme.color
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.graphics.Color
+
+/**
+ * Defines all the colors for the app.
+ */
+@Immutable
+data class BitwardenColorScheme(
+    val text: TextColors,
+    val background: BackgroundColors,
+    val stroke: StrokeColors,
+    val icon: IconColors,
+    val filledButton: FilledButtonColors,
+    val outlineButton: OutlineButtonColors,
+    val tonalButton: TonalButtonColors,
+    val toggleButton: ToggleButtonColors,
+    val sliderButton: SliderButtonColors,
+    val status: StatusColors,
+) {
+    /**
+     * Defines all the text colors for the app.
+     */
+    @Immutable
+    data class TextColors(
+        val primary: Color,
+        val secondary: Color,
+        val interaction: Color,
+        val reversed: Color,
+        val codePink: Color,
+        val codeBlue: Color,
+    )
+
+    /**
+     * Defines all the background colors for the app.
+     */
+    @Immutable
+    data class BackgroundColors(
+        val primary: Color,
+        val secondary: Color,
+        val tertiary: Color,
+        val alert: Color,
+        val scrim: Color,
+        val pressed: Color,
+    )
+
+    /**
+     * Defines all the stroke colors for the app.
+     */
+    @Immutable
+    data class StrokeColors(
+        val divider: Color,
+        val border: Color,
+        val segmentedNav: Color,
+    )
+
+    /**
+     * Defines all the icons colors for the app.
+     */
+    @Immutable
+    data class IconColors(
+        val primary: Color,
+        val secondary: Color,
+        val reversed: Color,
+        val badgeBackground: Color,
+        val badgeForeground: Color,
+    )
+
+    /**
+     * Defines all the filled button colors for the app.
+     */
+    @Immutable
+    data class FilledButtonColors(
+        val background: Color,
+        val backgroundDisabled: Color,
+        val backgroundReversed: Color,
+        val foreground: Color,
+        val foregroundDisabled: Color,
+        val foregroundReversed: Color,
+    )
+
+    /**
+     * Defines all the outline button colors for the app.
+     */
+    @Immutable
+    data class OutlineButtonColors(
+        val border: Color,
+        val borderDisabled: Color,
+        val borderReversed: Color,
+        val foreground: Color,
+        val foregroundDisabled: Color,
+        val foregroundReversed: Color,
+    )
+
+    /**
+     * Defines all the tonal button colors for the app.
+     */
+    @Immutable
+    data class TonalButtonColors(
+        val background: Color,
+        val backgroundDisabled: Color,
+        val foreground: Color,
+        val foregroundDisabled: Color,
+    )
+
+    /**
+     * Defines all the toggle colors for the app.
+     */
+    @Immutable
+    data class ToggleButtonColors(
+        val backgroundOn: Color,
+        val backgroundOff: Color,
+        val switch: Color,
+    )
+
+    /**
+     * Defines all the slider colors for the app.
+     */
+    @Immutable
+    data class SliderButtonColors(
+        val knobBackground: Color,
+        val knobLabel: Color,
+        val filled: Color,
+        val unfilled: Color,
+    )
+
+    /**
+     * Defines all the status colors for the app.
+     */
+    @Immutable
+    data class StatusColors(
+        val strong: Color,
+        val good: Color,
+        val weak1: Color,
+        val weak2: Color,
+        val error: Color,
+    )
+}
diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/ColorScheme.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/ColorScheme.kt
new file mode 100644
index 000000000..4ff0c02fa
--- /dev/null
+++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/ColorScheme.kt
@@ -0,0 +1,278 @@
+package com.x8bit.bitwarden.ui.platform.theme.color
+
+import androidx.compose.material3.ColorScheme
+import androidx.compose.ui.graphics.Color
+
+/**
+ * The default [BitwardenColorScheme] for dark mode.
+ */
+val darkBitwardenColorScheme: BitwardenColorScheme = BitwardenColorScheme(
+    text = BitwardenColorScheme.TextColors(
+        primary = PrimitiveColors.gray200,
+        secondary = PrimitiveColors.gray600,
+        interaction = PrimitiveColors.blue400,
+        reversed = PrimitiveColors.gray1200,
+        codePink = PrimitiveColors.pink200,
+        codeBlue = PrimitiveColors.blue400,
+    ),
+    background = BitwardenColorScheme.BackgroundColors(
+        primary = PrimitiveColors.gray1200,
+        secondary = PrimitiveColors.gray1100,
+        tertiary = PrimitiveColors.gray1000,
+        alert = PrimitiveColors.gray300,
+        scrim = PrimitiveColors.gray1400.copy(alpha = 0.4f),
+        pressed = PrimitiveColors.gray500,
+    ),
+    stroke = BitwardenColorScheme.StrokeColors(
+        divider = PrimitiveColors.gray900,
+        border = PrimitiveColors.blue400,
+        segmentedNav = PrimitiveColors.gray900,
+    ),
+    icon = BitwardenColorScheme.IconColors(
+        primary = PrimitiveColors.gray500,
+        secondary = PrimitiveColors.blue400,
+        reversed = PrimitiveColors.gray1100,
+        badgeBackground = PrimitiveColors.pink200,
+        badgeForeground = PrimitiveColors.gray1100,
+    ),
+    filledButton = BitwardenColorScheme.FilledButtonColors(
+        background = PrimitiveColors.blue400,
+        backgroundDisabled = PrimitiveColors.gray900,
+        backgroundReversed = PrimitiveColors.gray1100,
+        foreground = PrimitiveColors.gray1100,
+        foregroundDisabled = PrimitiveColors.gray500,
+        foregroundReversed = PrimitiveColors.blue400,
+    ),
+    outlineButton = BitwardenColorScheme.OutlineButtonColors(
+        border = PrimitiveColors.blue400,
+        borderDisabled = PrimitiveColors.gray900,
+        borderReversed = PrimitiveColors.gray1100,
+        foreground = PrimitiveColors.blue400,
+        foregroundDisabled = PrimitiveColors.gray900,
+        foregroundReversed = PrimitiveColors.gray1100,
+    ),
+    tonalButton = BitwardenColorScheme.TonalButtonColors(
+        background = PrimitiveColors.gray900,
+        backgroundDisabled = PrimitiveColors.gray900,
+        foreground = PrimitiveColors.gray200,
+        foregroundDisabled = PrimitiveColors.gray500,
+    ),
+    toggleButton = BitwardenColorScheme.ToggleButtonColors(
+        backgroundOn = PrimitiveColors.blue300,
+        backgroundOff = PrimitiveColors.gray900,
+        switch = PrimitiveColors.gray100,
+    ),
+    sliderButton = BitwardenColorScheme.SliderButtonColors(
+        knobBackground = PrimitiveColors.blue400,
+        knobLabel = PrimitiveColors.gray1100,
+        filled = PrimitiveColors.blue400,
+        unfilled = PrimitiveColors.gray900,
+    ),
+    status = BitwardenColorScheme.StatusColors(
+        strong = PrimitiveColors.green200,
+        good = PrimitiveColors.blue400,
+        weak1 = PrimitiveColors.red200,
+        weak2 = PrimitiveColors.yellow200,
+        error = PrimitiveColors.red200,
+    ),
+)
+
+/**
+ * The default [BitwardenColorScheme] for light mode.
+ */
+val lightBitwardenColorScheme: BitwardenColorScheme = BitwardenColorScheme(
+    text = BitwardenColorScheme.TextColors(
+        primary = PrimitiveColors.gray1300,
+        secondary = PrimitiveColors.gray700,
+        interaction = PrimitiveColors.blue500,
+        reversed = PrimitiveColors.gray100,
+        codePink = PrimitiveColors.pink100,
+        codeBlue = PrimitiveColors.blue500,
+    ),
+    background = BitwardenColorScheme.BackgroundColors(
+        primary = PrimitiveColors.gray200,
+        secondary = PrimitiveColors.gray100,
+        tertiary = PrimitiveColors.blue100,
+        alert = PrimitiveColors.blue700,
+        scrim = PrimitiveColors.gray1400.copy(alpha = 0.4f),
+        pressed = PrimitiveColors.gray1000,
+    ),
+    stroke = BitwardenColorScheme.StrokeColors(
+        divider = PrimitiveColors.gray400,
+        border = PrimitiveColors.blue500,
+        segmentedNav = PrimitiveColors.blue100,
+    ),
+    icon = BitwardenColorScheme.IconColors(
+        primary = PrimitiveColors.gray700,
+        secondary = PrimitiveColors.blue500,
+        reversed = PrimitiveColors.gray100,
+        badgeBackground = PrimitiveColors.pink100,
+        badgeForeground = PrimitiveColors.gray100,
+    ),
+    filledButton = BitwardenColorScheme.FilledButtonColors(
+        background = PrimitiveColors.blue500,
+        backgroundDisabled = PrimitiveColors.gray400,
+        backgroundReversed = PrimitiveColors.gray100,
+        foreground = PrimitiveColors.gray100,
+        foregroundDisabled = PrimitiveColors.gray500,
+        foregroundReversed = PrimitiveColors.blue500,
+    ),
+    outlineButton = BitwardenColorScheme.OutlineButtonColors(
+        border = PrimitiveColors.blue500,
+        borderDisabled = PrimitiveColors.gray500,
+        borderReversed = PrimitiveColors.gray100,
+        foreground = PrimitiveColors.blue500,
+        foregroundDisabled = PrimitiveColors.gray500,
+        foregroundReversed = PrimitiveColors.gray100,
+    ),
+    tonalButton = BitwardenColorScheme.TonalButtonColors(
+        background = PrimitiveColors.blue100,
+        backgroundDisabled = PrimitiveColors.gray400,
+        foreground = PrimitiveColors.gray1300,
+        foregroundDisabled = PrimitiveColors.gray500,
+    ),
+    toggleButton = BitwardenColorScheme.ToggleButtonColors(
+        backgroundOn = PrimitiveColors.blue500,
+        backgroundOff = PrimitiveColors.gray500,
+        switch = PrimitiveColors.gray100,
+    ),
+    sliderButton = BitwardenColorScheme.SliderButtonColors(
+        knobBackground = PrimitiveColors.blue500,
+        knobLabel = PrimitiveColors.gray100,
+        filled = PrimitiveColors.blue500,
+        unfilled = PrimitiveColors.gray300,
+    ),
+    status = BitwardenColorScheme.StatusColors(
+        strong = PrimitiveColors.green300,
+        good = PrimitiveColors.blue500,
+        weak1 = PrimitiveColors.red300,
+        weak2 = PrimitiveColors.yellow300,
+        error = PrimitiveColors.red300,
+    ),
+)
+
+/**
+ * Creates a [BitwardenColorScheme] for dark mode based on dynamic Material You colors.
+ */
+@Suppress("LongMethod")
+fun dynamicBitwardenColorScheme(
+    materialColorScheme: ColorScheme,
+    isDarkTheme: Boolean,
+): BitwardenColorScheme {
+    val defaultTheme = if (isDarkTheme) darkBitwardenColorScheme else lightBitwardenColorScheme
+    return BitwardenColorScheme(
+        text = BitwardenColorScheme.TextColors(
+            primary = materialColorScheme.onSurface,
+            secondary = materialColorScheme.onSecondaryContainer,
+            interaction = materialColorScheme.primary,
+            reversed = materialColorScheme.onTertiary,
+            codePink = defaultTheme.text.codePink,
+            codeBlue = defaultTheme.text.codeBlue,
+        ),
+        background = BitwardenColorScheme.BackgroundColors(
+            primary = materialColorScheme.surface,
+            secondary = materialColorScheme.secondary,
+            tertiary = materialColorScheme.tertiary,
+            alert = materialColorScheme.error,
+            scrim = materialColorScheme.scrim,
+            pressed = materialColorScheme.onSurfaceVariant,
+        ),
+        stroke = BitwardenColorScheme.StrokeColors(
+            divider = materialColorScheme.outline,
+            border = materialColorScheme.primary,
+            segmentedNav = materialColorScheme.outline,
+        ),
+        icon = BitwardenColorScheme.IconColors(
+            primary = materialColorScheme.onPrimary,
+            secondary = materialColorScheme.onSecondary,
+            reversed = materialColorScheme.inversePrimary,
+            badgeBackground = materialColorScheme.error,
+            badgeForeground = materialColorScheme.onError,
+        ),
+        filledButton = BitwardenColorScheme.FilledButtonColors(
+            background = materialColorScheme.surface,
+            backgroundDisabled = materialColorScheme.onSurface.copy(alpha = 0.12f),
+            backgroundReversed = materialColorScheme.inversePrimary,
+            foreground = materialColorScheme.onSurface,
+            foregroundDisabled = materialColorScheme.onSurface.copy(alpha = 0.38f),
+            foregroundReversed = materialColorScheme.inverseOnSurface,
+        ),
+        outlineButton = BitwardenColorScheme.OutlineButtonColors(
+            border = materialColorScheme.outline,
+            borderDisabled = materialColorScheme.outlineVariant,
+            borderReversed = materialColorScheme.outlineVariant,
+            foreground = materialColorScheme.outlineVariant,
+            foregroundDisabled = materialColorScheme.outlineVariant,
+            foregroundReversed = materialColorScheme.outlineVariant,
+        ),
+        tonalButton = BitwardenColorScheme.TonalButtonColors(
+            background = materialColorScheme.secondaryContainer,
+            backgroundDisabled = materialColorScheme.onSurface.copy(alpha = 0.12f),
+            foreground = materialColorScheme.onSecondaryContainer,
+            foregroundDisabled = materialColorScheme.onSurface.copy(alpha = 0.38f),
+        ),
+        toggleButton = BitwardenColorScheme.ToggleButtonColors(
+            backgroundOn = materialColorScheme.primary,
+            backgroundOff = materialColorScheme.surfaceContainerHighest,
+            switch = materialColorScheme.onPrimary,
+        ),
+        sliderButton = BitwardenColorScheme.SliderButtonColors(
+            knobBackground = materialColorScheme.primary,
+            knobLabel = materialColorScheme.onPrimary,
+            filled = materialColorScheme.primary,
+            unfilled = materialColorScheme.secondaryContainer,
+        ),
+        status = BitwardenColorScheme.StatusColors(
+            strong = defaultTheme.status.strong,
+            good = defaultTheme.status.good,
+            weak1 = defaultTheme.status.weak1,
+            weak2 = defaultTheme.status.weak2,
+            error = defaultTheme.status.error,
+        ),
+    )
+}
+
+/**
+ * The raw colors that support the [BitwardenColorScheme].
+ */
+private data object PrimitiveColors {
+    val gray100: Color = Color(color = 0xFFFFFFFF)
+    val gray200: Color = Color(color = 0xFFF3F6F9)
+    val gray300: Color = Color(color = 0xFFE6E9EF)
+    val gray400: Color = Color(color = 0xFFD3D9E3)
+    val gray500: Color = Color(color = 0xFF96A3BB)
+    val gray600: Color = Color(color = 0xFF8898B5)
+    val gray700: Color = Color(color = 0xFF5A6D91)
+    val gray800: Color = Color(color = 0xFF79808E)
+    val gray900: Color = Color(color = 0xFF525B6A)
+    val gray1000: Color = Color(color = 0xFF303946)
+    val gray1100: Color = Color(color = 0xFF202733)
+    val gray1200: Color = Color(color = 0xFF121A27)
+    val gray1300: Color = Color(color = 0xFF1B2029)
+    val gray1400: Color = Color(color = 0xFF000000)
+
+    val blue100: Color = Color(color = 0xFFDBE5F6)
+    val blue200: Color = Color(color = 0xFFAAC3EF)
+    val blue300: Color = Color(color = 0xFF79A1E9)
+    val blue400: Color = Color(color = 0xFF65ABFF)
+    val blue500: Color = Color(color = 0xFF175DDC)
+    val blue600: Color = Color(color = 0xFF1A41AC)
+    val blue700: Color = Color(color = 0xFF020F66)
+
+    val green100: Color = Color(color = 0xFFBFECC3)
+    val green200: Color = Color(color = 0xFF6BF178)
+    val green300: Color = Color(color = 0xFF0C8018)
+    val green400: Color = Color(color = 0xFF08540F)
+
+    val red100: Color = Color(color = 0xFFFFECEF)
+    val red200: Color = Color(color = 0xFFFF4E63)
+    val red300: Color = Color(color = 0xFFCB263A)
+    val red400: Color = Color(color = 0xFF951B2A)
+
+    val yellow100: Color = Color(color = 0xFFFFF8E4)
+    val yellow200: Color = Color(color = 0xFFFFBF00)
+    val yellow300: Color = Color(color = 0xFFAC5800)
+
+    val pink100: Color = Color(color = 0xFFC01176)
+    val pink200: Color = Color(color = 0xFFFF8FD0)
+}