1 /*
2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package androidx.glance.material3
18
19 import androidx.compose.material3.ColorScheme
20 import androidx.compose.ui.graphics.Color
21 import androidx.compose.ui.graphics.toArgb
22 import androidx.core.graphics.ColorUtils.M3HCTToColor
23 import androidx.core.graphics.ColorUtils.colorToM3HCT
24 import androidx.glance.color.ColorProvider
25 import androidx.glance.color.ColorProviders
26 import androidx.glance.color.colorProviders
27 import androidx.glance.unit.ColorProvider
28
29 /**
30 * Creates a Material 3 [ColorProviders] given a light and dark [ColorScheme]. Each color in the
31 * theme will have a day and night mode.
32 */
ColorProvidersnull33 fun ColorProviders(light: ColorScheme, dark: ColorScheme): ColorProviders {
34 return colorProviders(
35 primary = ColorProvider(day = light.primary, night = dark.primary),
36 onPrimary = ColorProvider(day = light.onPrimary, night = dark.onPrimary),
37 primaryContainer =
38 ColorProvider(day = light.primaryContainer, night = dark.primaryContainer),
39 onPrimaryContainer =
40 ColorProvider(day = light.onPrimaryContainer, night = dark.onPrimaryContainer),
41 secondary = ColorProvider(day = light.secondary, night = dark.secondary),
42 onSecondary = ColorProvider(day = light.onSecondary, night = dark.onSecondary),
43 secondaryContainer =
44 ColorProvider(day = light.secondaryContainer, night = dark.secondaryContainer),
45 onSecondaryContainer =
46 ColorProvider(day = light.onSecondaryContainer, night = dark.onSecondaryContainer),
47 tertiary = ColorProvider(day = light.tertiary, night = dark.tertiary),
48 onTertiary = ColorProvider(day = light.onTertiary, night = dark.onTertiary),
49 tertiaryContainer =
50 ColorProvider(day = light.tertiaryContainer, night = dark.tertiaryContainer),
51 onTertiaryContainer =
52 ColorProvider(day = light.onTertiaryContainer, night = dark.onTertiaryContainer),
53 error = ColorProvider(day = light.error, night = dark.error),
54 errorContainer = ColorProvider(day = light.errorContainer, night = dark.errorContainer),
55 onError = ColorProvider(day = light.onError, night = dark.onError),
56 onErrorContainer =
57 ColorProvider(day = light.onErrorContainer, night = dark.onErrorContainer),
58 background = ColorProvider(day = light.background, night = dark.background),
59 onBackground = ColorProvider(day = light.onBackground, night = dark.onBackground),
60 surface = ColorProvider(day = light.surface, night = dark.surface),
61 onSurface = ColorProvider(day = light.onSurface, night = dark.onSurface),
62 surfaceVariant = ColorProvider(day = light.surfaceVariant, night = dark.surfaceVariant),
63 onSurfaceVariant =
64 ColorProvider(day = light.onSurfaceVariant, night = dark.onSurfaceVariant),
65 outline = ColorProvider(day = light.outline, night = dark.outline),
66 inverseOnSurface =
67 ColorProvider(day = light.inverseOnSurface, night = dark.inverseOnSurface),
68 inverseSurface = ColorProvider(day = light.inverseSurface, night = dark.inverseSurface),
69 inversePrimary = ColorProvider(day = light.inversePrimary, night = dark.inversePrimary),
70 // Widget background is a widget / glace specific token it is generally derived from the
71 // secondary container color.
72 widgetBackground =
73 ColorProvider(
74 day = adjustColorToneForWidgetBackground(light.secondaryContainer),
75 night = adjustColorToneForWidgetBackground(dark.secondaryContainer)
76 ),
77 )
78 }
79
80 /**
81 * Creates a Material 3 [ColorProviders] given a [ColorScheme]. This is a fixed scheme and does not
82 * have day/night modes.
83 */
ColorProvidersnull84 fun ColorProviders(scheme: ColorScheme): ColorProviders {
85 return colorProviders(
86 primary = ColorProvider(color = scheme.primary),
87 onPrimary = ColorProvider(scheme.onPrimary),
88 primaryContainer = ColorProvider(color = scheme.primaryContainer),
89 onPrimaryContainer = ColorProvider(color = scheme.onPrimaryContainer),
90 secondary = ColorProvider(color = scheme.secondary),
91 onSecondary = ColorProvider(color = scheme.onSecondary),
92 secondaryContainer = ColorProvider(color = scheme.secondaryContainer),
93 onSecondaryContainer = ColorProvider(color = scheme.onSecondaryContainer),
94 tertiary = ColorProvider(color = scheme.tertiary),
95 onTertiary = ColorProvider(color = scheme.onTertiary),
96 tertiaryContainer = ColorProvider(color = scheme.tertiaryContainer),
97 onTertiaryContainer = ColorProvider(color = scheme.onTertiaryContainer),
98 error = ColorProvider(color = scheme.error),
99 onError = ColorProvider(color = scheme.onError),
100 errorContainer = ColorProvider(color = scheme.errorContainer),
101 onErrorContainer = ColorProvider(color = scheme.onErrorContainer),
102 background = ColorProvider(color = scheme.background),
103 onBackground = ColorProvider(color = scheme.onBackground),
104 surface = ColorProvider(color = scheme.surface),
105 onSurface = ColorProvider(color = scheme.onSurface),
106 surfaceVariant = ColorProvider(color = scheme.surfaceVariant),
107 onSurfaceVariant = ColorProvider(color = scheme.onSurfaceVariant),
108 outline = ColorProvider(color = scheme.outline),
109 inverseOnSurface = ColorProvider(color = scheme.inverseOnSurface),
110 inverseSurface = ColorProvider(color = scheme.inverseSurface),
111 inversePrimary = ColorProvider(color = scheme.inversePrimary),
112
113 // Widget background is a widget / glace specific token it is generally derived from the
114 // secondary container color.
115 widgetBackground =
116 ColorProvider(color = adjustColorToneForWidgetBackground(scheme.secondaryContainer))
117 )
118 }
119
120 private const val WIDGET_BG_TONE_ADJUSTMENT_LIGHT = 5f
121 private const val WIDGET_BG_TONE_ADJUSTMENT_DARK = -10f
122
123 /**
124 * Adjusts the input color to work as a widgetBackground token.
125 *
126 * widgetBackground is a Widgets / Glance specific role so won't be present in the original Scheme.
127 * In the system it is defined as being a variation on the secondaryContainer, lighter for light
128 * themes and darker for dark themes.
129 */
adjustColorToneForWidgetBackgroundnull130 private fun adjustColorToneForWidgetBackground(input: Color): Color {
131 val hctColor = floatArrayOf(0f, 0f, 0f)
132 colorToM3HCT(input.toArgb(), hctColor)
133 // Check the Tone of the input color, if it is "light" (greater than 50) lighten it, otherwise
134 // darken it.
135 val adjustment =
136 if (hctColor[2] > 50) WIDGET_BG_TONE_ADJUSTMENT_LIGHT else WIDGET_BG_TONE_ADJUSTMENT_DARK
137
138 // Tone should be defined in the 0 - 100 range, ok to clamp here.
139 val tone = (hctColor[2] + adjustment).coerceIn(0f, 100f)
140 return Color(M3HCTToColor(hctColor[0], hctColor[1], tone))
141 }
142