package com.airbnb.lottie.utils;

import android.animation.Animator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.os.Build;

import java.util.HashSet;
import java.util.Set;

public abstract class BaseLottieAnimator extends ValueAnimator {
  private final Set<ValueAnimator.AnimatorUpdateListener> updateListeners = new HashSet<>();
  private final Set<AnimatorListener> listeners = new HashSet<>();

  @Override public long getStartDelay() {
    throw new UnsupportedOperationException("LottieAnimator does not support getStartDelay.");
  }

  @Override public void setStartDelay(long startDelay) {
    throw new UnsupportedOperationException("LottieAnimator does not support setStartDelay.");
  }
  @Override public ValueAnimator setDuration(long duration) {
    throw new UnsupportedOperationException("LottieAnimator does not support setDuration.");
  }

  @Override public void setInterpolator(TimeInterpolator value) {
    throw new UnsupportedOperationException("LottieAnimator does not support setInterpolator.");
  }

  public void addUpdateListener(ValueAnimator.AnimatorUpdateListener listener) {
    updateListeners.add(listener);
  }

  public void removeUpdateListener(ValueAnimator.AnimatorUpdateListener listener) {
    updateListeners.remove(listener);
  }

  public void removeAllUpdateListeners() {
    updateListeners.clear();
  }

  public void addListener(ValueAnimator.AnimatorListener listener) {
    listeners.add(listener);
  }

  public void removeListener(ValueAnimator.AnimatorListener listener) {
    listeners.remove(listener);
  }

  public void removeAllListeners() {
    listeners.clear();
  }

  void notifyStart(boolean isReverse) {
    for (Animator.AnimatorListener listener : listeners) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        listener.onAnimationStart(this, isReverse);
      } else {
        listener.onAnimationStart(this);
      }
    }
  }

  void notifyRepeat() {
    for (Animator.AnimatorListener listener : listeners) {
      listener.onAnimationRepeat(this);
    }
  }

  void notifyEnd(boolean isReverse) {
    for (Animator.AnimatorListener listener : listeners) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        listener.onAnimationEnd(this, isReverse);
      } else {
        listener.onAnimationEnd(this);
      }
    }
  }

  void notifyCancel() {
    for (Animator.AnimatorListener listener : listeners) {
      listener.onAnimationCancel(this);
    }
  }

  void notifyUpdate() {
    for (ValueAnimator.AnimatorUpdateListener listener : updateListeners) {
      listener.onAnimationUpdate(this);
    }
  }
}

@@ -188,20 +188,16 @@
 va.util.
-Hash
 Set;%0Aimp
@@ -206,24 +206,51 @@
 t java.util.
+concurrent.CopyOnWriteArray
 Set;%0A%0Apublic
@@ -380,36 +380,48 @@
 Listeners = new 
-Hash
+CopyOnWriteArray
 Set%3C%3E();%0A  priva
@@ -471,12 +471,24 @@
 new 
-Hash
+CopyOnWriteArray
 Set%3C

Use thread-safe set for listeners. Closes #639 (#642)