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.compose.animation.core 18 19 import androidx.compose.runtime.getValue 20 import androidx.compose.runtime.mutableStateOf 21 import androidx.compose.runtime.setValue 22 import kotlinx.coroutines.CoroutineScope 23 import kotlinx.coroutines.launch 24 25 @RequiresOptIn( 26 message = "This is an experimental animation API for Transition. It may change in the future." 27 ) 28 @Retention(AnnotationRetention.BINARY) 29 public annotation class ExperimentalAnimatableApi 30 31 /** 32 * [DeferredTargetAnimation] is intended for animations where the target is unknown at the time of 33 * instantiation. Such use cases include, but are not limited to, size or position animations 34 * created during composition or the initialization of a Modifier.Node, yet the target size or 35 * position stays unknown until the later measure and placement phase. 36 * 37 * [DeferredTargetAnimation] offers a declarative [updateTarget] function, which requires a target 38 * to either set up the animation or update the animation, and to read the current value of the 39 * animation. 40 * 41 * @sample androidx.compose.animation.core.samples.DeferredTargetAnimationSample 42 */ 43 @ExperimentalAnimatableApi 44 public class DeferredTargetAnimation<T, V : AnimationVector>( 45 private val vectorConverter: TwoWayConverter<T, V> 46 ) { 47 /** Returns the target value from the most recent [updateTarget] call. */ 48 public val pendingTarget: T? 49 get() = _pendingTarget 50 51 private var _pendingTarget: T? by mutableStateOf(null) 52 private val target: T? 53 get() = animatable?.targetValue 54 55 private var animatable: Animatable<T, V>? = null 56 57 /** 58 * [updateTarget] sets up an animation, or updates an already running animation, based on the 59 * [target] in the given [coroutineScope]. [pendingTarget] will be updated to track the last 60 * seen [target]. 61 * 62 * [updateTarget] will return the current value of the animation after launching the animation 63 * in the given [coroutineScope]. 64 * 65 * @return current value of the animation 66 */ updateTargetnull67 public fun updateTarget( 68 target: T, 69 coroutineScope: CoroutineScope, 70 animationSpec: FiniteAnimationSpec<T> = spring() 71 ): T { 72 _pendingTarget = target 73 val anim = animatable ?: Animatable(target, vectorConverter).also { animatable = it } 74 coroutineScope.launch { 75 if (anim.targetValue != _pendingTarget) { 76 anim.animateTo(target, animationSpec) 77 } 78 } 79 return anim.value 80 } 81 82 /** 83 * [isIdle] returns true when the animation has finished running and reached its 84 * [pendingTarget], or when the animation has not been set up (i.e. [updateTarget] has never 85 * been called). 86 */ 87 public val isIdle: Boolean 88 get() = _pendingTarget == target && animatable?.isRunning != true 89 } 90