Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 61d599e2 authored by Garfield Tan's avatar Garfield Tan
Browse files

Use WeakReference to hold target in values.

WeakHashMap holds strong references to values, so if we would like to
have them cleaned up automatically we need make sure we don't reference
the key strongly in the value.

Bug: 149918957
Test: Bubble animation looks OK.
Test: No GC root paths through PhysicsAnimator.
Test: atest PhysicsAnimator
Change-Id: I4e701bfb7f91662e18c317eaf895b5ea31e0860d
parent 9f15ba2e
Loading
Loading
Loading
Loading
+25 −7
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.systemui.util.animation.PhysicsAnimator.Companion.getInstance
import java.lang.ref.WeakReference
import java.util.WeakHashMap
import kotlin.math.abs
import kotlin.math.max
@@ -87,7 +88,9 @@ private var verboseLogging = false
 *
 * @param T The type of the object being animated.
 */
class PhysicsAnimator<T> private constructor (val target: T) {
class PhysicsAnimator<T> private constructor (target: T) {
    /** Weak reference to the animation target. */
    val weakTarget = WeakReference(target)

    /** Data class for representing animation frame updates. */
    data class AnimationUpdate(val value: Float, val velocity: Float)
@@ -307,6 +310,11 @@ class PhysicsAnimator<T> private constructor (val target: T) {
        springConfig: SpringConfig,
        flingMustReachMinOrMax: Boolean = false
    ): PhysicsAnimator<T> {
        val target = weakTarget.get()
        if (target == null) {
            Log.w(TAG, "Trying to animate a GC-ed target.")
            return this
        }
        val flingConfigCopy = flingConfig.copy()
        val springConfigCopy = springConfig.copy()
        val toAtLeast = if (startVelocity < 0) flingConfig.min else flingConfig.max
@@ -454,6 +462,11 @@ class PhysicsAnimator<T> private constructor (val target: T) {
                    "this message in a test, call PhysicsAnimatorTestUtils#prepareForTest in " +
                    "your test setup.")
        }
        val target = weakTarget.get()
        if (target == null) {
            Log.w(TAG, "Trying to animate a GC-ed object.")
            return
        }

        // Functions that will actually start the animations. These are run after we build and add
        // the InternalListener, since some animations might update/end immediately and we don't
@@ -489,7 +502,7 @@ class PhysicsAnimator<T> private constructor (val target: T) {
                    cancel(animatedProperty)

                    // Apply the configuration and start the animation.
                    getFlingAnimation(animatedProperty)
                    getFlingAnimation(animatedProperty, target)
                            .also { flingConfig.applyToAnimation(it) }
                            .start()
                }
@@ -502,7 +515,7 @@ class PhysicsAnimator<T> private constructor (val target: T) {
                // If there is no corresponding fling config, we're only springing.
                if (flingConfig == null) {
                    // Apply the configuration and start the animation.
                    val springAnim = getSpringAnimation(animatedProperty)
                    val springAnim = getSpringAnimation(animatedProperty, target)
                    springConfig.applyToAnimation(springAnim)
                    animationStartActions.add(springAnim::start)
                } else {
@@ -558,7 +571,7 @@ class PhysicsAnimator<T> private constructor (val target: T) {
                                }

                                // Apply the configuration and start the spring animation.
                                getSpringAnimation(animatedProperty)
                                getSpringAnimation(animatedProperty, target)
                                        .also { springConfig.applyToAnimation(it) }
                                        .start()
                            }
@@ -570,6 +583,7 @@ class PhysicsAnimator<T> private constructor (val target: T) {

        // Add an internal listener that will dispatch animation events to the provided listeners.
        internalListeners.add(InternalListener(
                target,
                getAnimatedProperties(),
                ArrayList(updateListeners),
                ArrayList(endListeners),
@@ -594,7 +608,10 @@ class PhysicsAnimator<T> private constructor (val target: T) {
    }

    /** Retrieves a spring animation for the given property, building one if needed. */
    private fun getSpringAnimation(property: FloatPropertyCompat<in T>): SpringAnimation {
    private fun getSpringAnimation(
        property: FloatPropertyCompat<in T>,
        target: T
    ): SpringAnimation {
        return springAnimations.getOrPut(
                property,
                { configureDynamicAnimation(SpringAnimation(target, property), property)
@@ -602,7 +619,7 @@ class PhysicsAnimator<T> private constructor (val target: T) {
    }

    /** Retrieves a fling animation for the given property, building one if needed. */
    private fun getFlingAnimation(property: FloatPropertyCompat<in T>): FlingAnimation {
    private fun getFlingAnimation(property: FloatPropertyCompat<in T>, target: T): FlingAnimation {
        return flingAnimations.getOrPut(
                property,
                { configureDynamicAnimation(FlingAnimation(target, property), property)
@@ -638,6 +655,7 @@ class PhysicsAnimator<T> private constructor (val target: T) {
     * appropriate value for allEnded to [EndListener.onAnimationEnd].
     */
    internal inner class InternalListener constructor(
        private val target: T,
        private var properties: Set<FloatPropertyCompat<in T>>,
        private var updateListeners: List<UpdateListener<T>>,
        private var endListeners: List<EndListener<T>>,
+5 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Ignore
@@ -590,8 +591,10 @@ class PhysicsAnimatorTest : SysuiTestCase() {
                updatesForProperty.remove(update)
            }

            val target = animator.weakTarget.get()
            assertNotNull(target)
            // Mark this invocation verified.
            verify(mockUpdateListener).onAnimationUpdateForProperty(animator.target, updateMap)
            verify(mockUpdateListener).onAnimationUpdateForProperty(target!!, updateMap)
        }

        verifyNoMoreInteractions(mockUpdateListener)