Modifies PhysicsAnimator's cancellation methods to support tests.

Test: atest SystemUITests
Change-Id: I97ae15d827958b2fdcf7a9f9df6e1254406fd0c8
This commit is contained in:
Joshua Tsuji
2020-02-13 03:21:56 -05:00
parent a76b713592
commit 959ac760cf
2 changed files with 57 additions and 6 deletions

View File

@@ -125,6 +125,13 @@ class PhysicsAnimator<T> 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<FloatPropertyCompat<in T>>) -> Unit = ::cancelInternal
/**
* Springs a property to the given value, using the provided configuration settings.
*
@@ -429,10 +436,13 @@ class PhysicsAnimator<T> 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<T> 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<FloatPropertyCompat<in T>>) {
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<in T>) {
cancelAction(properties.toSet())
}
/**

View File

@@ -363,8 +363,12 @@ object PhysicsAnimatorTestUtils {
private val testEndListeners = ArrayList<PhysicsAnimator.EndListener<T>>()
private val testUpdateListeners = ArrayList<PhysicsAnimator.UpdateListener<T>>()
/** 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<T>) {
@@ -437,7 +441,29 @@ object PhysicsAnimatorTestUtils {
}
})
currentlyRunningStartInternal = true
animator.startInternal()
currentlyRunningStartInternal = false
unblockLatch.countDown()
}
unblockLatch.await(timeoutMs, TimeUnit.MILLISECONDS)
}
private fun cancelForTest(properties: Set<FloatPropertyCompat<in T>>) {
// 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()
}