Loading mechanics/src/com/android/mechanics/MotionValueCollection.kt +17 −28 Original line number Diff line number Diff line Loading @@ -70,7 +70,12 @@ class MotionValueCollection( * [MotionValueCollection] is kept active. */ fun create(spec: () -> MotionSpec, label: String? = null): ManagedMotionValue { return ManagedMotionComputation(this, spec, label).also { managedComputations.add(it) } return ManagedMotionComputation(this, spec, label).also { if (isActive) { it.onActivate() } managedComputations.add(it) } } /** Loading @@ -96,12 +101,7 @@ class MotionValueCollection( var capturedGestureDragOffset = currentGestureDragOffset var capturedDirection = currentDirection val activeComputations = mutableSetOf<ManagedMotionComputation>() managedComputations.forEach { it.onActivate() activeComputations.add(it) } activeComputationCount = activeComputations.size managedComputations.forEach { it.onActivate() } try { isAnimating = true Loading @@ -115,17 +115,6 @@ class MotionValueCollection( while (true) { withFrameNanos { frameTimeNanos -> val addedComputations = managedComputations - activeComputations val removedComputations = activeComputations - managedComputations addedComputations.forEach { it.onActivate() activeComputations.add(it) } removedComputations.forEach { it.onDeactivate() activeComputations.remove(it) } activeComputationCount = activeComputations.size frameCount++ currentAnimationTimeNanos = frameTimeNanos Loading @@ -137,7 +126,7 @@ class MotionValueCollection( currentDirection = gestureContext.direction currentGestureDragOffset = gestureContext.dragOffset activeComputations.forEach { it.onFrameStart() } managedComputations.forEach { it.onFrameStart() } } // At this point, the complete frame is done (including layout, drawing and Loading @@ -152,7 +141,7 @@ class MotionValueCollection( // re-computation if the current state is being read before the next frame). var scheduleNextFrame = false activeComputations.forEach { managedComputations.forEach { if (it.onFrameEnd(isAnimatingUninterrupted)) { scheduleNextFrame = true } Loading Loading @@ -181,7 +170,9 @@ class MotionValueCollection( } isAnimating = false activeComputations.forEach { it.debugInspector?.isAnimating = false } managedComputations.forEach { it.debugInspector?.isAnimating = false } val activeComputations = managedComputations.toSet() snapshotFlow { val hasComputations = activeComputations.isNotEmpty() || managedComputations.isNotEmpty() Loading @@ -197,12 +188,11 @@ class MotionValueCollection( } .first { it } isAnimating = true activeComputations.forEach { it.debugInspector?.isAnimating = true } managedComputations.forEach { it.debugInspector?.isAnimating = true } } } finally { isActive = false activeComputations.forEach { it.onDeactivate() } activeComputationCount = 0 managedComputations.forEach { it.onDeactivate() } } } } Loading Loading @@ -241,10 +231,6 @@ class MotionValueCollection( var frameCount = 0 private set @VisibleForTesting var activeComputationCount = 0 private set @VisibleForTesting // Note - this is public so that its accessible by the mechanics:testing library val managedMotionValues: Set<ManagedMotionValue> Loading @@ -252,6 +238,7 @@ class MotionValueCollection( internal fun onDispose(toDispose: ManagedMotionComputation) { managedComputations.remove(toDispose) toDispose.onDeactivate() } } Loading Loading @@ -399,6 +386,8 @@ internal class ManagedMotionComputation( capturedAnimation = currentComputedValues.animation capturedSpringState = currentSpringState onFrameStart() debugInspector?.isAnimating = true debugInspector?.isActive = true } Loading mechanics/tests/src/com/android/mechanics/MotionValueCollectionLifecycleTest.kt +48 −31 Original line number Diff line number Diff line Loading @@ -69,18 +69,52 @@ class MotionValueCollectionLifecycleTest : } @Test fun keepRunning_activatesExisting() = runTest { fun create_withoutKeepRunning_remainsInactive() = runTest { val input = mutableFloatStateOf(1f) val underTest = MotionValueCollection(input::value, FakeGestureContext) rule.setContent {} assertThat(underTest.isActive).isFalse() val motionValue = underTest.create({ MotionSpec.Identity }) assertThat(motionValue.output).isNaN() val inspector = motionValue.debugInspector() assertThat(inspector.isActive).isFalse() } @Test fun create_whileKeepRunning_isActivatedImmediately() = runTest { val input = mutableFloatStateOf(1f) val underTest = MotionValueCollection(input::value, FakeGestureContext) rule.setContent { LaunchedEffect(Unit) { underTest.keepRunning() } } rule.awaitIdle() assertThat(underTest.isActive).isTrue() assertThat(underTest.managedMotionValues.size).isEqualTo(0) val motionValue = underTest.create({ MotionSpec.Identity }) assertThat(motionValue.output).isEqualTo(1f) val inspector = motionValue.debugInspector() assertThat(inspector.isActive).isTrue() } @Test fun keepRunning_activatesAlreadyCreated() = runTest { val input = mutableFloatStateOf(0f) val underTest = MotionValueCollection(input::value, FakeGestureContext) val inspector = underTest.create({ MotionSpec.Identity }).debugInspector() val motionValue = underTest.create({ MotionSpec.Identity }) val inspector = motionValue.debugInspector() assertThat(underTest.frameCount).isEqualTo(0) assertThat(underTest.isActive).isFalse() assertThat(underTest.isAnimating).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(0) assertThat(underTest.managedMotionValues.size).isEqualTo(1) assertThat(inspector.isActive).isFalse() assertThat(inspector.isAnimating).isFalse() assertThat(motionValue.output).isNaN() rule.setContent { LaunchedEffect(Unit) { underTest.keepRunning() } } Loading @@ -89,9 +123,10 @@ class MotionValueCollectionLifecycleTest : assertThat(underTest.frameCount).isEqualTo(1) assertThat(underTest.isActive).isTrue() assertThat(underTest.isAnimating).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(1) assertThat(underTest.managedMotionValues.size).isEqualTo(1) assertThat(inspector.isActive).isTrue() assertThat(inspector.isAnimating).isFalse() assertThat(motionValue.output).isFinite() } @Test Loading @@ -109,7 +144,7 @@ class MotionValueCollectionLifecycleTest : assertThat(underTest.frameCount).isEqualTo(1) assertThat(underTest.isActive).isTrue() assertThat(underTest.isAnimating).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(1) assertThat(underTest.managedMotionValues.size).isEqualTo(1) assertThat(inspector.isActive).isTrue() assertThat(inspector.isAnimating).isFalse() Loading @@ -119,7 +154,7 @@ class MotionValueCollectionLifecycleTest : assertThat(underTest.frameCount).isEqualTo(2) assertThat(underTest.isActive).isTrue() assertThat(underTest.isAnimating).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(0) assertThat(underTest.managedMotionValues.size).isEqualTo(0) assertThat(inspector.isActive).isFalse() assertThat(inspector.isAnimating).isFalse() } Loading @@ -138,32 +173,14 @@ class MotionValueCollectionLifecycleTest : assertThat(underTest.isActive).isFalse() assertThat(inspector.isActive).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(0) assertThat(underTest.managedMotionValues.size).isEqualTo(1) motionValue.dispose() rule.awaitIdle() assertThat(underTest.isActive).isFalse() assertThat(inspector.isActive).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(0) } @Test fun keepRunning_activatesNew() = runTest { val input = mutableFloatStateOf(0f) val underTest = MotionValueCollection(input::value, FakeGestureContext) rule.setContent { LaunchedEffect(Unit) { underTest.keepRunning() } } rule.awaitIdle() assertThat(underTest.isActive).isTrue() assertThat(underTest.activeComputationCount).isEqualTo(0) val inspector = underTest.create({ MotionSpec.Identity }).debugInspector() rule.awaitIdle() assertThat(underTest.activeComputationCount).isEqualTo(1) assertThat(inspector.isActive).isTrue() assertThat(underTest.managedMotionValues.size).isEqualTo(0) } @Test Loading @@ -180,21 +197,21 @@ class MotionValueCollectionLifecycleTest : rule.awaitIdle() assertThat(underTest.isActive).isTrue() assertThat(underTest.activeComputationCount).isEqualTo(2) assertThat(underTest.managedMotionValues.size).isEqualTo(2) assertThat(inspector1.isActive).isTrue() assertThat(inspector2.isActive).isTrue() mv1.dispose() rule.awaitIdle() assertThat(underTest.activeComputationCount).isEqualTo(1) assertThat(underTest.managedMotionValues.size).isEqualTo(1) assertThat(inspector1.isActive).isFalse() assertThat(inspector2.isActive).isTrue() mv2.dispose() rule.awaitIdle() assertThat(underTest.activeComputationCount).isEqualTo(0) assertThat(underTest.managedMotionValues.size).isEqualTo(0) assertThat(inspector1.isActive).isFalse() assertThat(inspector2.isActive).isFalse() } Loading @@ -215,14 +232,14 @@ class MotionValueCollectionLifecycleTest : assertThat(underTest.isActive).isTrue() assertThat(inspector.isActive).isTrue() assertThat(underTest.activeComputationCount).isEqualTo(1) assertThat(underTest.managedMotionValues.size).isEqualTo(1) keepRunning.value = false rule.awaitIdle() assertThat(underTest.isActive).isFalse() assertThat(inspector.isActive).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(0) assertThat(underTest.managedMotionValues.size).isEqualTo(1) } @Test Loading Loading
mechanics/src/com/android/mechanics/MotionValueCollection.kt +17 −28 Original line number Diff line number Diff line Loading @@ -70,7 +70,12 @@ class MotionValueCollection( * [MotionValueCollection] is kept active. */ fun create(spec: () -> MotionSpec, label: String? = null): ManagedMotionValue { return ManagedMotionComputation(this, spec, label).also { managedComputations.add(it) } return ManagedMotionComputation(this, spec, label).also { if (isActive) { it.onActivate() } managedComputations.add(it) } } /** Loading @@ -96,12 +101,7 @@ class MotionValueCollection( var capturedGestureDragOffset = currentGestureDragOffset var capturedDirection = currentDirection val activeComputations = mutableSetOf<ManagedMotionComputation>() managedComputations.forEach { it.onActivate() activeComputations.add(it) } activeComputationCount = activeComputations.size managedComputations.forEach { it.onActivate() } try { isAnimating = true Loading @@ -115,17 +115,6 @@ class MotionValueCollection( while (true) { withFrameNanos { frameTimeNanos -> val addedComputations = managedComputations - activeComputations val removedComputations = activeComputations - managedComputations addedComputations.forEach { it.onActivate() activeComputations.add(it) } removedComputations.forEach { it.onDeactivate() activeComputations.remove(it) } activeComputationCount = activeComputations.size frameCount++ currentAnimationTimeNanos = frameTimeNanos Loading @@ -137,7 +126,7 @@ class MotionValueCollection( currentDirection = gestureContext.direction currentGestureDragOffset = gestureContext.dragOffset activeComputations.forEach { it.onFrameStart() } managedComputations.forEach { it.onFrameStart() } } // At this point, the complete frame is done (including layout, drawing and Loading @@ -152,7 +141,7 @@ class MotionValueCollection( // re-computation if the current state is being read before the next frame). var scheduleNextFrame = false activeComputations.forEach { managedComputations.forEach { if (it.onFrameEnd(isAnimatingUninterrupted)) { scheduleNextFrame = true } Loading Loading @@ -181,7 +170,9 @@ class MotionValueCollection( } isAnimating = false activeComputations.forEach { it.debugInspector?.isAnimating = false } managedComputations.forEach { it.debugInspector?.isAnimating = false } val activeComputations = managedComputations.toSet() snapshotFlow { val hasComputations = activeComputations.isNotEmpty() || managedComputations.isNotEmpty() Loading @@ -197,12 +188,11 @@ class MotionValueCollection( } .first { it } isAnimating = true activeComputations.forEach { it.debugInspector?.isAnimating = true } managedComputations.forEach { it.debugInspector?.isAnimating = true } } } finally { isActive = false activeComputations.forEach { it.onDeactivate() } activeComputationCount = 0 managedComputations.forEach { it.onDeactivate() } } } } Loading Loading @@ -241,10 +231,6 @@ class MotionValueCollection( var frameCount = 0 private set @VisibleForTesting var activeComputationCount = 0 private set @VisibleForTesting // Note - this is public so that its accessible by the mechanics:testing library val managedMotionValues: Set<ManagedMotionValue> Loading @@ -252,6 +238,7 @@ class MotionValueCollection( internal fun onDispose(toDispose: ManagedMotionComputation) { managedComputations.remove(toDispose) toDispose.onDeactivate() } } Loading Loading @@ -399,6 +386,8 @@ internal class ManagedMotionComputation( capturedAnimation = currentComputedValues.animation capturedSpringState = currentSpringState onFrameStart() debugInspector?.isAnimating = true debugInspector?.isActive = true } Loading
mechanics/tests/src/com/android/mechanics/MotionValueCollectionLifecycleTest.kt +48 −31 Original line number Diff line number Diff line Loading @@ -69,18 +69,52 @@ class MotionValueCollectionLifecycleTest : } @Test fun keepRunning_activatesExisting() = runTest { fun create_withoutKeepRunning_remainsInactive() = runTest { val input = mutableFloatStateOf(1f) val underTest = MotionValueCollection(input::value, FakeGestureContext) rule.setContent {} assertThat(underTest.isActive).isFalse() val motionValue = underTest.create({ MotionSpec.Identity }) assertThat(motionValue.output).isNaN() val inspector = motionValue.debugInspector() assertThat(inspector.isActive).isFalse() } @Test fun create_whileKeepRunning_isActivatedImmediately() = runTest { val input = mutableFloatStateOf(1f) val underTest = MotionValueCollection(input::value, FakeGestureContext) rule.setContent { LaunchedEffect(Unit) { underTest.keepRunning() } } rule.awaitIdle() assertThat(underTest.isActive).isTrue() assertThat(underTest.managedMotionValues.size).isEqualTo(0) val motionValue = underTest.create({ MotionSpec.Identity }) assertThat(motionValue.output).isEqualTo(1f) val inspector = motionValue.debugInspector() assertThat(inspector.isActive).isTrue() } @Test fun keepRunning_activatesAlreadyCreated() = runTest { val input = mutableFloatStateOf(0f) val underTest = MotionValueCollection(input::value, FakeGestureContext) val inspector = underTest.create({ MotionSpec.Identity }).debugInspector() val motionValue = underTest.create({ MotionSpec.Identity }) val inspector = motionValue.debugInspector() assertThat(underTest.frameCount).isEqualTo(0) assertThat(underTest.isActive).isFalse() assertThat(underTest.isAnimating).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(0) assertThat(underTest.managedMotionValues.size).isEqualTo(1) assertThat(inspector.isActive).isFalse() assertThat(inspector.isAnimating).isFalse() assertThat(motionValue.output).isNaN() rule.setContent { LaunchedEffect(Unit) { underTest.keepRunning() } } Loading @@ -89,9 +123,10 @@ class MotionValueCollectionLifecycleTest : assertThat(underTest.frameCount).isEqualTo(1) assertThat(underTest.isActive).isTrue() assertThat(underTest.isAnimating).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(1) assertThat(underTest.managedMotionValues.size).isEqualTo(1) assertThat(inspector.isActive).isTrue() assertThat(inspector.isAnimating).isFalse() assertThat(motionValue.output).isFinite() } @Test Loading @@ -109,7 +144,7 @@ class MotionValueCollectionLifecycleTest : assertThat(underTest.frameCount).isEqualTo(1) assertThat(underTest.isActive).isTrue() assertThat(underTest.isAnimating).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(1) assertThat(underTest.managedMotionValues.size).isEqualTo(1) assertThat(inspector.isActive).isTrue() assertThat(inspector.isAnimating).isFalse() Loading @@ -119,7 +154,7 @@ class MotionValueCollectionLifecycleTest : assertThat(underTest.frameCount).isEqualTo(2) assertThat(underTest.isActive).isTrue() assertThat(underTest.isAnimating).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(0) assertThat(underTest.managedMotionValues.size).isEqualTo(0) assertThat(inspector.isActive).isFalse() assertThat(inspector.isAnimating).isFalse() } Loading @@ -138,32 +173,14 @@ class MotionValueCollectionLifecycleTest : assertThat(underTest.isActive).isFalse() assertThat(inspector.isActive).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(0) assertThat(underTest.managedMotionValues.size).isEqualTo(1) motionValue.dispose() rule.awaitIdle() assertThat(underTest.isActive).isFalse() assertThat(inspector.isActive).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(0) } @Test fun keepRunning_activatesNew() = runTest { val input = mutableFloatStateOf(0f) val underTest = MotionValueCollection(input::value, FakeGestureContext) rule.setContent { LaunchedEffect(Unit) { underTest.keepRunning() } } rule.awaitIdle() assertThat(underTest.isActive).isTrue() assertThat(underTest.activeComputationCount).isEqualTo(0) val inspector = underTest.create({ MotionSpec.Identity }).debugInspector() rule.awaitIdle() assertThat(underTest.activeComputationCount).isEqualTo(1) assertThat(inspector.isActive).isTrue() assertThat(underTest.managedMotionValues.size).isEqualTo(0) } @Test Loading @@ -180,21 +197,21 @@ class MotionValueCollectionLifecycleTest : rule.awaitIdle() assertThat(underTest.isActive).isTrue() assertThat(underTest.activeComputationCount).isEqualTo(2) assertThat(underTest.managedMotionValues.size).isEqualTo(2) assertThat(inspector1.isActive).isTrue() assertThat(inspector2.isActive).isTrue() mv1.dispose() rule.awaitIdle() assertThat(underTest.activeComputationCount).isEqualTo(1) assertThat(underTest.managedMotionValues.size).isEqualTo(1) assertThat(inspector1.isActive).isFalse() assertThat(inspector2.isActive).isTrue() mv2.dispose() rule.awaitIdle() assertThat(underTest.activeComputationCount).isEqualTo(0) assertThat(underTest.managedMotionValues.size).isEqualTo(0) assertThat(inspector1.isActive).isFalse() assertThat(inspector2.isActive).isFalse() } Loading @@ -215,14 +232,14 @@ class MotionValueCollectionLifecycleTest : assertThat(underTest.isActive).isTrue() assertThat(inspector.isActive).isTrue() assertThat(underTest.activeComputationCount).isEqualTo(1) assertThat(underTest.managedMotionValues.size).isEqualTo(1) keepRunning.value = false rule.awaitIdle() assertThat(underTest.isActive).isFalse() assertThat(inspector.isActive).isFalse() assertThat(underTest.activeComputationCount).isEqualTo(0) assertThat(underTest.managedMotionValues.size).isEqualTo(1) } @Test Loading