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