1 /* <lambda>null2 * Copyright (C) 2025 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(ExperimentalAnimatableApi::class) 18 19 package com.android.mechanics.demo.presentation 20 21 import androidx.compose.animation.core.ExperimentalAnimatableApi 22 import androidx.compose.foundation.background 23 import androidx.compose.foundation.layout.Arrangement 24 import androidx.compose.foundation.layout.Column 25 import androidx.compose.foundation.layout.ColumnScope 26 import androidx.compose.foundation.layout.Row 27 import androidx.compose.foundation.layout.Spacer 28 import androidx.compose.foundation.layout.fillMaxWidth 29 import androidx.compose.foundation.layout.height 30 import androidx.compose.foundation.layout.padding 31 import androidx.compose.foundation.rememberScrollState 32 import androidx.compose.foundation.verticalScroll 33 import androidx.compose.material.icons.Icons 34 import androidx.compose.material.icons.filled.AllInclusive 35 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi 36 import androidx.compose.material3.Icon 37 import androidx.compose.material3.LocalContentColor 38 import androidx.compose.material3.MaterialTheme 39 import androidx.compose.material3.MaterialTheme.typography 40 import androidx.compose.material3.Text 41 import androidx.compose.runtime.Composable 42 import androidx.compose.runtime.CompositionLocalProvider 43 import androidx.compose.runtime.getValue 44 import androidx.compose.runtime.mutableStateOf 45 import androidx.compose.runtime.remember 46 import androidx.compose.runtime.setValue 47 import androidx.compose.ui.Modifier 48 import androidx.compose.ui.unit.Dp 49 import androidx.compose.ui.unit.dp 50 import com.android.compose.animation.scene.ElementKey 51 import com.android.compose.modifiers.thenIf 52 import com.android.mechanics.demo.staging.behavior.reveal.FadeContentRevealSpec 53 import com.android.mechanics.demo.staging.behavior.reveal.fadeReveal 54 import com.android.mechanics.demo.staging.behavior.reveal.rememberFadeContentRevealSpec 55 import com.android.mechanics.demo.staging.behavior.reveal.revealContainer 56 import com.android.mechanics.demo.tuneable.Demo 57 import com.android.mechanics.demo.tuneable.DpSlider 58 import com.android.mechanics.demo.tuneable.GuaranteeSection 59 import com.android.mechanics.demo.tuneable.LabelledCheckbox 60 import com.android.mechanics.demo.tuneable.Section 61 import com.android.mechanics.demo.tuneable.SpringParameterSection 62 import com.android.mechanics.demo.util.ExpandableCard 63 64 object PracticalDemoAppear : Demo<PracticalDemoAppear.Config> { 65 object Elements { 66 val ExpandableContent = ElementKey("ExpandableContent") 67 } 68 69 data class Config(val fadeSpec: FadeContentRevealSpec, val showItemBackground: Boolean) 70 71 @Composable 72 override fun DemoUi(config: Config, modifier: Modifier) { 73 val colors = MaterialTheme.colorScheme 74 ExpandableCard( 75 modifier = modifier.fillMaxWidth(), 76 header = { Text(text = "Contents", style = typography.titleMedium) }, 77 ) { isExpanded -> 78 Column( 79 verticalArrangement = Arrangement.spacedBy(8.dp), 80 modifier = 81 Modifier.fillMaxWidth() 82 .element(Elements.ExpandableContent) 83 .revealContainer(this@ExpandableCard) 84 .verticalScroll(rememberScrollState()) 85 .padding(start = 16.dp, end = 16.dp, bottom = 16.dp), 86 ) { 87 if (isExpanded) { 88 Spacer(modifier = Modifier.height(24.dp)) 89 repeat(10) { 90 Row( 91 horizontalArrangement = Arrangement.spacedBy(8.dp), 92 modifier = 93 Modifier.noResizeDuringTransitions() 94 .fadeReveal(spec = config.fadeSpec, debug = true) 95 .fillMaxWidth() 96 .thenIf(config.showItemBackground) { 97 Modifier.background(colors.primary) 98 }, 99 ) { 100 CompositionLocalProvider( 101 LocalContentColor provides 102 if (config.showItemBackground) colors.onPrimary 103 else colors.onSurface 104 ) { 105 Icon(Icons.Default.AllInclusive, null) 106 Text(text = "Item ${it + 1}", modifier = Modifier.height(20.dp)) 107 } 108 } 109 } 110 } 111 } 112 } 113 } 114 115 @OptIn(ExperimentalMaterial3ExpressiveApi::class) 116 @Composable 117 override fun rememberDefaultConfig(): Config { 118 val fadeRevealSpec = rememberFadeContentRevealSpec(showDelta = 8.dp, hideDelta = 16.dp) 119 return remember(fadeRevealSpec) { Config(fadeRevealSpec, showItemBackground = false) } 120 } 121 122 override var visualizationInputRange by mutableStateOf(0f..1000f) 123 override val collapsedGraphHeight: Dp = 20.dp 124 125 @Composable 126 override fun ColumnScope.ConfigUi(config: Config, onConfigChanged: (Config) -> Unit) { 127 LabelledCheckbox( 128 "Show item background", 129 config.showItemBackground, 130 onCheckedChange = { onConfigChanged(config.copy(showItemBackground = it)) }, 131 modifier = Modifier.fillMaxWidth(), 132 ) 133 134 Section( 135 "Show", 136 summary = { "" }, 137 value = config.fadeSpec, 138 onValueChanged = { onConfigChanged(config.copy(fadeSpec = it)) }, 139 sectionKey = "show spec", 140 modifier = Modifier.fillMaxWidth(), 141 ) { spec, onSpecChanged -> 142 SpringParameterSection( 143 " Spring", 144 spec.showSpring, 145 onValueChanged = { onSpecChanged(spec.copy(showSpring = it)) }, 146 "showspring", 147 modifier = Modifier.fillMaxWidth(), 148 ) 149 GuaranteeSection( 150 " Guarantee", 151 spec.showGuarantee, 152 { onSpecChanged(spec.copy(showGuarantee = it)) }, 153 "showguarantee", 154 modifier = Modifier.fillMaxWidth(), 155 ) 156 157 Text(text = "Delta", modifier = Modifier.padding(start = 8.dp)) 158 DpSlider(spec.showDelta, { onSpecChanged(spec.copy(showDelta = it)) }, 0.dp..24.dp) 159 } 160 161 Section( 162 "Hide", 163 summary = { "" }, 164 value = config.fadeSpec, 165 onValueChanged = { onConfigChanged(config.copy(fadeSpec = it)) }, 166 sectionKey = "show spec", 167 modifier = Modifier.fillMaxWidth(), 168 ) { spec, onSpecChanged -> 169 SpringParameterSection( 170 "Spring", 171 spec.hideSpring, 172 onValueChanged = { onSpecChanged(spec.copy(hideSpring = it)) }, 173 "hidespring", 174 modifier = Modifier.fillMaxWidth(), 175 ) 176 GuaranteeSection( 177 "Guarantee", 178 spec.hideGuarantee, 179 { onSpecChanged(spec.copy(hideGuarantee = it)) }, 180 "hideguarantee", 181 modifier = Modifier.fillMaxWidth(), 182 ) 183 184 Text(text = "Delta", modifier = Modifier.padding(start = 8.dp)) 185 DpSlider(spec.hideDelta, { onSpecChanged(spec.copy(hideDelta = it)) }, 0.dp..24.dp) 186 } 187 } 188 189 override val identifier: String = "PracticalDemoAppear" 190 } 191