Prevent setting seekable transition playTime when no transition is running

`seekAnimations(playTimeNanos = 0)` is called after animations finish. It's triggered by the animation duration being updated to 0.
It seems reasonable for a transition that's not playing (`startTimeNanos == UnspecifiedTime`) to _stay_ not playing when seeked with a 0-duration animation, and `playTimeNanos = 0` ambiguously means 0% or 100% when the duration is 0. Setting that to 0 when there's no animation erroneously causes `isRunning` to always return true. This defends against that by checking for 0 duration and preventing calling seekToFraction when that's the case.

Fixes: 334275648
Test: Added test asserting parent seekable and child transition return correct isRunning before, during, and after animateTo
Change-Id: I227113399a9b6baaa3496eaa0a3661a7a11bebb3
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 32d573f..8efa4d2 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
@@ -71,6 +71,7 @@
 import leakcanary.DetectLeaksAfterTestSuccess
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
 import org.junit.Assert.assertTrue
 import org.junit.Rule
 import org.junit.Test
@@ -2474,6 +2475,46 @@
     }
 
     @Test
+    fun isRunningFalseAfterChildAnimatedVisibilityTransition() {
+        val seekableTransitionState = SeekableTransitionState(AnimStates.From)
+        lateinit var coroutineScope: CoroutineScope
+        lateinit var transition: Transition<AnimStates>
+        var animatedVisibilityTransition: Transition<*>? = null
+
+        rule.mainClock.autoAdvance = false
+
+        rule.setContent {
+            coroutineScope = rememberCoroutineScope()
+            transition = rememberTransition(seekableTransitionState, label = "Test")
+            transition.AnimatedVisibility(
+                visible = { it == AnimStates.To },
+            ) {
+                animatedVisibilityTransition = this.transition
+                Box(Modifier.size(100.dp))
+            }
+        }
+        rule.runOnIdle {
+            assertFalse(transition.isRunning)
+            assertNull(animatedVisibilityTransition)
+        }
+
+        rule.runOnUiThread {
+            coroutineScope.launch { seekableTransitionState.animateTo(AnimStates.To) }
+        }
+        rule.mainClock.advanceTimeBy(50)
+        rule.runOnIdle {
+            assertTrue(transition.isRunning)
+            assertTrue(animatedVisibilityTransition!!.isRunning)
+        }
+
+        rule.mainClock.advanceTimeBy(5000)
+        rule.runOnIdle {
+            assertFalse(transition.isRunning)
+            assertFalse(animatedVisibilityTransition!!.isRunning)
+        }
+    }
+
+    @Test
     fun testCleanupAfterDispose() {
         fun isObserving(): Boolean {
             var active = false
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 b61ed67..1fc3a21 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
@@ -707,7 +707,7 @@
                     animation.animationSpecDuration =
                         ((1.0 - animation.start[0]) * totalDurationNanos).roundToLong()
                 }
-            } else {
+            } else if (totalDurationNanos != 0L) {
                 // seekTo() called with a fraction. If an animation is running, we can just wait
                 // for the animation to change the value. The fraction may not be the best way
                 // to advance a regular animation.