1 /*
<lambda>null2  * Copyright 2023 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 @file:OptIn(ExperimentalMotionApi::class)
18 
19 package androidx.constraintlayout.compose.integration.macrobenchmark.target.graphs
20 
21 import androidx.compose.animation.core.Animatable
22 import androidx.compose.animation.core.tween
23 import androidx.compose.foundation.background
24 import androidx.compose.foundation.clickable
25 import androidx.compose.foundation.layout.Box
26 import androidx.compose.foundation.layout.fillMaxSize
27 import androidx.compose.foundation.layout.height
28 import androidx.compose.foundation.layout.padding
29 import androidx.compose.foundation.lazy.LazyColumn
30 import androidx.compose.foundation.shape.RoundedCornerShape
31 import androidx.compose.runtime.Composable
32 import androidx.compose.runtime.LaunchedEffect
33 import androidx.compose.runtime.getValue
34 import androidx.compose.runtime.mutableStateOf
35 import androidx.compose.runtime.remember
36 import androidx.compose.runtime.setValue
37 import androidx.compose.ui.Modifier
38 import androidx.compose.ui.draw.clip
39 import androidx.compose.ui.graphics.Color
40 import androidx.compose.ui.layout.layoutId
41 import androidx.compose.ui.platform.testTag
42 import androidx.compose.ui.tooling.preview.Preview
43 import androidx.compose.ui.unit.dp
44 import androidx.constraintlayout.compose.Dimension
45 import androidx.constraintlayout.compose.ExperimentalMotionApi
46 import androidx.constraintlayout.compose.MotionLayout
47 import androidx.constraintlayout.compose.MotionScene
48 import kotlin.random.Random
49 
50 /**
51  * Shows how to use MotionLayout to have animated graphs in a LazyColumn, where each graph is
52  * animated as it's revealed.
53  *
54  * Demonstrates how to dynamically create constraints based on input. See [DynamicGraph]. Where
55  * constraints are created to lay out the given values into a single graph layout.
56  */
57 @Preview(group = "scroll", device = "spec:width=480dp,height=800dp,dpi=440")
58 @Composable
59 fun DynamicGraphsPreview(modifier: Modifier = Modifier) {
60     val graphs = remember {
61         val list = mutableListOf<List<Float>>()
62         repeat(300) {
63             val values = FloatArray(10) { Random.nextInt(100).toFloat() + 10f }.asList()
64             list.add(values)
65         }
66         return@remember list
67     }
68     LazyColumn(modifier.testTag("LazyColumn")) {
69         items(graphs.size) {
70             Box(modifier = Modifier.padding(3.dp).height(200.dp)) { DynamicGraph(graphs[it]) }
71         }
72     }
73 }
74 
75 @Preview(group = "scroll", device = "spec:width=480dp,height=800dp,dpi=440")
76 @Composable
DynamicGraphnull77 private fun DynamicGraph(
78     values: List<Float> = listOf<Float>(12f, 32f, 21f, 32f, 2f),
79     max: Int = 100
80 ) {
81     val scene = remember {
82         val scale = values.map { (it * 0.8f) / max }
83         val count = values.size
84         val widthPercent = 1 / (count * 2f)
85 
86         MotionScene {
87             val cols = Array(count) { i -> createRefFor("foo$i") }
88             val start1 = constraintSet {
89                 createHorizontalChain(elements = cols)
90                 cols.forEach {
91                     constrain(it) {
92                         width = Dimension.percent(widthPercent)
93                         height = Dimension.value(1.dp)
94                         bottom.linkTo(parent.bottom, 16.dp)
95                     }
96                 }
97             }
98 
99             val end1 = constraintSet {
100                 createHorizontalChain(elements = cols)
101                 cols.forEachIndexed { i, col ->
102                     constrain(col) {
103                         width = Dimension.percent(widthPercent)
104                         height = Dimension.percent(scale[i])
105                         bottom.linkTo(parent.bottom, 16.dp)
106                     }
107                 }
108             }
109             defaultTransition(start1, end1)
110         }
111     }
112     var animateToEnd by remember { mutableStateOf(true) }
113     val progress = remember { Animatable(0f) }
114 
115     // Animate on reveal
116     LaunchedEffect(animateToEnd) {
117         progress.animateTo(if (animateToEnd) 1f else 0f, animationSpec = tween(800))
118     }
119 
120     MotionLayout(
121         modifier =
122             Modifier.background(Color(0xFF221010))
123                 .fillMaxSize()
124                 .clickable { animateToEnd = !animateToEnd }
125                 .padding(1.dp),
126         motionScene = scene,
127         progress = progress.value
128     ) {
129         for (i in 0..values.size) {
130             Box(
131                 modifier =
132                     Modifier.layoutId("foo$i")
133                         .clip(RoundedCornerShape(20.dp))
134                         .background(Color.hsv(i * 240f / values.size, 0.6f, 0.6f))
135             )
136         }
137     }
138 }
139