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.appwidget.demos
18
19 import android.content.Context
20 import androidx.compose.material.darkColors
21 import androidx.compose.material.lightColors
22 import androidx.compose.material3.darkColorScheme
23 import androidx.compose.material3.lightColorScheme
24 import androidx.compose.runtime.Composable
25 import androidx.compose.runtime.getValue
26 import androidx.compose.runtime.mutableStateOf
27 import androidx.compose.runtime.remember
28 import androidx.compose.runtime.setValue
29 import androidx.compose.ui.graphics.Color
30 import androidx.compose.ui.unit.dp
31 import androidx.glance.Button
32 import androidx.glance.GlanceId
33 import androidx.glance.GlanceModifier
34 import androidx.glance.GlanceTheme
35 import androidx.glance.appwidget.CheckBox
36 import androidx.glance.appwidget.GlanceAppWidget
37 import androidx.glance.appwidget.GlanceAppWidgetReceiver
38 import androidx.glance.appwidget.RadioButton
39 import androidx.glance.appwidget.Switch
40 import androidx.glance.appwidget.cornerRadius
41 import androidx.glance.appwidget.provideContent
42 import androidx.glance.background
43 import androidx.glance.color.ColorProviders
44 import androidx.glance.layout.Column
45 import androidx.glance.layout.Row
46 import androidx.glance.layout.fillMaxSize
47 import androidx.glance.layout.fillMaxWidth
48 import androidx.glance.layout.padding
49 import androidx.glance.material.ColorProviders
50 import androidx.glance.material3.ColorProviders
51 import androidx.glance.text.Text
52 import androidx.glance.text.TextStyle
53 import androidx.glance.unit.ColorProvider
54
55 /**
56 * A demo showing how to construct a widget with [GlanceTheme]. It will use Material 3 colors and
57 * when supported, it will use the dynamic color theme.
58 */
59 class DefaultColorsAppWidget : GlanceAppWidget() {
60 enum class Scheme {
61 SystemM3,
62 CustomM3,
63 CustomM2
64 }
65
<lambda>null66 override suspend fun provideGlance(context: Context, id: GlanceId) = provideContent {
67 var currentScheme by remember { mutableStateOf(Scheme.SystemM3) }
68 val colors =
69 when (currentScheme) {
70 Scheme.SystemM3 -> GlanceTheme.colors
71 Scheme.CustomM3 ->
72 ColorProviders(
73 light = DemoColorScheme.LightColors,
74 dark = DemoColorScheme.DarkColors
75 )
76 Scheme.CustomM2 ->
77 ColorProviders(
78 light = DemoColorScheme.SampleM2ColorsLight,
79 dark = DemoColorScheme.SampleM2ColorsDark
80 )
81 }
82
83 Content(colors, currentScheme)
84 }
85
<lambda>null86 override suspend fun providePreview(context: Context, widgetCategory: Int) = provideContent {
87 Content(GlanceTheme.colors, Scheme.SystemM3)
88 }
89
90 @Composable
Contentnull91 private fun Content(colors: ColorProviders, currentScheme: Scheme) {
92 var currentScheme1 = currentScheme
93 GlanceTheme(colors) {
94 Column(
95 GlanceModifier.fillMaxSize()
96 .padding(16.dp)
97 .background(GlanceTheme.colors.widgetBackground)
98 ) {
99 Button(
100 text = "Theme: $currentScheme1",
101 onClick = {
102 currentScheme1 =
103 when (currentScheme1) {
104 Scheme.SystemM3 -> Scheme.CustomM3
105 Scheme.CustomM3 -> Scheme.CustomM2
106 Scheme.CustomM2 -> Scheme.SystemM3
107 }
108 },
109 modifier = GlanceModifier.padding(8.dp)
110 )
111 Row(GlanceModifier.fillMaxWidth().padding(top = 8.dp)) {
112 CheckBox(
113 modifier = GlanceModifier.defaultWeight(),
114 checked = false,
115 onCheckedChange = doNothingAction,
116 text = "Unchecked"
117 )
118 CheckBox(
119 modifier = GlanceModifier.defaultWeight(),
120 checked = true,
121 onCheckedChange = doNothingAction,
122 text = "Checked"
123 )
124 }
125
126 Row(modifier = GlanceModifier.fillMaxWidth().padding(bottom = 8.dp)) {
127 RadioButton(
128 modifier = GlanceModifier.defaultWeight(),
129 checked = false,
130 onClick = doNothingAction,
131 text = "Unchecked"
132 )
133 RadioButton(
134 modifier = GlanceModifier.defaultWeight(),
135 checked = true,
136 onClick = doNothingAction,
137 text = "Checked"
138 )
139 }
140
141 Row(modifier = GlanceModifier.padding(bottom = 8.dp)) {
142 Switch(checked = false, onCheckedChange = doNothingAction, text = "Off")
143 Switch(checked = true, onCheckedChange = doNothingAction, text = "On")
144 }
145 ColorDebug()
146 }
147 }
148 }
149 }
150
151 @Composable
ColorDebugnull152 private fun ColorDebug() {
153 @Composable
154 fun Text(text: String, fg: ColorProvider, bg: ColorProvider) =
155 Text(
156 text = text,
157 style = TextStyle(color = fg),
158 modifier = GlanceModifier.fillMaxWidth().background(bg).padding(6.dp)
159 )
160 Column(modifier = GlanceModifier.cornerRadius(8.dp)) {
161 with(GlanceTheme.colors) {
162 // Using nested column because Glance uses statically generated layouts. Our
163 // Rows/Columns can only support a fixed number of children, so nesting is a workaround.
164 // The usual perf caveats for nested views still apply.
165 Column(modifier = GlanceModifier.background(Color.Transparent)) {
166 Text(text = "Primary / OnPrimary", fg = onPrimary, bg = primary)
167 Text(
168 text = "PrimaryContainer / OnPrimaryContainer",
169 fg = onPrimaryContainer,
170 bg = primaryContainer
171 )
172 Text(text = "Secondary / OnSecondary", fg = onSecondary, bg = secondary)
173 Text(
174 text = "SecondaryContainer / OnSecondaryContainer",
175 fg = onSecondaryContainer,
176 bg = secondaryContainer
177 )
178 Text(text = "Tertiary / OnTertiary", fg = onTertiary, bg = tertiary)
179 }
180 Column {
181 Text(
182 text = "TertiaryContainer / OnTertiaryContainer",
183 fg = onTertiaryContainer,
184 bg = tertiaryContainer
185 )
186 Text(text = "Surface / OnSurface", fg = onSurface, bg = surface)
187 Text(
188 text = "SurfaceVariant / OnSurfaceVariant",
189 fg = onSurfaceVariant,
190 bg = surfaceVariant
191 )
192 Text(
193 text = "InverseOnSurface / InverseSurface",
194 fg = inverseOnSurface,
195 bg = inverseSurface
196 )
197 Text(text = "Background / OnBackground", fg = onBackground, bg = background)
198 Text(text = "Error / OnError", fg = onError, bg = error)
199 }
200 }
201 }
202 }
203
204 private val doNothingAction = null
205
206 class DefaultColorsAppWidgetReceiver : GlanceAppWidgetReceiver() {
207 override val glanceAppWidget = DefaultColorsAppWidget()
208 }
209
210 /** Color scheme generated using https://m3.material.io/theme-builder#/custom */
211 object DemoColorScheme {
212
213 val md_theme_light_primary = Color(0xFF026E00)
214 val md_theme_light_onPrimary = Color(0xFFFFFFFF)
215 val md_theme_light_primaryContainer = Color(0xFF77FF61)
216 val md_theme_light_onPrimaryContainer = Color(0xFF002200)
217 val md_theme_light_secondary = Color(0xFFA900A9)
218 val md_theme_light_onSecondary = Color(0xFFFFFFFF)
219 val md_theme_light_secondaryContainer = Color(0xFFFFD7F5)
220 val md_theme_light_onSecondaryContainer = Color(0xFF380038)
221 val md_theme_light_tertiary = Color(0xFF006A6A)
222 val md_theme_light_onTertiary = Color(0xFFFFFFFF)
223 val md_theme_light_tertiaryContainer = Color(0xFF00FBFB)
224 val md_theme_light_onTertiaryContainer = Color(0xFF002020)
225 val md_theme_light_error = Color(0xFFBA1A1A)
226 val md_theme_light_errorContainer = Color(0xFFFFDAD6)
227 val md_theme_light_onError = Color(0xFFFFFFFF)
228 val md_theme_light_onErrorContainer = Color(0xFF410002)
229 val md_theme_light_background = Color(0xFFFFFBFF)
230 val md_theme_light_onBackground = Color(0xFF1E1C00)
231 val md_theme_light_surface = Color(0xFFFFFBFF)
232 val md_theme_light_onSurface = Color(0xFF1E1C00)
233 val md_theme_light_surfaceVariant = Color(0xFFDFE4D7)
234 val md_theme_light_onSurfaceVariant = Color(0xFF43483F)
235 val md_theme_light_outline = Color(0xFF73796E)
236 val md_theme_light_inverseOnSurface = Color(0xFFFFF565)
237 val md_theme_light_inverseSurface = Color(0xFF353200)
238 val md_theme_light_inversePrimary = Color(0xFF02E600)
239 val md_theme_light_shadow = Color(0xFF000000)
240 val md_theme_light_surfaceTint = Color(0xFF026E00)
241
242 val md_theme_dark_primary = Color(0xFF02E600)
243 val md_theme_dark_onPrimary = Color(0xFF013A00)
244 val md_theme_dark_primaryContainer = Color(0xFF015300)
245 val md_theme_dark_onPrimaryContainer = Color(0xFF77FF61)
246 val md_theme_dark_secondary = Color(0xFFFFABF3)
247 val md_theme_dark_onSecondary = Color(0xFF5B005B)
248 val md_theme_dark_secondaryContainer = Color(0xFF810081)
249 val md_theme_dark_onSecondaryContainer = Color(0xFFFFD7F5)
250 val md_theme_dark_tertiary = Color(0xFF00DDDD)
251 val md_theme_dark_onTertiary = Color(0xFF003737)
252 val md_theme_dark_tertiaryContainer = Color(0xFF004F4F)
253 val md_theme_dark_onTertiaryContainer = Color(0xFF00FBFB)
254 val md_theme_dark_error = Color(0xFFFFB4AB)
255 val md_theme_dark_errorContainer = Color(0xFF93000A)
256 val md_theme_dark_onError = Color(0xFF690005)
257 val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
258 val md_theme_dark_background = Color(0xFF1E1C00)
259 val md_theme_dark_onBackground = Color(0xFFF2E720)
260 val md_theme_dark_surface = Color(0xFF1E1C00)
261 val md_theme_dark_onSurface = Color(0xFFF2E720)
262 val md_theme_dark_surfaceVariant = Color(0xFF43483F)
263 val md_theme_dark_onSurfaceVariant = Color(0xFFC3C8BC)
264 val md_theme_dark_outline = Color(0xFF8D9387)
265 val md_theme_dark_inverseOnSurface = Color(0xFF1E1C00)
266 val md_theme_dark_inverseSurface = Color(0xFFF2E720)
267 val md_theme_dark_inversePrimary = Color(0xFF026E00)
268 val md_theme_dark_shadow = Color(0xFF000000)
269 val md_theme_dark_surfaceTint = Color(0xFF02E600)
270
271 val seed = Color(0xFF00FF00)
272
273 val LightColors =
274 lightColorScheme(
275 primary = md_theme_light_primary,
276 onPrimary = md_theme_light_onPrimary,
277 primaryContainer = md_theme_light_primaryContainer,
278 onPrimaryContainer = md_theme_light_onPrimaryContainer,
279 secondary = md_theme_light_secondary,
280 onSecondary = md_theme_light_onSecondary,
281 secondaryContainer = md_theme_light_secondaryContainer,
282 onSecondaryContainer = md_theme_light_onSecondaryContainer,
283 tertiary = md_theme_light_tertiary,
284 onTertiary = md_theme_light_onTertiary,
285 tertiaryContainer = md_theme_light_tertiaryContainer,
286 onTertiaryContainer = md_theme_light_onTertiaryContainer,
287 error = md_theme_light_error,
288 onError = md_theme_light_onError,
289 errorContainer = md_theme_light_errorContainer,
290 onErrorContainer = md_theme_light_onErrorContainer,
291 background = md_theme_light_background,
292 onBackground = md_theme_light_onBackground,
293 surface = md_theme_light_surface,
294 onSurface = md_theme_light_onSurface,
295 surfaceVariant = md_theme_light_surfaceVariant,
296 onSurfaceVariant = md_theme_light_onSurfaceVariant,
297 outline = md_theme_light_outline,
298 inverseSurface = md_theme_light_inverseSurface,
299 inverseOnSurface = md_theme_light_inverseOnSurface,
300 inversePrimary = md_theme_light_inversePrimary,
301 surfaceTint = md_theme_light_surfaceTint,
302 )
303
304 val DarkColors =
305 darkColorScheme(
306 primary = md_theme_dark_primary,
307 onPrimary = md_theme_dark_onPrimary,
308 primaryContainer = md_theme_dark_primaryContainer,
309 onPrimaryContainer = md_theme_dark_onPrimaryContainer,
310 secondary = md_theme_dark_secondary,
311 onSecondary = md_theme_dark_onSecondary,
312 secondaryContainer = md_theme_dark_secondaryContainer,
313 onSecondaryContainer = md_theme_dark_onSecondaryContainer,
314 tertiary = md_theme_dark_tertiary,
315 onTertiary = md_theme_dark_onTertiary,
316 tertiaryContainer = md_theme_dark_tertiaryContainer,
317 onTertiaryContainer = md_theme_dark_onTertiaryContainer,
318 error = md_theme_dark_error,
319 onError = md_theme_dark_onError,
320 errorContainer = md_theme_dark_errorContainer,
321 onErrorContainer = md_theme_dark_onErrorContainer,
322 background = md_theme_dark_background,
323 onBackground = md_theme_dark_onBackground,
324 surface = md_theme_dark_surface,
325 onSurface = md_theme_dark_onSurface,
326 surfaceVariant = md_theme_dark_surfaceVariant,
327 onSurfaceVariant = md_theme_dark_onSurfaceVariant,
328 outline = md_theme_dark_outline,
329 inverseSurface = md_theme_dark_inverseSurface,
330 inverseOnSurface = md_theme_dark_inverseOnSurface,
331 inversePrimary = md_theme_dark_inversePrimary,
332 surfaceTint = md_theme_dark_surfaceTint,
333 )
334
335 // Palette based on Jetchat
336 private val Yellow400 = Color(0xFFF6E547)
337 private val Yellow700 = Color(0xFFF3B711)
338 private val Yellow800 = Color(0xFFF29F05)
339 private val Blue200 = Color(0xFF9DA3FA)
340 private val Blue400 = Color(0xFF4860F7)
341 private val Blue500 = Color(0xFF0540F2)
342 private val Blue800 = Color(0xFF001CCF)
343 private val Red300 = Color(0xFFEA6D7E)
344 private val Red800 = Color(0xFFD00036)
345
346 val SampleM2ColorsDark =
347 darkColors(
348 primary = Blue200,
349 primaryVariant = Blue400,
350 onPrimary = Color.Black,
351 secondary = Yellow400,
352 onSecondary = Color.Black,
353 onSurface = Color.White,
354 onBackground = Color.White,
355 error = Red300,
356 onError = Color.Black
357 )
358 val SampleM2ColorsLight =
359 lightColors(
360 primary = Blue500,
361 primaryVariant = Blue800,
362 onPrimary = Color.White,
363 secondary = Yellow700,
364 secondaryVariant = Yellow800,
365 onSecondary = Color.Black,
366 onSurface = Color.Black,
367 onBackground = Color.Black,
368 error = Red800,
369 onError = Color.White
370 )
371 }
372