1 /*
2 * Copyright 2024 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.wear.protolayout.material3.samples
18
19 import android.content.Context
20 import androidx.annotation.Sampled
21 import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters
22 import androidx.wear.protolayout.DimensionBuilders.dp
23 import androidx.wear.protolayout.DimensionBuilders.expand
24 import androidx.wear.protolayout.DimensionBuilders.weight
25 import androidx.wear.protolayout.LayoutElementBuilders
26 import androidx.wear.protolayout.LayoutElementBuilders.Box
27 import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement
28 import androidx.wear.protolayout.ModifiersBuilders
29 import androidx.wear.protolayout.ModifiersBuilders.Clickable
30 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
31 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
32 import androidx.wear.protolayout.material3.AppCardStyle
33 import androidx.wear.protolayout.material3.CardDefaults.filledTonalCardColors
34 import androidx.wear.protolayout.material3.CardDefaults.filledVariantCardColors
35 import androidx.wear.protolayout.material3.CircularProgressIndicatorDefaults
36 import androidx.wear.protolayout.material3.CircularProgressIndicatorDefaults.filledTonalProgressIndicatorColors
37 import androidx.wear.protolayout.material3.CircularProgressIndicatorDefaults.filledVariantProgressIndicatorColors
38 import androidx.wear.protolayout.material3.DataCardStyle.Companion.extraLargeDataCardStyle
39 import androidx.wear.protolayout.material3.DataCardStyle.Companion.largeCompactDataCardStyle
40 import androidx.wear.protolayout.material3.GraphicDataCardStyle.Companion.largeGraphicDataCardStyle
41 import androidx.wear.protolayout.material3.MaterialScope
42 import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.MAX_PRIMARY_LAYOUT_MARGIN
43 import androidx.wear.protolayout.material3.TitleCardStyle.Companion.largeTitleCardStyle
44 import androidx.wear.protolayout.material3.Typography
45 import androidx.wear.protolayout.material3.appCard
46 import androidx.wear.protolayout.material3.avatarButton
47 import androidx.wear.protolayout.material3.avatarImage
48 import androidx.wear.protolayout.material3.backgroundImage
49 import androidx.wear.protolayout.material3.button
50 import androidx.wear.protolayout.material3.buttonGroup
51 import androidx.wear.protolayout.material3.card
52 import androidx.wear.protolayout.material3.circularProgressIndicator
53 import androidx.wear.protolayout.material3.compactButton
54 import androidx.wear.protolayout.material3.graphicDataCard
55 import androidx.wear.protolayout.material3.icon
56 import androidx.wear.protolayout.material3.iconButton
57 import androidx.wear.protolayout.material3.iconDataCard
58 import androidx.wear.protolayout.material3.iconEdgeButton
59 import androidx.wear.protolayout.material3.imageButton
60 import androidx.wear.protolayout.material3.materialScope
61 import androidx.wear.protolayout.material3.primaryLayout
62 import androidx.wear.protolayout.material3.segmentedCircularProgressIndicator
63 import androidx.wear.protolayout.material3.text
64 import androidx.wear.protolayout.material3.textButton
65 import androidx.wear.protolayout.material3.textDataCard
66 import androidx.wear.protolayout.material3.textEdgeButton
67 import androidx.wear.protolayout.material3.titleCard
68 import androidx.wear.protolayout.modifiers.LayoutModifier
69 import androidx.wear.protolayout.modifiers.background
70 import androidx.wear.protolayout.modifiers.clickable
71 import androidx.wear.protolayout.modifiers.contentDescription
72 import androidx.wear.protolayout.types.LayoutString
73 import androidx.wear.protolayout.types.asLayoutConstraint
74 import androidx.wear.protolayout.types.layoutString
75
76 /** Builds Material3 text element with default options. */
77 @Sampled
helloWorldTextDefaultnull78 fun helloWorldTextDefault(context: Context, deviceConfiguration: DeviceParameters): LayoutElement =
79 materialScope(context, deviceConfiguration) {
80 text(text = "Hello Material3".layoutString, typography = Typography.DISPLAY_LARGE)
81 }
82
83 /** Builds Material3 text element with some of the overridden defaults. */
84 @Sampled
helloWorldTextDynamicCustomnull85 fun helloWorldTextDynamicCustom(
86 context: Context,
87 deviceConfiguration: DeviceParameters
88 ): LayoutElement =
89 materialScope(context, deviceConfiguration) {
90 this.text(
91 text =
92 LayoutString(
93 "Static",
94 DynamicString.constant("Dynamic"),
95 "LongestConstraint".asLayoutConstraint()
96 ),
97 typography = Typography.DISPLAY_LARGE,
98 color = colorScheme.tertiary,
99 underline = true,
100 maxLines = 5
101 )
102 }
103
104 @Sampled
edgeButtonSampleIconnull105 fun edgeButtonSampleIcon(
106 context: Context,
107 deviceConfiguration: DeviceParameters,
108 clickable: Clickable
109 ): LayoutElement =
110 materialScope(context, deviceConfiguration) {
111 iconEdgeButton(
112 onClick = clickable,
113 modifier = LayoutModifier.contentDescription("Description of a button")
114 ) {
115 icon(protoLayoutResourceId = "id")
116 }
117 }
118
119 @Sampled
edgeButtonSampleTextnull120 fun edgeButtonSampleText(
121 context: Context,
122 deviceConfiguration: DeviceParameters,
123 clickable: Clickable
124 ): LayoutElement =
125 materialScope(context, deviceConfiguration) {
126 textEdgeButton(
127 onClick = clickable,
128 modifier = LayoutModifier.contentDescription("Description of a button")
129 ) {
130 text("Hello".layoutString)
131 }
132 }
133
134 @Sampled
topLevelLayoutnull135 fun topLevelLayout(
136 context: Context,
137 deviceConfiguration: DeviceParameters,
138 clickable: Clickable
139 ): LayoutElement =
140 materialScope(context, deviceConfiguration) {
141 primaryLayout(
142 titleSlot = { text("App title".layoutString) },
143 mainSlot = {
144 buttonGroup {
145 // To be populated with proper components
146 buttonGroupItem {
147 LayoutElementBuilders.Box.Builder()
148 .setModifiers(
149 ModifiersBuilders.Modifiers.Builder()
150 .setBackground(
151 ModifiersBuilders.Background.Builder()
152 .setCorner(shapes.small)
153 .build()
154 )
155 .build()
156 )
157 .build()
158 }
159 }
160 },
161 // Adjust margins as the corner of the inner content is on the square side.
162 margins = MAX_PRIMARY_LAYOUT_MARGIN,
163 bottomSlot = {
164 iconEdgeButton(
165 onClick = clickable,
166 modifier = LayoutModifier.contentDescription("Description")
167 ) {
168 icon("id")
169 }
170 }
171 )
172 }
173
174 @Sampled
cardSamplenull175 fun cardSample(
176 context: Context,
177 deviceConfiguration: DeviceParameters,
178 clickable: Clickable
179 ): LayoutElement =
180 materialScope(context, deviceConfiguration) {
181 primaryLayout(
182 mainSlot = {
183 card(
184 onClick = clickable,
185 modifier =
186 LayoutModifier.contentDescription("Card with image background")
187 .clickable(id = "card"),
188 width = expand(),
189 height = expand(),
190 backgroundContent = { backgroundImage(protoLayoutResourceId = "id") }
191 ) {
192 text("Content of the Card!".layoutString)
193 }
194 }
195 )
196 }
197
198 @Sampled
titleCardSamplenull199 fun titleCardSample(
200 context: Context,
201 deviceConfiguration: DeviceParameters,
202 clickable: Clickable
203 ): LayoutElement =
204 materialScope(context, deviceConfiguration) {
205 primaryLayout(
206 mainSlot = {
207 titleCard(
208 onClick = clickable,
209 modifier = LayoutModifier.contentDescription("Title Card"),
210 height = expand(),
211 colors = filledVariantCardColors(),
212 style = largeTitleCardStyle(),
213 title = { text("This is title of the title card".layoutString) },
214 time = { text("NOW".layoutString) },
215 content = { text("Content of the Card!".layoutString) }
216 )
217 }
218 )
219 }
220
221 @Sampled
appCardSamplenull222 fun appCardSample(
223 context: Context,
224 deviceConfiguration: DeviceParameters,
225 clickable: Clickable
226 ): LayoutElement =
227 materialScope(context, deviceConfiguration) {
228 primaryLayout(
229 mainSlot = {
230 appCard(
231 onClick = clickable,
232 modifier = LayoutModifier.contentDescription("App Card"),
233 height = expand(),
234 colors = filledTonalCardColors(),
235 style = AppCardStyle.largeAppCardStyle(),
236 title = { text("This is title of the app card".layoutString) },
237 time = { text("NOW".layoutString) },
238 label = { text("Label".layoutString) },
239 content = { text("Content of the Card!".layoutString) },
240 )
241 }
242 )
243 }
244
245 @Sampled
dataCardSamplenull246 fun dataCardSample(
247 context: Context,
248 deviceConfiguration: DeviceParameters,
249 clickable: Clickable
250 ): LayoutElement =
251 materialScope(context, deviceConfiguration) {
252 primaryLayout(
253 mainSlot = {
254 buttonGroup {
255 buttonGroupItem {
256 textDataCard(
257 onClick = clickable,
258 modifier = LayoutModifier.contentDescription("Data Card with text"),
259 width = weight(1f),
260 height = expand(),
261 colors = filledTonalCardColors(),
262 style = extraLargeDataCardStyle(),
263 title = { this.text("1km".layoutString) },
264 content = { this.text("Run".layoutString) },
265 secondaryText = { this.text("Nice!".layoutString) }
266 )
267 }
268 buttonGroupItem {
269 iconDataCard(
270 onClick = clickable,
271 modifier = LayoutModifier.contentDescription("Data Card with icon"),
272 width = weight(1f),
273 height = expand(),
274 colors = filledTonalCardColors(),
275 style = extraLargeDataCardStyle(),
276 title = { this.text("2km".layoutString) },
277 secondaryIcon = { icon("id") },
278 content = { this.text("Run".layoutString) },
279 )
280 }
281 buttonGroupItem {
282 textDataCard(
283 onClick = clickable,
284 modifier =
285 LayoutModifier.contentDescription(
286 "Compact Data Card without icon or secondary label"
287 ),
288 width = weight(3f),
289 height = expand(),
290 colors = filledVariantCardColors(),
291 style = largeCompactDataCardStyle(),
292 title = { this.text("10:30".layoutString) },
293 content = { this.text("PM".layoutString) },
294 )
295 }
296 }
297 }
298 )
299 }
300
301 @Sampled
graphicDataCardSamplenull302 fun graphicDataCardSample(
303 context: Context,
304 deviceConfiguration: DeviceParameters,
305 clickable: Clickable
306 ): LayoutElement =
307 materialScope(context, deviceConfiguration) {
308 primaryLayout(
309 mainSlot = {
310 graphicDataCard(
311 onClick = clickable,
312 modifier = LayoutModifier.contentDescription("Data Card with graphic"),
313 height = expand(),
314 colors = filledVariantCardColors(),
315 style = largeGraphicDataCardStyle(),
316 title = { text("1,234".layoutString) },
317 content = { icon("steps") },
318 graphic = {
319 segmentedCircularProgressIndicator(
320 segmentCount = 5,
321 staticProgress = 0.5F,
322 colors = filledTonalProgressIndicatorColors(),
323 )
324 },
325 )
326 }
327 )
328 }
329
330 @Sampled
customButtonSamplenull331 fun customButtonSample(
332 context: Context,
333 deviceConfiguration: DeviceParameters,
334 clickable: Clickable
335 ): LayoutElement =
336 materialScope(context, deviceConfiguration) {
337 primaryLayout(
338 mainSlot = {
339 // Button with custom content inside
340 button(
341 onClick = clickable,
342 modifier =
343 LayoutModifier.contentDescription("Big button with image background")
344 .background(colorScheme.primary),
345 width = expand(),
346 height = expand(),
347 labelContent = {
348 // This can be further built.
349 Box.Builder().build()
350 }
351 )
352 }
353 )
354 }
355
356 @Sampled
oneSlotButtonsSamplenull357 fun oneSlotButtonsSample(
358 context: Context,
359 deviceConfiguration: DeviceParameters,
360 clickable: Clickable
361 ): LayoutElement =
362 materialScope(context, deviceConfiguration) {
363 primaryLayout(
364 mainSlot = {
365 buttonGroup {
366 buttonGroupItem {
367 iconButton(
368 onClick = clickable,
369 modifier =
370 LayoutModifier.contentDescription(
371 "Big button with image background"
372 ),
373 width = expand(),
374 height = expand(),
375 iconContent = { icon("id1") }
376 )
377 }
378 buttonGroupItem {
379 iconButton(
380 onClick = clickable,
381 modifier =
382 LayoutModifier.contentDescription(
383 "Big button with image background"
384 ),
385 width = expand(),
386 height = expand(),
387 shape = shapes.large,
388 iconContent = { icon("id2") }
389 )
390 }
391 buttonGroupItem {
392 textButton(
393 onClick = clickable,
394 modifier =
395 LayoutModifier.contentDescription(
396 "Big button with image background"
397 ),
398 width = expand(),
399 height = expand(),
400 shape = shapes.large,
401 labelContent = { text("Dec".layoutString) }
402 )
403 }
404 }
405 }
406 )
407 }
408
409 @Sampled
imageButtonSamplenull410 fun imageButtonSample(
411 context: Context,
412 deviceConfiguration: DeviceParameters,
413 clickable: Clickable
414 ): LayoutElement =
415 materialScope(context, deviceConfiguration) {
416 primaryLayout(
417 mainSlot = {
418 imageButton(
419 onClick = clickable,
420 modifier =
421 LayoutModifier.contentDescription("Big button with image background"),
422 width = expand(),
423 height = expand(),
424 backgroundContent = { backgroundImage(protoLayoutResourceId = "id") }
425 )
426 }
427 )
428 }
429
430 @Sampled
singleSegmentCircularProgressIndicatornull431 fun singleSegmentCircularProgressIndicator(
432 context: Context,
433 deviceParameters: DeviceParameters,
434 ): LayoutElement =
435 materialScope(context, deviceParameters) {
436 circularProgressIndicator(
437 dynamicProgress =
438 DynamicFloat.animate(
439 0.0F,
440 1.1F,
441 CircularProgressIndicatorDefaults.recommendedAnimationSpec
442 ),
443 startAngleDegrees = 200F,
444 endAngleDegrees = 520F,
445 colors = filledVariantProgressIndicatorColors(),
446 size = dp(85F)
447 )
448 }
449
450 @Sampled
pillShapeButtonsSamplenull451 fun pillShapeButtonsSample(
452 context: Context,
453 deviceConfiguration: DeviceParameters,
454 clickable: Clickable
455 ): LayoutElement =
456 materialScope(context, deviceConfiguration) {
457 primaryLayout(
458 mainSlot = {
459 button(
460 onClick = clickable,
461 modifier = LayoutModifier.contentDescription("Pill shape button"),
462 width = expand(),
463 height = expand(),
464 labelContent = { text("First label".layoutString) },
465 secondaryLabelContent = { text("Second label".layoutString) },
466 iconContent = { icon("id") }
467 )
468 }
469 )
470 }
471
472 @Sampled
avatarButtonSamplenull473 fun MaterialScope.avatarButtonSample() =
474 avatarButton(
475 onClick = clickable(),
476 modifier = LayoutModifier.contentDescription("Pill button"),
477 avatarContent = { avatarImage("id") },
<lambda>null478 labelContent = { text("Primary label".layoutString) },
<lambda>null479 secondaryLabelContent = { text("Secondary label".layoutString) },
480 )
481
482 @Sampled
compactButtonsSamplenull483 fun compactButtonsSample(
484 context: Context,
485 deviceConfiguration: DeviceParameters,
486 clickable: Clickable
487 ): LayoutElement =
488 materialScope(context, deviceConfiguration) {
489 primaryLayout(
490 mainSlot = {
491 compactButton(
492 onClick = clickable,
493 modifier = LayoutModifier.contentDescription("Compact button"),
494 width = expand(),
495 labelContent = { text("Action".layoutString) },
496 iconContent = { icon("id") }
497 )
498 }
499 )
500 }
501
502 @Sampled
multipleSegmentsCircularProgressIndicatornull503 fun multipleSegmentsCircularProgressIndicator(
504 context: Context,
505 deviceParameters: DeviceParameters,
506 ): LayoutElement =
507 materialScope(context, deviceParameters) {
508 segmentedCircularProgressIndicator(
509 segmentCount = 5,
510 dynamicProgress =
511 DynamicFloat.animate(
512 0.0F,
513 1.1F,
514 CircularProgressIndicatorDefaults.recommendedAnimationSpec
515 ),
516 startAngleDegrees = 200F,
517 endAngleDegrees = 520F,
518 colors = filledVariantProgressIndicatorColors(),
519 size = dp(85F)
520 )
521 }
522