Merge "Clear start animation time after SeekableTransitionState animates/snaps" into androidx-main
diff --git a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/SeekableTransitionStateTest.kt b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/SeekableTransitionStateTest.kt
index f2101f2..d5992f9 100644
--- a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/SeekableTransitionStateTest.kt
+++ b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/SeekableTransitionStateTest.kt
@@ -51,6 +51,7 @@
import androidx.test.filters.SdkSuppress
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.android.awaitFrame
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@@ -2390,4 +2391,81 @@
}
}
}
+
+ @Test
+ fun isRunningDuringAnimateTo() {
+ val seekableTransitionState = SeekableTransitionState(AnimStates.From)
+ lateinit var transition: Transition<AnimStates>
+ var animatedValue by mutableIntStateOf(-1)
+
+ rule.mainClock.autoAdvance = false
+
+ rule.setContent {
+ LaunchedEffect(seekableTransitionState) {
+ seekableTransitionState.animateTo(AnimStates.To)
+ }
+ transition = rememberTransition(seekableTransitionState, label = "Test")
+ animatedValue = transition.animateInt(
+ label = "Value",
+ transitionSpec = { tween(easing = LinearEasing) }
+ ) { state ->
+ when (state) {
+ AnimStates.From -> 0
+ else -> 1000
+ }
+ }.value
+ }
+ rule.runOnIdle {
+ assertEquals(0, animatedValue)
+ assertFalse(transition.isRunning)
+ }
+ rule.mainClock.advanceTimeByFrame() // wait for composition after animateTo()
+ rule.mainClock.advanceTimeByFrame() // one frame to set the start time
+ rule.runOnIdle {
+ assertTrue(animatedValue > 0)
+ assertTrue(transition.isRunning)
+ }
+ rule.mainClock.advanceTimeBy(5000)
+ rule.runOnIdle {
+ assertEquals(1000, animatedValue)
+ assertFalse(transition.isRunning)
+ }
+ }
+
+ @Test
+ fun isRunningFalseAfterSnapTo() {
+ val seekableTransitionState = SeekableTransitionState(AnimStates.From)
+ lateinit var transition: Transition<AnimStates>
+ var animatedValue by mutableIntStateOf(-1)
+
+ rule.mainClock.autoAdvance = false
+
+ rule.setContent {
+ LaunchedEffect(seekableTransitionState) {
+ awaitFrame() // Not sure why this is needed. Animated val doesn't change without it.
+ seekableTransitionState.snapTo(AnimStates.To)
+ }
+ transition = rememberTransition(seekableTransitionState, label = "Test")
+ animatedValue = transition.animateInt(
+ label = "Value",
+ transitionSpec = { tween(easing = LinearEasing) }
+ ) { state ->
+ when (state) {
+ AnimStates.From -> 0
+ else -> 1000
+ }
+ }.value
+ }
+ rule.runOnIdle {
+ assertEquals(0, animatedValue)
+ assertFalse(transition.isRunning)
+ }
+ rule.mainClock.advanceTimeByFrame() // wait for composition after animateTo()
+ rule.mainClock.advanceTimeByFrame() // one frame to snap
+ rule.mainClock.advanceTimeByFrame() // one frame for LaunchedEffect's awaitFrame()
+ rule.runOnIdle {
+ assertEquals(1000, animatedValue)
+ assertFalse(transition.isRunning)
+ }
+ }
}
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
index fc75648..ba2fc7d 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
@@ -475,6 +475,7 @@
// the correct animation values
waitForCompositionAfterTargetStateChange()
}
+ transition.onTransitionEnd()
}
}
@@ -689,6 +690,7 @@
currentState = targetState
waitForComposition()
fraction = 0f
+ transition.onTransitionEnd()
}
}
}