1 /*
<lambda>null2 * Copyright 2021 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
18
19 import android.app.Activity
20 import android.graphics.drawable.BitmapDrawable
21 import androidx.compose.runtime.Composable
22 import androidx.compose.ui.graphics.Color
23 import androidx.compose.ui.unit.DpSize
24 import androidx.compose.ui.unit.dp
25 import androidx.compose.ui.unit.sp
26 import androidx.glance.Button
27 import androidx.glance.ButtonDefaults
28 import androidx.glance.ColorFilter
29 import androidx.glance.GlanceModifier
30 import androidx.glance.GlanceTheme
31 import androidx.glance.Image
32 import androidx.glance.ImageProvider
33 import androidx.glance.LocalContext
34 import androidx.glance.action.actionStartActivity
35 import androidx.glance.appwidget.components.CircleIconButton
36 import androidx.glance.appwidget.components.FilledButton
37 import androidx.glance.appwidget.components.OutlineButton
38 import androidx.glance.appwidget.components.Scaffold
39 import androidx.glance.appwidget.components.SquareIconButton
40 import androidx.glance.appwidget.components.TitleBar
41 import androidx.glance.appwidget.lazy.LazyColumn
42 import androidx.glance.appwidget.test.R
43 import androidx.glance.background
44 import androidx.glance.color.ColorProvider
45 import androidx.glance.color.colorProviders
46 import androidx.glance.layout.Alignment
47 import androidx.glance.layout.Box
48 import androidx.glance.layout.Column
49 import androidx.glance.layout.ContentScale
50 import androidx.glance.layout.Row
51 import androidx.glance.layout.Spacer
52 import androidx.glance.layout.fillMaxHeight
53 import androidx.glance.layout.fillMaxSize
54 import androidx.glance.layout.fillMaxWidth
55 import androidx.glance.layout.height
56 import androidx.glance.layout.padding
57 import androidx.glance.layout.size
58 import androidx.glance.layout.width
59 import androidx.glance.layout.wrapContentSize
60 import androidx.glance.text.FontStyle
61 import androidx.glance.text.FontWeight
62 import androidx.glance.text.Text
63 import androidx.glance.text.TextAlign
64 import androidx.glance.text.TextDecoration
65 import androidx.glance.text.TextStyle
66 import androidx.glance.unit.ColorProvider
67 import androidx.test.filters.MediumTest
68 import androidx.test.filters.SdkSuppress
69 import kotlinx.coroutines.ExperimentalCoroutinesApi
70 import kotlinx.coroutines.test.runTest
71 import org.junit.Rule
72 import org.junit.Test
73 import org.junit.rules.RuleChain
74 import org.junit.rules.TestRule
75
76 @SdkSuppress(minSdkVersion = 29)
77 @OptIn(ExperimentalCoroutinesApi::class)
78 @MediumTest
79 class GlanceAppWidgetReceiverScreenshotTest {
80 private val mScreenshotRule = screenshotRule()
81 private val mHostRule = AppWidgetHostRule()
82
83 @Rule
84 @JvmField
85 val mRule: TestRule =
86 RuleChain.outerRule(mHostRule)
87 .around(mScreenshotRule)
88 .around(WithRtlRule)
89 .around(WithNightModeRule)
90
91 @Test
92 fun createSimpleAppWidget() {
93 TestGlanceAppWidget.uiDefinition = {
94 Text(
95 "text",
96 style =
97 TextStyle(
98 textDecoration = TextDecoration.Underline,
99 fontWeight = FontWeight.Medium,
100 fontStyle = FontStyle.Italic,
101 )
102 )
103 }
104
105 mHostRule.startHost()
106
107 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "simpleAppWidget")
108 }
109
110 @Test
111 fun createCheckBoxAppWidget() {
112 TestGlanceAppWidget.uiDefinition = { CheckBoxScreenshotTest() }
113
114 mHostRule.startHost()
115
116 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "checkBoxWidget")
117 }
118
119 @WithNightMode
120 @Test
121 fun createCheckBoxAppWidget_dark() {
122 TestGlanceAppWidget.uiDefinition = { CheckBoxScreenshotTest() }
123
124 mHostRule.startHost()
125
126 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "checkBoxWidget_dark")
127 }
128
129 @Test
130 fun createCheckSwitchAppWidget() {
131 TestGlanceAppWidget.uiDefinition = { SwitchTest() }
132
133 mHostRule.startHost()
134
135 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "switchWidget")
136 }
137
138 @WithNightMode
139 @Test
140 fun createCheckSwitchAppWidget_dark() {
141 TestGlanceAppWidget.uiDefinition = { SwitchTest() }
142
143 mHostRule.startHost()
144
145 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "switchWidget_dark")
146 }
147
148 @Test
149 fun createRadioButtonAppWidget() {
150 TestGlanceAppWidget.uiDefinition = { RadioButtonScreenshotTest() }
151
152 mHostRule.startHost()
153
154 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "radioButtonWidget")
155 }
156
157 @WithNightMode
158 @Test
159 fun createRadioButtonAppWidget_dark() {
160 TestGlanceAppWidget.uiDefinition = { RadioButtonScreenshotTest() }
161
162 mHostRule.startHost()
163
164 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "radioButtonWidget_dark")
165 }
166
167 @Test
168 fun createRowWidget() {
169 TestGlanceAppWidget.uiDefinition = { RowTest() }
170
171 mHostRule.startHost()
172
173 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "rowWidget")
174 }
175
176 @WithRtl
177 @Test
178 fun createRowWidget_rtl() {
179 TestGlanceAppWidget.uiDefinition = { RowTest() }
180
181 mHostRule.startHost()
182
183 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "rowWidget_rtl")
184 }
185
186 @Test
187 fun checkTextAlignment() {
188 TestGlanceAppWidget.uiDefinition = { TextAlignmentTest() }
189
190 mHostRule.startHost()
191
192 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "textAlignment")
193 }
194
195 @WithRtl
196 @Test
197 fun checkTextAlignment_rtl() {
198 TestGlanceAppWidget.uiDefinition = { TextAlignmentTest() }
199
200 mHostRule.startHost()
201
202 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "textAlignment_rtl")
203 }
204
205 @Test
206 fun checkBackgroundColor_light() {
207 TestGlanceAppWidget.uiDefinition = { BackgroundTest() }
208
209 mHostRule.startHost()
210
211 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "backgroundColor")
212 }
213
214 @Test
215 @WithNightMode
216 fun checkBackgroundColor_dark() {
217 TestGlanceAppWidget.uiDefinition = { BackgroundTest() }
218
219 mHostRule.startHost()
220
221 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "backgroundColor_dark")
222 }
223
224 @Test
225 fun checkTextColor_light() {
226 TestGlanceAppWidget.uiDefinition = { TextColorTest() }
227
228 mHostRule.startHost()
229
230 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "textColor")
231 }
232
233 @Test
234 @WithNightMode
235 fun checkTextColor_dark() {
236 TestGlanceAppWidget.uiDefinition = { TextColorTest() }
237
238 mHostRule.startHost()
239
240 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "textColor_dark")
241 }
242
243 @Test
244 fun checkButtonRoundedCorners_light() {
245 TestGlanceAppWidget.uiDefinition = { RoundedButtonScreenshotTest() }
246
247 mHostRule.startHost()
248
249 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "roundedButton_light")
250 }
251
252 @Test
253 @WithNightMode
254 fun checkButtonRoundedCorners_dark() {
255 TestGlanceAppWidget.uiDefinition = { RoundedButtonScreenshotTest() }
256
257 mHostRule.startHost()
258
259 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "roundedButton_dark")
260 }
261
262 @Test
263 fun checkButtonTextAlignment() {
264 TestGlanceAppWidget.uiDefinition = {
265 Column(modifier = GlanceModifier.fillMaxSize()) {
266 Row(modifier = GlanceModifier.defaultWeight().fillMaxWidth()) {
267 Button(
268 "Start",
269 onClick = actionStartActivity<Activity>(),
270 modifier = GlanceModifier.defaultWeight().fillMaxHeight(),
271 colors =
272 ButtonDefaults.buttonColors(
273 backgroundColor = ColorProvider(Color.Transparent),
274 contentColor = ColorProvider(Color.DarkGray)
275 ),
276 style = TextStyle(textAlign = TextAlign.Start)
277 )
278 Button(
279 "End",
280 onClick = actionStartActivity<Activity>(),
281 modifier = GlanceModifier.defaultWeight().fillMaxHeight(),
282 colors =
283 ButtonDefaults.buttonColors(
284 backgroundColor = ColorProvider(Color.Transparent),
285 contentColor = ColorProvider(Color.DarkGray)
286 ),
287 style = TextStyle(textAlign = TextAlign.End)
288 )
289 }
290 Row(modifier = GlanceModifier.defaultWeight().fillMaxWidth()) {
291 CheckBox(
292 checked = false,
293 onCheckedChange = null,
294 text = "Start",
295 modifier = GlanceModifier.defaultWeight().fillMaxHeight(),
296 style = TextStyle(textAlign = TextAlign.Start)
297 )
298 CheckBox(
299 checked = true,
300 onCheckedChange = null,
301 text = "End",
302 modifier = GlanceModifier.defaultWeight().fillMaxHeight(),
303 style = TextStyle(textAlign = TextAlign.End)
304 )
305 }
306 Row(modifier = GlanceModifier.defaultWeight().fillMaxWidth()) {
307 Switch(
308 checked = false,
309 onCheckedChange = null,
310 text = "Start",
311 modifier = GlanceModifier.defaultWeight().fillMaxHeight(),
312 style = TextStyle(textAlign = TextAlign.Start)
313 )
314 Switch(
315 checked = true,
316 onCheckedChange = null,
317 text = "End",
318 modifier = GlanceModifier.defaultWeight().fillMaxHeight(),
319 style = TextStyle(textAlign = TextAlign.End)
320 )
321 }
322 Row(modifier = GlanceModifier.defaultWeight().fillMaxWidth()) {
323 RadioButton(
324 checked = false,
325 onClick = null,
326 text = "Start",
327 modifier = GlanceModifier.defaultWeight().fillMaxHeight(),
328 style = TextStyle(textAlign = TextAlign.Start)
329 )
330 RadioButton(
331 checked = true,
332 onClick = null,
333 text = "End",
334 modifier = GlanceModifier.defaultWeight().fillMaxHeight(),
335 style = TextStyle(textAlign = TextAlign.End)
336 )
337 }
338 }
339 }
340
341 mHostRule.setSizes(DpSize(300.dp, 400.dp))
342 mHostRule.startHost()
343
344 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "button_text_align")
345 }
346
347 @Test
348 fun checkFixTopLevelSize() {
349 TestGlanceAppWidget.uiDefinition = {
350 Column(
351 modifier = GlanceModifier.size(100.dp).background(Color.DarkGray),
352 horizontalAlignment = Alignment.End,
353 ) {
354 Text(
355 "Upper half",
356 modifier = GlanceModifier.defaultWeight().fillMaxWidth().background(Color.Green)
357 )
358 Text(
359 "Lower right half",
360 modifier = GlanceModifier.defaultWeight().width(50.dp).background(Color.Cyan)
361 )
362 }
363 }
364
365 mHostRule.startHost()
366
367 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "fixed_top_level_size")
368 }
369
370 @Test
371 fun checkTopLevelFill() {
372 TestGlanceAppWidget.uiDefinition = {
373 Column(
374 modifier = GlanceModifier.fillMaxSize().background(Color.DarkGray),
375 horizontalAlignment = Alignment.End,
376 ) {
377 Text(
378 "Upper half",
379 modifier = GlanceModifier.defaultWeight().fillMaxWidth().background(Color.Green)
380 )
381 Text(
382 "Lower right half",
383 modifier = GlanceModifier.defaultWeight().width(50.dp).background(Color.Cyan)
384 )
385 }
386 }
387
388 mHostRule.startHost()
389
390 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "fill_top_level_size")
391 }
392
393 @Test
394 fun checkTopLevelWrap() {
395 TestGlanceAppWidget.uiDefinition = {
396 Column(
397 modifier = GlanceModifier.wrapContentSize().background(Color.DarkGray),
398 horizontalAlignment = Alignment.CenterHorizontally,
399 ) {
400 Text("Above", modifier = GlanceModifier.background(Color.Green))
401 Text("Larger below", modifier = GlanceModifier.background(Color.Cyan))
402 }
403 }
404
405 mHostRule.startHost()
406
407 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "wrap_top_level_size")
408 }
409
410 @Test
411 fun drawableBackground() {
412 TestGlanceAppWidget.uiDefinition = {
413 Box(
414 modifier = GlanceModifier.fillMaxSize().background(Color.Green).padding(8.dp),
415 contentAlignment = Alignment.Center
416 ) {
417 Text(
418 "Some useful text",
419 modifier =
420 GlanceModifier.fillMaxWidth()
421 .height(220.dp)
422 .background(ImageProvider(R.drawable.filled_oval))
423 )
424 }
425 }
426
427 mHostRule.startHost()
428
429 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "drawable_background")
430 }
431
432 @Test
433 fun drawableBackgroundWithColorAndAlpha() {
434 TestGlanceAppWidget.uiDefinition = {
435 Box(
436 modifier = GlanceModifier.fillMaxSize().background(Color.Green).padding(8.dp),
437 contentAlignment = Alignment.Center
438 ) {
439 Text(
440 text = "Some useful text",
441 modifier =
442 GlanceModifier.fillMaxWidth()
443 .height(220.dp)
444 .background(
445 ImageProvider(R.drawable.filled_oval),
446 colorFilter = ColorFilter.tint(GlanceTheme.colors.secondary),
447 alpha = 0.5f
448 )
449 )
450 }
451 }
452
453 mHostRule.startHost()
454
455 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "drawable_background_color_alpha")
456 }
457
458 @Test
459 fun drawableFitBackground() {
460 TestGlanceAppWidget.uiDefinition = {
461 Box(
462 modifier = GlanceModifier.fillMaxSize().background(Color.Green).padding(8.dp),
463 contentAlignment = Alignment.Center
464 ) {
465 Text(
466 "Some useful text",
467 modifier =
468 GlanceModifier.fillMaxWidth()
469 .height(220.dp)
470 .background(
471 ImageProvider(R.drawable.filled_oval),
472 contentScale = ContentScale.Fit
473 )
474 )
475 }
476 }
477
478 mHostRule.startHost()
479
480 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "drawable_fit_background")
481 }
482
483 @Test
484 fun bitmapBackground() {
485 TestGlanceAppWidget.uiDefinition = {
486 val context = LocalContext.current
487 val bitmap = context.resources.getDrawable(R.drawable.compose, null) as BitmapDrawable
488 Box(
489 modifier = GlanceModifier.fillMaxSize().background(Color.Green).padding(8.dp),
490 contentAlignment = Alignment.Center
491 ) {
492 Text(
493 "Some useful text",
494 modifier =
495 GlanceModifier.fillMaxWidth()
496 .height(220.dp)
497 .background(ImageProvider(bitmap.bitmap!!))
498 )
499 }
500 }
501
502 mHostRule.startHost()
503
504 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "bitmap_background")
505 }
506
507 @Test
508 fun alignment() {
509 TestGlanceAppWidget.uiDefinition = {
510 Row(
511 modifier = GlanceModifier.fillMaxSize(),
512 horizontalAlignment = Alignment.End,
513 verticalAlignment = Alignment.Bottom,
514 ) {
515 Text("##")
516 Column(
517 horizontalAlignment = Alignment.CenterHorizontally,
518 verticalAlignment = Alignment.CenterVertically,
519 modifier = GlanceModifier.fillMaxHeight(),
520 ) {
521 Box(
522 contentAlignment = Alignment.Center,
523 modifier = GlanceModifier.height(80.dp),
524 ) {
525 Text("Center")
526 }
527 Box(
528 contentAlignment = Alignment.Center,
529 modifier = GlanceModifier.height(80.dp),
530 ) {
531 Text("BottomCenter")
532 }
533 Box(
534 contentAlignment = Alignment.Center,
535 modifier = GlanceModifier.height(80.dp),
536 ) {
537 Text("CenterStart")
538 }
539 }
540 Column(
541 horizontalAlignment = Alignment.CenterHorizontally,
542 verticalAlignment = Alignment.CenterVertically,
543 modifier = GlanceModifier.fillMaxHeight(),
544 ) {
545 Box(contentAlignment = Alignment.Center) {
546 Image(
547 ImageProvider(R.drawable.compose),
548 "Compose",
549 modifier = GlanceModifier.size(80.dp),
550 )
551 Text("OXO", style = TextStyle(fontSize = 18.sp))
552 }
553 Box(contentAlignment = Alignment.BottomCenter) {
554 Image(
555 ImageProvider(R.drawable.compose),
556 "Compose",
557 modifier = GlanceModifier.size(80.dp),
558 )
559 Text("OXO", style = TextStyle(fontSize = 18.sp))
560 }
561 Box(contentAlignment = Alignment.CenterStart) {
562 Image(
563 ImageProvider(R.drawable.compose),
564 "Compose",
565 modifier = GlanceModifier.size(80.dp),
566 )
567 Text("OXO", style = TextStyle(fontSize = 18.sp))
568 }
569 }
570 }
571 }
572
573 mHostRule.startHost()
574
575 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "alignment")
576 }
577
578 @Test
579 fun lazyColumn_alignment_end() = runTest {
580 val count = 5
581 TestGlanceAppWidget.uiDefinition = {
582 LazyColumnAlignmentTest(count = count, horizontalAlignment = Alignment.End)
583 }
584
585 mHostRule.startHost()
586
587 mHostRule.waitForListViewChildCount(count)
588 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "lazyColumn_alignment_end")
589 }
590
591 @Test
592 fun lazyColumn_alignment_center() = runTest {
593 val count = 5
594 TestGlanceAppWidget.uiDefinition = {
595 LazyColumnAlignmentTest(
596 count = count,
597 horizontalAlignment = Alignment.CenterHorizontally
598 )
599 }
600
601 mHostRule.startHost()
602
603 mHostRule.waitForListViewChildCount(count)
604 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "lazyColumn_alignment_center")
605 }
606
607 @Test
608 fun lazyColumn_alignment_start() = runTest {
609 val count = 5
610 TestGlanceAppWidget.uiDefinition = {
611 LazyColumnAlignmentTest(count = count, horizontalAlignment = Alignment.Start)
612 }
613
614 mHostRule.startHost()
615
616 mHostRule.waitForListViewChildCount(count)
617 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "lazyColumn_alignment_start")
618 }
619
620 @Test
621 fun linearProgressIndicator_colors() {
622 TestGlanceAppWidget.uiDefinition = { LinearProgressIndicatorColorsTest() }
623
624 mHostRule.startHost()
625
626 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "linearProgressIndicator_colors")
627 }
628
629 @Test
630 fun buttonTests_createFilledButton() {
631 TestGlanceAppWidget.uiDefinition = { ButtonComponentsScreenshotTests.FilledButtonTest() }
632 mHostRule.startHost()
633 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "buttonTests_createFilledButton")
634 }
635
636 @Test
637 fun buttonTests_createOutlineButton() {
638 TestGlanceAppWidget.uiDefinition = { ButtonComponentsScreenshotTests.OutlineButtonTest() }
639 mHostRule.startHost()
640 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "buttonTests_createOutlineButton")
641 }
642
643 @Test
644 fun buttonTests_createSquareButton() {
645 TestGlanceAppWidget.uiDefinition = { ButtonComponentsScreenshotTests.SquareButtonTest() }
646 mHostRule.startHost()
647 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "buttonTests_createSquareButton")
648 }
649
650 @Test
651 fun buttonTests_createCircleButton() {
652 TestGlanceAppWidget.uiDefinition = { ButtonComponentsScreenshotTests.CircleButtonTest() }
653 mHostRule.startHost()
654 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "buttonTests_createCircleButton")
655 }
656
657 @Test
658 fun buttonTests_buttonDefaultColors() {
659 TestGlanceAppWidget.uiDefinition = {
660 ButtonComponentsScreenshotTests.ButtonDefaultColorsTest()
661 }
662 mHostRule.startHost()
663 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "buttonTests_buttonDefaultColors")
664 }
665
666 /**
667 * Button should ignore [androidx.glance.BackgroundModifier]. It does not support background
668 * images, and background color should be set via [androidx.glance.ButtonColors].
669 */
670 @Test
671 fun buttonTests_buttonShouldIgnoreBackgroundModifiers() {
672 @Composable
673 fun Ui() {
674 Column {
675 // the bg image modifier should be stripped.
676 Button(
677 text = "Button w/incorrect bg modifier: image",
678 onClick = {},
679 modifier =
680 GlanceModifier.background(
681 imageProvider = ImageProvider(R.drawable.compose),
682 ),
683 )
684 Spacer(GlanceModifier.size(4.dp))
685
686 // Bg modifiers should be stripped
687 Button(
688 text = "Button w/incorrect modifiers: Image, Color",
689 onClick = {},
690 modifier =
691 GlanceModifier.background(Color.Cyan)
692 .background(
693 imageProvider = ImageProvider(R.drawable.compose),
694 ),
695 )
696 Spacer(GlanceModifier.size(4.dp))
697
698 // Bg color modifier should be stripped.
699 Button(
700 text = "Button w/incorrect bg modifier: color",
701 onClick = {},
702 modifier = GlanceModifier.background(Color.Cyan),
703 )
704 Spacer(GlanceModifier.size(4.dp))
705
706 Button(
707 text = "Button with no modifier",
708 onClick = {},
709 )
710 Spacer(GlanceModifier.size(4.dp))
711 Button(
712 text = "Button with proper color",
713 colors =
714 ButtonDefaults.buttonColors(
715 backgroundColor = ColorProvider(Color.Cyan),
716 contentColor = ColorProvider(Color.Magenta)
717 ),
718 onClick = {},
719 )
720 }
721 }
722
723 TestGlanceAppWidget.uiDefinition = { Ui() }
724 mHostRule.startHost()
725 mScreenshotRule.checkScreenshot(
726 mHostRule.mHostView,
727 "buttonTests_buttonIgnoresBgImageModifier"
728 )
729 }
730
731 @Test
732 fun topbarTests_createBarWithIconTextTwoActions() {
733 TestGlanceAppWidget.uiDefinition = { TopBarUi() }
734 mHostRule.startHost()
735 mScreenshotRule.checkScreenshot(
736 mHostRule.mHostView,
737 "topbarTests_createBarWithIconTextTwoActions"
738 )
739 }
740
741 @WithRtl
742 @Test
743 fun topbarTests_createBarWithIconTextTwoActions_rtl() {
744 TestGlanceAppWidget.uiDefinition = { TopBarUi() }
745 mHostRule.startHost()
746 mScreenshotRule.checkScreenshot(
747 mHostRule.mHostView,
748 "topbarTests_createBarWithIconTextTwoActions_rtl"
749 )
750 }
751
752 @Composable
753 private fun TopBarUi() {
754 val fg = ColorProvider(Color.Magenta)
755 val bg = ColorProvider(Color.Cyan)
756
757 val actionButton =
758 @Composable {
759 CircleIconButton(
760 imageProvider = ImageProvider(R.drawable.filled_oval),
761 contentDescription = null,
762 backgroundColor = null,
763 contentColor = fg,
764 onClick = {}
765 )
766 }
767
768 Box(GlanceModifier.padding(4.dp).background(Color.Black)) {
769 TitleBar(
770 modifier = GlanceModifier.background(bg).fillMaxWidth(),
771 startIcon = ImageProvider(R.drawable.filled_oval),
772 title = "Lead icon; title; 2 btns",
773 textColor = fg,
774 iconColor = fg,
775 actions = {
776 actionButton()
777 Spacer(GlanceModifier.size(8.dp))
778 actionButton()
779 }
780 )
781 }
782 }
783
784 @Test
785 fun scaffoldTests_basicScaffold() {
786 TestGlanceAppWidget.uiDefinition = { ScaffoldTestUi.BasicScaffold() }
787 mHostRule.startHost()
788 mScreenshotRule.checkScreenshot(mHostRule.mHostView, "scaffoldTests_basicScaffold")
789 }
790
791 @Test
792 fun scaffoldTests_scaffoldWithPaddingOverride() {
793 TestGlanceAppWidget.uiDefinition = { ScaffoldTestUi.PaddedScaffold() }
794 mHostRule.startHost()
795 mScreenshotRule.checkScreenshot(
796 mHostRule.mHostView,
797 "scaffoldTests_scaffoldWithPaddingOverride"
798 )
799 }
800 }
801
802 private object ScaffoldTestUi {
803 private val fg = ColorProvider(Color.Magenta)
804 private val bg = ColorProvider(Color.Cyan)
805 private val widgetBg = ColorProvider(Color.Yellow)
806
807 @Composable
BasicScaffoldnull808 fun BasicScaffold() {
809 Scaffold(
810 titleBar = { ScaffoldTitleBar() },
811 modifier = GlanceModifier,
812 backgroundColor = widgetBg,
813 content = { ScaffoldContent() }
814 )
815 }
816
817 @Composable
PaddedScaffoldnull818 fun PaddedScaffold() {
819 Scaffold(
820 titleBar = { ScaffoldTitleBar() },
821 modifier = GlanceModifier,
822 backgroundColor = widgetBg,
823 horizontalPadding = 32.dp,
824 content = { ScaffoldContent() }
825 )
826 }
827
828 @Composable
ScaffoldTitleBarnull829 private fun ScaffoldTitleBar() {
830 TitleBar(
831 modifier = GlanceModifier.background(bg).fillMaxWidth(),
832 startIcon = ImageProvider(R.drawable.filled_oval),
833 title = "Lead icon; title; no btns",
834 textColor = fg,
835 iconColor = fg
836 )
837 }
838
839 @Composable
ScaffoldContentnull840 private fun ScaffoldContent() {
841 Text(
842 text = "text, fill-max-size\nbg cyan, fg magenta",
843 modifier = GlanceModifier.fillMaxSize()
844 )
845 }
846 }
847
848 @Composable
LazyColumnAlignmentTestnull849 private fun LazyColumnAlignmentTest(count: Int, horizontalAlignment: Alignment.Horizontal) {
850 val columnColors = listOf(Color(0xffffdbcd), Color(0xff7d2d00))
851 val textBgColors = listOf(Color(0xffa33e00), Color(0xffffb596))
852
853 LazyColumn(
854 modifier = GlanceModifier.fillMaxSize().background(columnColors[0], columnColors[1]),
855 horizontalAlignment = horizontalAlignment
856 ) {
857 items(count) { index ->
858 Text(
859 text = "item $index",
860 modifier =
861 GlanceModifier.padding(horizontal = 16.dp, vertical = 8.dp)
862 .background(textBgColors[0], textBgColors[1])
863 )
864 }
865 }
866 }
867
868 @Composable
TextAlignmentTestnull869 private fun TextAlignmentTest() {
870 Column(modifier = GlanceModifier.fillMaxWidth()) {
871 Text(
872 "Center",
873 style = TextStyle(textAlign = TextAlign.Center),
874 modifier = GlanceModifier.fillMaxWidth()
875 )
876 Text(
877 "Left",
878 style = TextStyle(textAlign = TextAlign.Left),
879 modifier = GlanceModifier.fillMaxWidth()
880 )
881 Text(
882 "Right",
883 style = TextStyle(textAlign = TextAlign.Right),
884 modifier = GlanceModifier.fillMaxWidth()
885 )
886 Text(
887 "Start",
888 style = TextStyle(textAlign = TextAlign.Start),
889 modifier = GlanceModifier.fillMaxWidth()
890 )
891 Text(
892 "End",
893 style = TextStyle(textAlign = TextAlign.End),
894 modifier = GlanceModifier.fillMaxWidth()
895 )
896 }
897 }
898
899 @Composable
RowTestnull900 private fun RowTest() {
901 Row(modifier = GlanceModifier.fillMaxWidth()) {
902 Text(
903 "Start",
904 style = TextStyle(textAlign = TextAlign.Start),
905 modifier = GlanceModifier.defaultWeight()
906 )
907 Text(
908 "Center",
909 style = TextStyle(textAlign = TextAlign.Center),
910 modifier = GlanceModifier.defaultWeight()
911 )
912 Text(
913 "End",
914 style = TextStyle(textAlign = TextAlign.End),
915 modifier = GlanceModifier.defaultWeight()
916 )
917 }
918 }
919
920 @Composable
BackgroundTestnull921 private fun BackgroundTest() {
922 Column(modifier = GlanceModifier.background(R.color.background_color)) {
923 Text(
924 "100x50 and cyan",
925 modifier = GlanceModifier.width(100.dp).height(50.dp).background(Color.Cyan)
926 )
927 Text(
928 "Transparent background",
929 modifier = GlanceModifier.height(50.dp).background(Color.Transparent)
930 )
931 Text(
932 "wrapx30 and red (light), yellow (dark)",
933 modifier =
934 GlanceModifier.height(30.dp).background(day = Color.Red, night = Color.Yellow)
935 )
936 Text("Below this should be 4 color boxes")
937 Row(modifier = GlanceModifier.padding(8.dp)) {
938 Box(
939 modifier =
940 GlanceModifier.width(32.dp)
941 .height(32.dp)
942 .background(day = Color.Black, night = Color.White)
943 ) {}
944 val colors = listOf(Color.Red, Color.Green, Color.Blue)
945 repeat(3) {
946 Box(modifier = GlanceModifier.width(8.dp).height(1.dp)) {}
947 Box(modifier = GlanceModifier.width(32.dp).height(32.dp).background(colors[it])) {}
948 }
949 }
950 }
951 }
952
953 @Composable
TextColorTestnull954 private fun TextColorTest() {
955 Column(modifier = GlanceModifier.background(R.color.background_color)) {
956 Text("Cyan", style = TextStyle(color = ColorProvider(Color.Cyan)))
957 Text(
958 "Red (light) or yellow (dark)",
959 style = TextStyle(color = ColorProvider(day = Color.Red, night = Color.Yellow))
960 )
961 Text(
962 "Resource (inverse of background color)",
963 style = TextStyle(color = ColorProvider(R.color.text_color))
964 )
965 }
966 }
967
968 @Composable
RoundedButtonScreenshotTestnull969 private fun RoundedButtonScreenshotTest() {
970 val columnColors = listOf(Color(0xffffdbcd), Color(0xff7d2d00))
971 val buttonBgColors = listOf(Color(0xffa33e00), Color(0xffffb596))
972 val buttonTextColors = listOf(Color(0xffffffff), Color(0xff581e00))
973
974 Column(
975 modifier =
976 GlanceModifier.padding(10.dp).background(day = columnColors[0], night = columnColors[1])
977 ) {
978 Button(
979 "Button with textAlign = Start",
980 onClick = actionStartActivity<Activity>(),
981 colors =
982 ButtonDefaults.buttonColors(
983 backgroundColor =
984 ColorProvider(day = buttonBgColors[0], night = buttonBgColors[1]),
985 contentColor =
986 ColorProvider(day = buttonTextColors[0], night = buttonTextColors[1])
987 ),
988 style = TextStyle(textAlign = TextAlign.Start)
989 )
990 Spacer(modifier = GlanceModifier.height(5.dp).fillMaxWidth())
991 Button(
992 "Button with textAlign = Center and padding (30dp, 30dp)",
993 onClick = actionStartActivity<Activity>(),
994 modifier = GlanceModifier.padding(horizontal = 30.dp, vertical = 30.dp),
995 colors =
996 ButtonDefaults.buttonColors(
997 backgroundColor =
998 ColorProvider(day = buttonBgColors[0], night = buttonBgColors[1]),
999 contentColor =
1000 ColorProvider(day = buttonTextColors[0], night = buttonTextColors[1])
1001 ),
1002 style = TextStyle(textAlign = TextAlign.Center)
1003 )
1004 Spacer(modifier = GlanceModifier.height(5.dp).fillMaxWidth())
1005 Button(
1006 "Button with textAlign = End",
1007 onClick = actionStartActivity<Activity>(),
1008 colors =
1009 ButtonDefaults.buttonColors(
1010 backgroundColor =
1011 ColorProvider(day = buttonBgColors[0], night = buttonBgColors[1]),
1012 contentColor =
1013 ColorProvider(day = buttonTextColors[0], night = buttonTextColors[1])
1014 ),
1015 style = TextStyle(textAlign = TextAlign.End)
1016 )
1017 }
1018 }
1019
1020 @Composable
CheckBoxScreenshotTestnull1021 private fun CheckBoxScreenshotTest() {
1022 Column(modifier = GlanceModifier.background(day = Color.White, night = Color.Black)) {
1023 CheckBox(
1024 checked = true,
1025 onCheckedChange = null,
1026 text =
1027 "Hello Checked Checkbox (text: day=black, night=white| box: day=magenta, " +
1028 "night=yellow)",
1029 style =
1030 TextStyle(
1031 color = ColorProvider(day = Color.Black, night = Color.White),
1032 fontWeight = FontWeight.Bold,
1033 fontStyle = FontStyle.Normal,
1034 ),
1035 colors =
1036 CheckboxDefaults.colors(
1037 checkedColor = ColorProvider(day = Color.Magenta, night = Color.Yellow),
1038 uncheckedColor = ColorProvider(day = Color.Black, night = Color.Gray)
1039 )
1040 )
1041
1042 CheckBox(
1043 checked = false,
1044 onCheckedChange = null,
1045 text = "Hello Unchecked Checkbox (text: day=dark gray, night=light gray, green box)",
1046 style =
1047 TextStyle(
1048 color = ColorProvider(day = Color.DarkGray, night = Color.LightGray),
1049 textDecoration = TextDecoration.Underline,
1050 fontWeight = FontWeight.Medium,
1051 fontStyle = FontStyle.Italic,
1052 ),
1053 colors = CheckboxDefaults.colors(checkedColor = Color.Red, uncheckedColor = Color.Green)
1054 )
1055 }
1056 }
1057
1058 @Composable
SwitchTestnull1059 private fun SwitchTest() {
1060 Column(modifier = GlanceModifier.background(day = Color.White, night = Color.Black)) {
1061 Switch(
1062 checked = true,
1063 onCheckedChange = null,
1064 text = "Hello Checked Switch (day: Blue/Green, night: Red/Yellow)",
1065 style =
1066 TextStyle(
1067 color = ColorProvider(day = Color.Black, night = Color.White),
1068 fontWeight = FontWeight.Bold,
1069 fontStyle = FontStyle.Normal,
1070 ),
1071 colors =
1072 SwitchDefaults.colors(
1073 checkedThumbColor = ColorProvider(day = Color.Blue, night = Color.Red),
1074 uncheckedThumbColor = ColorProvider(Color.Magenta),
1075 checkedTrackColor = ColorProvider(day = Color.Green, night = Color.Yellow),
1076 uncheckedTrackColor = ColorProvider(Color.Magenta)
1077 )
1078 )
1079
1080 Switch(
1081 checked = false,
1082 onCheckedChange = null,
1083 text = "Hello Unchecked Switch. day: thumb magenta / track cyan, night: thumb cyan",
1084 style =
1085 TextStyle(
1086 color = ColorProvider(day = Color.Black, night = Color.White),
1087 textDecoration = TextDecoration.Underline,
1088 fontWeight = FontWeight.Medium,
1089 fontStyle = FontStyle.Italic,
1090 ),
1091 colors =
1092 SwitchDefaults.colors(
1093 checkedThumbColor = ColorProvider(Color.Blue),
1094 uncheckedThumbColor = ColorProvider(day = Color.Magenta, night = Color.Cyan),
1095 checkedTrackColor = ColorProvider(Color.Blue),
1096 uncheckedTrackColor = ColorProvider(day = Color.Cyan, night = Color.Magenta)
1097 )
1098 )
1099 }
1100 }
1101
1102 @Composable
RadioButtonScreenshotTestnull1103 private fun RadioButtonScreenshotTest() {
1104 Column(modifier = GlanceModifier.background(day = Color.White, night = Color.Black)) {
1105 RadioButton(
1106 checked = true,
1107 onClick = null,
1108 text =
1109 "Hello Checked Radio (text: day=black, night=white| radio: day=magenta, " +
1110 "night=yellow)",
1111 style =
1112 TextStyle(
1113 color = ColorProvider(day = Color.Black, night = Color.White),
1114 fontWeight = FontWeight.Bold,
1115 fontStyle = FontStyle.Normal,
1116 ),
1117 colors =
1118 RadioButtonDefaults.colors(
1119 checkedColor = ColorProvider(day = Color.Magenta, night = Color.Yellow),
1120 uncheckedColor = ColorProvider(day = Color.Yellow, night = Color.Magenta)
1121 )
1122 )
1123
1124 RadioButton(
1125 checked = false,
1126 onClick = null,
1127 text = "Hello Unchecked Radio (text: day=dark gray, night=light gray| radio: green)",
1128 style =
1129 TextStyle(
1130 color = ColorProvider(day = Color.DarkGray, night = Color.LightGray),
1131 textDecoration = TextDecoration.Underline,
1132 fontWeight = FontWeight.Medium,
1133 fontStyle = FontStyle.Italic,
1134 ),
1135 colors =
1136 RadioButtonDefaults.colors(checkedColor = Color.Red, uncheckedColor = Color.Green)
1137 )
1138 }
1139 }
1140
1141 @Composable
LinearProgressIndicatorColorsTestnull1142 private fun LinearProgressIndicatorColorsTest() {
1143 Column(
1144 modifier =
1145 GlanceModifier.padding(16.dp)
1146 .background(colorProvider = GlanceTheme.colors.widgetBackground)
1147 ) {
1148 Text("Default colors")
1149 LinearProgressIndicator(progress = 0.5f)
1150 Spacer(modifier = GlanceModifier.height(8.dp))
1151 Text("Colors set to theme")
1152 LinearProgressIndicator(
1153 progress = 0.5f,
1154 color = GlanceTheme.colors.onTertiary,
1155 backgroundColor = GlanceTheme.colors.tertiary
1156 )
1157 Spacer(modifier = GlanceModifier.height(8.dp))
1158 Text("Fixed colors")
1159 LinearProgressIndicator(
1160 progress = 0.5f,
1161 color = ColorProvider(Color.Green),
1162 backgroundColor = ColorProvider(Color.Gray)
1163 )
1164 }
1165 }
1166
1167 // Tests the opinionated button components
1168 private object ButtonComponentsScreenshotTests {
1169
<lambda>null1170 private val onClick: () -> Unit = {}
1171
1172 // a filled square
1173 private val icon = ImageProvider(R.drawable.filled_oval)
1174
1175 private val buttonBg = ColorProvider(Color.Black)
1176 private val buttonFg = ColorProvider(Color.White)
1177
1178 @Composable
colorsnull1179 private fun colors() =
1180 ButtonDefaults.buttonColors(backgroundColor = buttonBg, contentColor = buttonFg)
1181
1182 @Composable private fun Space() = Spacer(GlanceModifier.size(16.dp))
1183
1184 /** A rectangular magenta background */
1185 @Composable
1186 private fun Background(content: @Composable () -> Unit) {
1187 Box(
1188 modifier = GlanceModifier.wrapContentSize().padding(16.dp).background(Color.Magenta),
1189 content = content
1190 )
1191 }
1192
1193 @Composable
FilledButtonTestnull1194 fun FilledButtonTest() {
1195 Background {
1196 Column {
1197 FilledButton(
1198 text = "Filled button\nbg 0x00, fg 0xff",
1199 onClick = onClick,
1200 icon = null,
1201 colors = colors()
1202 )
1203 Space()
1204 FilledButton(
1205 text = "Filled btn + icon\nbg 0x00, fg 0xff",
1206 onClick = onClick,
1207 icon = icon,
1208 colors = colors()
1209 )
1210 }
1211 }
1212 }
1213
1214 @Composable
OutlineButtonTestnull1215 fun OutlineButtonTest() {
1216 Background {
1217 Column {
1218 OutlineButton(
1219 text = "Outline Button\nfg 0xff",
1220 onClick = onClick,
1221 icon = null,
1222 contentColor = buttonFg
1223 )
1224 Space()
1225 OutlineButton(
1226 text = "Outline btn + icon\nfg 0xff",
1227 onClick = onClick,
1228 icon = icon,
1229 contentColor = buttonFg
1230 )
1231 }
1232 }
1233 }
1234
1235 @Composable
SquareButtonTestnull1236 fun SquareButtonTest() {
1237 Background {
1238 // square button with rounded corners, icon (a square w/sharp corners) in center.
1239 SquareIconButton(
1240 imageProvider = icon,
1241 contentDescription = null,
1242 onClick = onClick,
1243 backgroundColor = buttonBg,
1244 contentColor = buttonFg
1245 )
1246 }
1247 }
1248
1249 @Composable
CircleButtonTestnull1250 fun CircleButtonTest() {
1251 Background {
1252 Column {
1253 // Circle button with icon
1254 CircleIconButton(
1255 imageProvider = icon,
1256 contentDescription = null,
1257 onClick = onClick,
1258 backgroundColor = buttonBg,
1259 contentColor = buttonFg
1260 )
1261 Space()
1262 // Icon only, no background
1263 CircleIconButton(
1264 imageProvider = icon,
1265 contentDescription = null,
1266 onClick = onClick,
1267 backgroundColor = null,
1268 contentColor = buttonFg
1269 )
1270 }
1271 }
1272 }
1273
1274 /** Tests that buttons inherit the expected colors from their theme. */
1275 @Composable
ButtonDefaultColorsTestnull1276 fun ButtonDefaultColorsTest() {
1277 val unused = ColorProvider(Color.Cyan)
1278
1279 val colors =
1280 colorProviders(
1281 primary = ColorProvider(Color.Green),
1282 onPrimary = ColorProvider(Color.Black),
1283 surface = ColorProvider(Color.Gray),
1284 onSurface = ColorProvider(Color.Red),
1285 background = ColorProvider(Color.DarkGray),
1286 error = unused,
1287 errorContainer = unused,
1288 inverseOnSurface = unused,
1289 inversePrimary = unused,
1290 inverseSurface = unused,
1291 onBackground = unused,
1292 onError = unused,
1293 onErrorContainer = unused,
1294 onPrimaryContainer = unused,
1295 onSecondary = unused,
1296 onSecondaryContainer = unused,
1297 onSurfaceVariant = unused,
1298 onTertiary = unused,
1299 onTertiaryContainer = unused,
1300 outline = unused,
1301 primaryContainer = unused,
1302 secondary = unused,
1303 secondaryContainer = unused,
1304 surfaceVariant = unused,
1305 tertiary = unused,
1306 tertiaryContainer = unused,
1307 )
1308
1309 GlanceTheme(colors = colors) {
1310 Column {
1311 FilledButton("Filled button", icon = icon, onClick = onClick)
1312 // [OutlineButton] does not have a default color, so not important to test here
1313 Space()
1314 SquareIconButton(imageProvider = icon, contentDescription = null, onClick = onClick)
1315 Space()
1316 CircleIconButton(imageProvider = icon, contentDescription = null, onClick = onClick)
1317 }
1318 }
1319 }
1320 }
1321