content: package com.airbnb.lottie;
  
  import android.graphics.Canvas;
  import android.graphics.DashPathEffect;
  import android.graphics.Matrix;
  import android.graphics.Paint;
  import android.graphics.Path;
  import android.support.annotation.Nullable;
  
  import java.util.ArrayList;
  import java.util.List;
  
  class StrokeContent implements DrawingContent, BaseKeyframeAnimation.AnimationListener {
    private final Path path = new Path();
    private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final LottieDrawable lottieDrawable;
    private final List<PathContent> paths = new ArrayList<>();
    private final float[] dashPatternValues;
  
    private final BaseKeyframeAnimation<?, Integer> colorAnimation;
    private final BaseKeyframeAnimation<?, Float> widthAnimation;
    private final BaseKeyframeAnimation<?, Integer> opacityAnimation;
    private final List<BaseKeyframeAnimation<?, Float>> dashPatternAnimations;
    @Nullable private final BaseKeyframeAnimation<?, Float> dashPatternOffsetAnimation;
  
    StrokeContent(final LottieDrawable lottieDrawable, BaseLayer layer, ShapeStroke stroke) {
      this.lottieDrawable = lottieDrawable;
      paint.setStyle(Paint.Style.STROKE);
      paint.setStrokeCap(stroke.getCapType().toPaintCap());
      paint.setStrokeJoin(stroke.getJoinType().toPaintJoin());
  
      colorAnimation = stroke.getColor().createAnimation();
      opacityAnimation = stroke.getOpacity().createAnimation();
      widthAnimation = stroke.getWidth().createAnimation();
  
      if (stroke.getDashOffset() == null) {
        dashPatternOffsetAnimation = null;
      } else {
        dashPatternOffsetAnimation = stroke.getDashOffset().createAnimation();
      }
      List<AnimatableFloatValue> dashPattern = stroke.getLineDashPattern();
      dashPatternAnimations = new ArrayList<>(dashPattern.size());
      dashPatternValues = new float[dashPattern.size()];
  
      for (int i = 0; i < dashPattern.size(); i++) {
        dashPatternAnimations.add(dashPattern.get(i).createAnimation());
      }
  
      layer.addAnimation(colorAnimation);
      layer.addAnimation(opacityAnimation);
      layer.addAnimation(widthAnimation);
      for (int i = 0; i < dashPatternAnimations.size(); i++) {
        layer.addAnimation(dashPatternAnimations.get(i));
      }
      if (dashPatternOffsetAnimation != null) {
        layer.addAnimation(dashPatternOffsetAnimation);
      }
  
      colorAnimation.addUpdateListener(this);
      opacityAnimation.addUpdateListener(this);
      widthAnimation.addUpdateListener(this);
  
      for (int i = 0; i < dashPattern.size(); i++) {
        dashPatternAnimations.get(i).addUpdateListener(this);
      }
      if (dashPatternOffsetAnimation != null) {
        dashPatternOffsetAnimation.addUpdateListener(this);
      }
    }
  
    @Override public void onValueChanged() {
      lottieDrawable.invalidateSelf();
    }
  
    @Override public void setContents(List<Content> contentsBefore, List<Content> contentsAfter) {
      for (int i = 0; i < contentsAfter.size(); i++) {
        Content content = contentsAfter.get(i);
        if (content instanceof PathContent) {
          paths.add((PathContent) content);
        }
      }
    }
  
    @Override public void draw(Canvas canvas, Matrix parentMatrix, int parentAlpha) {
      paint.setColor(colorAnimation.getValue());
      int alpha = (int) ((parentAlpha / 255f * opacityAnimation.getValue() / 100f) * 255);
      paint.setAlpha(alpha);
      paint.setStrokeWidth(widthAnimation.getValue() * Utils.getScale(parentMatrix));
      if (paint.getStrokeWidth() < 1) {
        // Android draws a hairline stroke for 0, After Effects doesn't.
        return;
      }
      applyDashPatternIfNeeded();
  
      path.reset();
      for (int i = 0; i < paths.size(); i++) {
        this.path.addPath(paths.get(i).getPath(), parentMatrix);
      }
  
      canvas.drawPath(path, paint);
    }
  
    private void applyDashPatternIfNeeded() {
      if (dashPatternAnimations.isEmpty()) {
        return;
      }
  
      float scale = lottieDrawable.getScale();
      for (int i = 0; i < dashPatternAnimations.size(); i++) {
        dashPatternValues[i] = dashPatternAnimations.get(i).getValue();
        // If the value of the dash pattern or gap is too small, the number of individual sections
        // approaches infinity as the value approaches 0.
        // To mitigate this, we essentially put a minimum value on the dash pattern size of 1px
        // and a minimum gap size of 0.01.
        if (i % 2 == 0) {
          if (dashPatternValues[i] < 1f) {
            dashPatternValues[i] = 1f;
          }
        } else {
          if (dashPatternValues[i] < 0.1f) {
            dashPatternValues[i] = 0.1f;
          }
        }
        dashPatternValues[i] *= scale;
      }
      float offset = dashPatternOffsetAnimation == null ? 0f : dashPatternOffsetAnimation.getValue();
      paint.setPathEffect(new DashPathEffect(dashPatternValues, offset));
    }
  }
  