Remove value callback animations when they get overwritten (#1470)

Fixes #1464
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/DynamicActivity.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/DynamicActivity.kt
index 9e04ad3..8970d49 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/DynamicActivity.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/DynamicActivity.kt
@@ -48,19 +48,18 @@
         animationView.addLottieOnCompositionLoadedListener { _ ->
             animationView.resolveKeyPath(KeyPath("**")).forEach {
                 Log.d(TAG, it.keysToString())
+                setupValueCallbacks()
             }
         }
         animationView.setFailureListener { e ->
-            Log.e(TAG, "Failed to load composition", e)
+            Log.e(TAG, "Failed to load animation!", e)
         }
 
         updateButtonText()
-
     }
 
     private fun setupValueCallbacks() {
-        animationView.addValueCallback(KeyPath("LeftArmWave"),
-            LottieProperty.TIME_REMAP) { frameInfo ->
+        animationView.addValueCallback(KeyPath("LeftArmWave"), LottieProperty.TIME_REMAP) { frameInfo ->
             2 * speed.toFloat() * frameInfo.overallProgress
         }
 
diff --git a/gradle.properties b/gradle.properties
index c1c627b..65698f0 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -34,3 +34,7 @@
 POM_INCEPTION_YEAR=2017
 android.useAndroidX=true
 android.enableJetifier=true
+org.gradle.caching=true
+org.gradle.jvmargs=-Xmx4096m
+org.gradle.daemon=true
+org.gradle.parallel=true
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/BaseStrokeContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/BaseStrokeContent.java
index 8a1aece..813a8aa 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/BaseStrokeContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/BaseStrokeContent.java
@@ -314,6 +314,10 @@
     } else if (property == LottieProperty.STROKE_WIDTH) {
       widthAnimation.setValueCallback((LottieValueCallback<Float>) callback);
     } else if (property == LottieProperty.COLOR_FILTER) {
+      if (colorFilterAnimation != null) {
+        layer.removeAnimation(colorFilterAnimation);
+      }
+
       if (callback == null) {
         colorFilterAnimation = null;
       } else {
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/FillContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/FillContent.java
index 4e3b838..56420f9 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/FillContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/FillContent.java
@@ -128,6 +128,10 @@
     } else if (property == LottieProperty.OPACITY) {
       opacityAnimation.setValueCallback((LottieValueCallback<Integer>) callback);
     } else if (property == LottieProperty.COLOR_FILTER) {
+      if (colorFilterAnimation != null) {
+        layer.removeAnimation(colorFilterAnimation);
+      }
+
       if (callback == null) {
         colorFilterAnimation = null;
       } else {
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientFillContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientFillContent.java
index 747c20c..869cb4e 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientFillContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientFillContent.java
@@ -235,6 +235,10 @@
     if (property == LottieProperty.OPACITY) {
       opacityAnimation.setValueCallback((LottieValueCallback<Integer>) callback);
     } else if (property == LottieProperty.COLOR_FILTER) {
+      if (colorFilterAnimation != null) {
+        layer.removeAnimation(colorFilterAnimation);
+      }
+
        if (callback == null) {
          colorFilterAnimation = null;
        } else {
@@ -244,12 +248,14 @@
          layer.addAnimation(colorFilterAnimation);
        }
      } else if (property == LottieProperty.GRADIENT_COLOR) {
+      if (colorCallbackAnimation != null) {
+        layer.removeAnimation(colorCallbackAnimation);
+      }
+
        if (callback == null) {
-         if (colorCallbackAnimation != null) {
-           layer.removeAnimation(colorCallbackAnimation);
-         }
          colorCallbackAnimation = null;
        } else {
+         //noinspection rawtypes
          colorCallbackAnimation = new ValueCallbackKeyframeAnimation<>(callback);
          colorCallbackAnimation.addUpdateListener(this);
          layer.addAnimation(colorCallbackAnimation);
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientStrokeContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientStrokeContent.java
index ab5a29a..351171c 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientStrokeContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientStrokeContent.java
@@ -164,10 +164,11 @@
   public <T> void addValueCallback(T property, @Nullable LottieValueCallback<T> callback) {
     super.addValueCallback(property, callback);
     if (property == LottieProperty.GRADIENT_COLOR) {
+      if (colorCallbackAnimation != null) {
+        layer.removeAnimation(colorCallbackAnimation);
+      }
+
       if (callback == null) {
-        if (colorCallbackAnimation != null) {
-          layer.removeAnimation(colorCallbackAnimation);
-        }
         colorCallbackAnimation = null;
       } else {
         colorCallbackAnimation = new ValueCallbackKeyframeAnimation<>(callback);
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/PolystarContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/PolystarContent.java
index 651511e..c13c63f 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/PolystarContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/PolystarContent.java
@@ -316,8 +316,7 @@
       innerRadiusAnimation.setValueCallback((LottieValueCallback<Float>) callback);
     } else if (property == LottieProperty.POLYSTAR_OUTER_RADIUS) {
       outerRadiusAnimation.setValueCallback((LottieValueCallback<Float>) callback);
-    } else if (property == LottieProperty.POLYSTAR_INNER_ROUNDEDNESS &&
-        innerRoundednessAnimation != null) {
+    } else if (property == LottieProperty.POLYSTAR_INNER_ROUNDEDNESS && innerRoundednessAnimation != null) {
       innerRoundednessAnimation.setValueCallback((LottieValueCallback<Float>) callback);
     } else if (property == LottieProperty.POLYSTAR_OUTER_ROUNDEDNESS) {
       outerRoundednessAnimation.setValueCallback((LottieValueCallback<Float>) callback);
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/StrokeContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/StrokeContent.java
index 524ecb0..4e4900e 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/StrokeContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/StrokeContent.java
@@ -58,6 +58,10 @@
     if (property == STROKE_COLOR) {
       colorAnimation.setValueCallback((LottieValueCallback<Integer>) callback);
     } else if (property == LottieProperty.COLOR_FILTER) {
+      if (colorFilterAnimation != null) {
+        layer.removeAnimation(colorFilterAnimation);
+      }
+
       if (callback == null) {
         colorFilterAnimation = null;
       } else {
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
index 12a126b..d782f44 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
@@ -201,9 +201,12 @@
 
     if (property == LottieProperty.TIME_REMAP) {
       if (callback == null) {
-        timeRemapping = null;
+        if (timeRemapping != null) {
+          timeRemapping.setValueCallback(null);
+        }
       } else {
         timeRemapping = new ValueCallbackKeyframeAnimation<>((LottieValueCallback<Float>) callback);
+        timeRemapping.addUpdateListener(this);
         addAnimation(timeRemapping);
       }
     }
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java
index 494a581..3a02b6b 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java
@@ -52,13 +52,23 @@
   @Nullable
   private BaseKeyframeAnimation<Integer, Integer> colorAnimation;
   @Nullable
+  private BaseKeyframeAnimation<Integer, Integer> colorCallbackAnimation;
+  @Nullable
   private BaseKeyframeAnimation<Integer, Integer> strokeColorAnimation;
   @Nullable
+  private BaseKeyframeAnimation<Integer, Integer> strokeColorCallbackAnimation;
+  @Nullable
   private BaseKeyframeAnimation<Float, Float> strokeWidthAnimation;
   @Nullable
+  private BaseKeyframeAnimation<Float, Float> strokeWidthCallbackAnimation;
+  @Nullable
   private BaseKeyframeAnimation<Float, Float> trackingAnimation;
   @Nullable
+  private BaseKeyframeAnimation<Float, Float> trackingCallbackAnimation;
+  @Nullable
   private BaseKeyframeAnimation<Float, Float> textSizeAnimation;
+  @Nullable
+  private BaseKeyframeAnimation<Float, Float> textSizeCallbackAnimation;
 
   TextLayer(LottieDrawable lottieDrawable, Layer layerModel) {
     super(lottieDrawable, layerModel);
@@ -116,13 +126,17 @@
       return;
     }
 
-    if (colorAnimation != null) {
+    if (colorCallbackAnimation != null) {
+      fillPaint.setColor(colorCallbackAnimation.getValue());
+    } else if (colorAnimation != null) {
       fillPaint.setColor(colorAnimation.getValue());
     } else {
       fillPaint.setColor(documentData.color);
     }
 
-    if (strokeColorAnimation != null) {
+    if (strokeColorCallbackAnimation != null) {
+      strokePaint.setColor(strokeColorCallbackAnimation.getValue());
+    } else if (strokeColorAnimation != null) {
       strokePaint.setColor(strokeColorAnimation.getValue());
     } else {
       strokePaint.setColor(documentData.strokeColor);
@@ -132,7 +146,9 @@
     fillPaint.setAlpha(alpha);
     strokePaint.setAlpha(alpha);
 
-    if (strokeWidthAnimation != null) {
+    if (strokeWidthCallbackAnimation != null) {
+      strokePaint.setStrokeWidth(strokeWidthCallbackAnimation.getValue());
+    } else if (strokeWidthAnimation != null) {
       strokePaint.setStrokeWidth(strokeWidthAnimation.getValue());
     } else {
       float parentScale = Utils.getScale(parentMatrix);
@@ -150,7 +166,14 @@
 
   private void drawTextGlyphs(
       DocumentData documentData, Matrix parentMatrix, Font font, Canvas canvas) {
-    float textSize = textSizeAnimation == null ? documentData.size : textSizeAnimation.getValue();
+    float textSize;
+    if (textSizeCallbackAnimation != null) {
+      textSize = textSizeCallbackAnimation.getValue();
+    } else if (textSizeAnimation != null) {
+      textSize = textSizeAnimation.getValue();
+    } else {
+      textSize = documentData.size;
+    }
     float fontScale = textSize / 100f;
     float parentScale = Utils.getScale(parentMatrix);
 
@@ -199,7 +222,9 @@
       float tx = (float) character.getWidth() * fontScale * Utils.dpScale() * parentScale;
       // Add tracking
       float tracking = documentData.tracking / 10f;
-      if (trackingAnimation != null) {
+      if (trackingCallbackAnimation != null) {
+        tracking += trackingCallbackAnimation.getValue();
+      } else if (trackingAnimation != null) {
         tracking += trackingAnimation.getValue();
       }
       tx += tracking * parentScale;
@@ -220,7 +245,14 @@
       text = textDelegate.getTextInternal(text);
     }
     fillPaint.setTypeface(typeface);
-    float textSize = textSizeAnimation == null ? documentData.size : textSizeAnimation.getValue();
+    float textSize;
+    if (textSizeCallbackAnimation != null) {
+      textSize = textSizeCallbackAnimation.getValue();
+    } else if (textSizeAnimation != null) {
+      textSize = textSizeAnimation.getValue();
+    } else {
+      textSize = documentData.size;
+    }
     fillPaint.setTextSize(textSize * Utils.dpScale());
     strokePaint.setTypeface(fillPaint.getTypeface());
     strokePaint.setTextSize(fillPaint.getTextSize());
@@ -268,7 +300,9 @@
       float charWidth = fillPaint.measureText(charString, 0, 1);
       // Add tracking
       float tracking = documentData.tracking / 10f;
-      if (trackingAnimation != null) {
+      if (trackingCallbackAnimation != null) {
+        tracking += trackingCallbackAnimation.getValue();
+      } else if (trackingAnimation != null) {
         tracking += trackingAnimation.getValue();
       }
       float tx = charWidth + tracking * parentScale;
@@ -417,75 +451,64 @@
   public <T> void addValueCallback(T property, @Nullable LottieValueCallback<T> callback) {
     super.addValueCallback(property, callback);
     if (property == LottieProperty.COLOR) {
-      if (colorAnimation != null) {
-        colorAnimation.setValueCallback((LottieValueCallback<Integer>) callback);
+      if (colorCallbackAnimation != null) {
+        removeAnimation(colorCallbackAnimation);
+      }
+
+      if (callback == null) {
+          colorCallbackAnimation = null;
       } else {
-        if (callback == null) {
-          if (colorAnimation != null) {
-            removeAnimation(colorAnimation);
-          }
-          colorAnimation = null;
-        } else {
-          colorAnimation = new ValueCallbackKeyframeAnimation<>((LottieValueCallback<Integer>) callback);
-          colorAnimation.addUpdateListener(this);
-          addAnimation(colorAnimation);
-        }
+        colorCallbackAnimation = new ValueCallbackKeyframeAnimation<>((LottieValueCallback<Integer>) callback);
+        colorCallbackAnimation.addUpdateListener(this);
+        addAnimation(colorCallbackAnimation);
       }
     } else if (property == LottieProperty.STROKE_COLOR) {
-      if (strokeColorAnimation != null) {
-        strokeColorAnimation.setValueCallback((LottieValueCallback<Integer>) callback);
+      if (strokeColorCallbackAnimation != null) {
+        removeAnimation(strokeColorCallbackAnimation);
+      }
+
+      if (callback == null) {
+        strokeColorCallbackAnimation = null;
       } else {
-        if (callback == null) {
-          if (strokeColorAnimation != null) {
-            removeAnimation(strokeColorAnimation);
-          }
-          strokeColorAnimation = null;
-        } else {
-          strokeColorAnimation = new ValueCallbackKeyframeAnimation<>((LottieValueCallback<Integer>) callback);
-          strokeColorAnimation.addUpdateListener(this);
-          addAnimation(strokeColorAnimation);
-        }
+        strokeColorCallbackAnimation = new ValueCallbackKeyframeAnimation<>((LottieValueCallback<Integer>) callback);
+        strokeColorCallbackAnimation.addUpdateListener(this);
+        addAnimation(strokeColorCallbackAnimation);
       }
     } else if (property == LottieProperty.STROKE_WIDTH) {
-      if (strokeWidthAnimation != null) {
-        strokeWidthAnimation.setValueCallback((LottieValueCallback<Float>) callback);
+      if (strokeWidthCallbackAnimation != null) {
+        removeAnimation(strokeWidthCallbackAnimation);
+      }
+
+      if (callback == null) {
+        strokeWidthCallbackAnimation = null;
       } else {
-        if (callback == null) {
-          if (strokeWidthAnimation != null) {
-            removeAnimation(strokeWidthAnimation);
-          }
-          strokeWidthAnimation = null;
-        } else {
-          strokeWidthAnimation = new ValueCallbackKeyframeAnimation<>((LottieValueCallback<Float>) callback);
-          strokeWidthAnimation.addUpdateListener(this);
-          addAnimation(strokeWidthAnimation);
-        }
+        strokeWidthCallbackAnimation = new ValueCallbackKeyframeAnimation<>((LottieValueCallback<Float>) callback);
+        strokeWidthCallbackAnimation.addUpdateListener(this);
+        addAnimation(strokeWidthCallbackAnimation);
       }
     } else if (property == LottieProperty.TEXT_TRACKING) {
-      if (trackingAnimation != null) {
-        trackingAnimation.setValueCallback((LottieValueCallback<Float>) callback);
+      if (trackingCallbackAnimation != null) {
+        removeAnimation(trackingCallbackAnimation);
+      }
+
+      if (callback == null) {
+        trackingCallbackAnimation = null;
       } else {
-        if (callback == null) {
-          if (trackingAnimation != null) {
-            removeAnimation(trackingAnimation);
-          }
-          trackingAnimation = null;
-        } else {
-          trackingAnimation = new ValueCallbackKeyframeAnimation<>((LottieValueCallback<Float>) callback);
-          trackingAnimation.addUpdateListener(this);
-          addAnimation(trackingAnimation);
-        }
+        trackingCallbackAnimation = new ValueCallbackKeyframeAnimation<>((LottieValueCallback<Float>) callback);
+        trackingCallbackAnimation.addUpdateListener(this);
+        addAnimation(trackingCallbackAnimation);
       }
     } else if (property == LottieProperty.TEXT_SIZE) {
+      if (textSizeCallbackAnimation != null) {
+        removeAnimation(textSizeCallbackAnimation);
+      }
+
       if (callback == null) {
-        if (textSizeAnimation != null) {
-          removeAnimation(textSizeAnimation);
-        }
-        textSizeAnimation = null;
+        textSizeCallbackAnimation = null;
       } else {
-        textSizeAnimation = new ValueCallbackKeyframeAnimation<>((LottieValueCallback<Float>) callback);
-        textSizeAnimation.addUpdateListener(this);
-        addAnimation(textSizeAnimation);
+        textSizeCallbackAnimation = new ValueCallbackKeyframeAnimation<>((LottieValueCallback<Float>) callback);
+        textSizeCallbackAnimation.addUpdateListener(this);
+        addAnimation(textSizeCallbackAnimation);
       }
     }
   }