/* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ @file:OptIn(ExperimentalMaterial3ExpressiveApi::class) package com.android.mechanics.demo.presentation import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Slider import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import com.android.mechanics.debug.DebugMotionValueVisualization import com.android.mechanics.debug.debugMotionValueGraph import com.android.mechanics.demo.tuneable.Demo import com.android.mechanics.demo.tuneable.HasMotionValueVisualization import com.android.mechanics.rememberDistanceGestureContext import com.android.mechanics.rememberMotionSpecAsState import com.android.mechanics.rememberMotionValue import com.android.mechanics.spec.Guarantee import com.android.mechanics.spec.MotionSpec import com.android.mechanics.spec.builder.effectsDirectionalMotionSpec object GuaranteeFadeDemo : Demo, HasMotionValueVisualization { var inputRange by mutableStateOf(0f..200f) @Composable override fun DemoUi(config: Unit, modifier: Modifier) { val colors = MaterialTheme.colorScheme val density = LocalDensity.current val guaranteeDistance = remember { mutableFloatStateOf(80f) } // Also using GestureContext.dragOffset as input. val gestureContext = rememberDistanceGestureContext() val withoutGuarantee = rememberMotionValue( input = { gestureContext.dragOffset }, gestureContext = gestureContext, spec = rememberMotionSpecAsState { MotionSpec( effectsDirectionalMotionSpec { fixedValue( breakpoint = (inputRange.start + inputRange.endInclusive) / 2f, value = 1f, guarantee = Guarantee.None, ) } ) }, ) val withGuarantee = rememberMotionValue( input = { gestureContext.dragOffset }, gestureContext = gestureContext, spec = rememberMotionSpecAsState { val distance = guaranteeDistance.floatValue MotionSpec( effectsDirectionalMotionSpec { fixedValue( breakpoint = (inputRange.start + inputRange.endInclusive) / 2f, value = 1f, guarantee = if (distance > 0) { Guarantee.InputDelta(distance) } else { Guarantee.None }, ) } ) }, ) val defaultValueColor = MaterialTheme.colorScheme.primary val guaranteeValueColor = MaterialTheme.colorScheme.secondary Column( verticalArrangement = Arrangement.spacedBy(24.dp), modifier = modifier.fillMaxWidth().padding(vertical = 24.dp, horizontal = 48.dp), ) { Text("Guarantee Distance") Slider( value = guaranteeDistance.value, valueRange = inputRange, onValueChange = { guaranteeDistance.value = it }, modifier = Modifier.fillMaxWidth(), ) Row( horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth().padding(16.dp), ) { Box( modifier = Modifier.size(48.dp) .graphicsLayer { this.alpha = withoutGuarantee.floatValue } .background(defaultValueColor) ) Box( modifier = Modifier.size(48.dp) .graphicsLayer { this.alpha = withGuarantee.floatValue } .background(guaranteeValueColor) ) } // MotionValue visualization DebugMotionValueVisualization( withGuarantee, inputRange, modifier = Modifier.fillMaxWidth() .height(64.dp) .debugMotionValueGraph( withoutGuarantee, defaultValueColor, inputRange, 0f..1f, ), ) // Input visualization Slider( value = gestureContext.dragOffset, valueRange = inputRange, onValueChange = { gestureContext.dragOffset = it }, modifier = Modifier.fillMaxWidth(), ) } } @Composable override fun rememberDefaultConfig() {} override val visualizationInputRange: ClosedFloatingPointRange get() = inputRange @Composable override fun ColumnScope.ConfigUi(config: Unit, onConfigChanged: (Unit) -> Unit) {} override val identifier: String = "GuaranteeFadeDemo" }