From 959ac760cf45557b018509a96d77f759228e7661 Mon Sep 17 00:00:00 2001 From: Joshua Tsuji Date: Thu, 13 Feb 2020 03:21:56 -0500 Subject: [PATCH] Modifies PhysicsAnimator's cancellation methods to support tests. Test: atest SystemUITests Change-Id: I97ae15d827958b2fdcf7a9f9df6e1254406fd0c8 --- .../util/animation/PhysicsAnimator.kt | 37 ++++++++++++++++--- .../animation/PhysicsAnimatorTestUtils.kt | 26 +++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt index f4157f21e1583..8625d63a3c7ef 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt @@ -125,6 +125,13 @@ class PhysicsAnimator private constructor (val target: T) { */ internal var startAction: () -> Unit = ::startInternal + /** + * Action to run when [cancel] is called. This can be changed by + * [PhysicsAnimatorTestUtils.prepareForTest] to cancel animations from the main thread, which + * is required. + */ + internal var cancelAction: (Set>) -> Unit = ::cancelInternal + /** * Springs a property to the given value, using the provided configuration settings. * @@ -429,10 +436,13 @@ class PhysicsAnimator private constructor (val target: T) { max = max(currentValue, this.max) } - // Apply the configuration and start the animation. Since flings can't be - // redirected while in motion, cancel it first. + // Flings can't be updated to a new position while maintaining velocity, because + // we're using the explicitly provided start velocity. Cancel any flings (or + // springs) on this property before flinging. + cancel(animatedProperty) + + // Apply the configuration and start the animation. getFlingAnimation(animatedProperty) - .also { it.cancel() } .also { flingConfig.applyToAnimation(it) } .start() } @@ -707,11 +717,26 @@ class PhysicsAnimator private constructor (val target: T) { return springConfigs.keys.union(flingConfigs.keys) } + /** + * Cancels the given properties. This is typically called immediately by [cancel], unless this + * animator is under test. + */ + internal fun cancelInternal(properties: Set>) { + for (property in properties) { + flingAnimations[property]?.cancel() + springAnimations[property]?.cancel() + } + } + /** Cancels all in progress animations on all properties. */ fun cancel() { - for (dynamicAnim in flingAnimations.values.union(springAnimations.values)) { - dynamicAnim.cancel() - } + cancelAction(flingAnimations.keys) + cancelAction(springAnimations.keys) + } + + /** Cancels in progress animations on the provided properties only. */ + fun cancel(vararg properties: FloatPropertyCompat) { + cancelAction(properties.toSet()) } /** diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt index 965decd255a03..c50eeac80d7a9 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt @@ -363,8 +363,12 @@ object PhysicsAnimatorTestUtils { private val testEndListeners = ArrayList>() private val testUpdateListeners = ArrayList>() + /** Whether we're currently in the middle of executing startInternal(). */ + private var currentlyRunningStartInternal = false + init { animator.startAction = ::startForTest + animator.cancelAction = ::cancelForTest } internal fun addTestEndListener(listener: PhysicsAnimator.EndListener) { @@ -437,7 +441,29 @@ object PhysicsAnimatorTestUtils { } }) + currentlyRunningStartInternal = true animator.startInternal() + currentlyRunningStartInternal = false + unblockLatch.countDown() + } + + unblockLatch.await(timeoutMs, TimeUnit.MILLISECONDS) + } + + private fun cancelForTest(properties: Set>) { + // If this was called from startInternal, we are already on the animation thread, and + // should just call cancelInternal rather than posting it. If we post it, the + // cancellation will occur after the rest of startInternal() and we'll immediately + // cancel the animation we worked so hard to start! + if (currentlyRunningStartInternal) { + animator.cancelInternal(properties) + return + } + + val unblockLatch = CountDownLatch(1) + + animationThreadHandler.post { + animator.cancelInternal(properties) unblockLatch.countDown() }