content: package com.airbnb.lottie;
  
  import android.content.Context;
  import android.content.res.Resources;
  import android.graphics.Rect;
  
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  import androidx.annotation.RawRes;
  import androidx.annotation.RestrictTo;
  import androidx.annotation.WorkerThread;
  import androidx.collection.LongSparseArray;
  import androidx.collection.SparseArrayCompat;
  
  import com.airbnb.lottie.model.Font;
  import com.airbnb.lottie.model.FontCharacter;
  import com.airbnb.lottie.model.Marker;
  import com.airbnb.lottie.model.layer.Layer;
  import com.airbnb.lottie.parser.moshi.JsonReader;
  import com.airbnb.lottie.utils.Logger;
  import com.airbnb.lottie.utils.MiscUtils;
  import com.airbnb.lottie.utils.Utils;
  
  import org.json.JSONObject;
  
  import java.io.InputStream;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  /**
   * After Effects/Bodymovin composition model. This is the serialized model from which the
   * animation will be created. It is designed to be stateless, cacheable, and shareable.
   * <p>
   * To create one, use {@link LottieCompositionFactory}.
   * <p>
   * It can be used with a {@link com.airbnb.lottie.LottieAnimationView} or
   * {@link com.airbnb.lottie.LottieDrawable}.
   */
  public class LottieComposition {
  
    private final PerformanceTracker performanceTracker = new PerformanceTracker();
    private final HashSet<String> warnings = new HashSet<>();
    private Map<String, List<Layer>> precomps;
    private Map<String, LottieImageAsset> images;
    private float imagesDpScale;
    /**
     * Map of font names to fonts
     */
    private Map<String, Font> fonts;
    private List<Marker> markers;
    private SparseArrayCompat<FontCharacter> characters;
    private LongSparseArray<Layer> layerMap;
    private List<Layer> layers;
    // This is stored as a set to avoid duplicates.
    private Rect bounds;
    private float startFrame;
    private float endFrame;
    private float frameRate;
    /**
     * Used to determine if an animation can be drawn with hardware acceleration.
     */
    private boolean hasDashPattern;
    /**
     * Counts the number of mattes and masks. Before Android switched to SKIA
     * for drawing in Oreo (API 28), using hardware acceleration with mattes and masks
     * was only faster until you had ~4 masks after which it would actually become slower.
     */
    private int maskAndMatteCount = 0;
  
    private int unscaledWidth;
    private int unscaledHeight;
  
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public void init(Rect bounds, float startFrame, float endFrame, float frameRate,
        List<Layer> layers, LongSparseArray<Layer> layerMap, Map<String,
        List<Layer>> precomps, Map<String, LottieImageAsset> images, float imagesDpScale,
        SparseArrayCompat<FontCharacter> characters, Map<String, Font> fonts,
        List<Marker> markers, int unscaledWidth, int unscaledHeight) {
      this.bounds = bounds;
      this.startFrame = startFrame;
      this.endFrame = endFrame;
      this.frameRate = frameRate;
      this.layers = layers;
      this.layerMap = layerMap;
      this.precomps = precomps;
      this.images = images;
      this.imagesDpScale = imagesDpScale;
      this.characters = characters;
      this.fonts = fonts;
      this.markers = markers;
      this.unscaledWidth = unscaledWidth;
      this.unscaledHeight = unscaledHeight;
    }
  
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public void addWarning(String warning) {
      Logger.warning(warning);
      warnings.add(warning);
    }
  
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public void setHasDashPattern(boolean hasDashPattern) {
      this.hasDashPattern = hasDashPattern;
    }
  
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public void incrementMatteOrMaskCount(int amount) {
      maskAndMatteCount += amount;
    }
  
    /**
     * Used to determine if an animation can be drawn with hardware acceleration.
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public boolean hasDashPattern() {
      return hasDashPattern;
    }
  
    /**
     * Used to determine if an animation can be drawn with hardware acceleration.
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public int getMaskAndMatteCount() {
      return maskAndMatteCount;
    }
  
    public ArrayList<String> getWarnings() {
      return new ArrayList<>(Arrays.asList(warnings.toArray(new String[warnings.size()])));
    }
  
    @SuppressWarnings("WeakerAccess") public void setPerformanceTrackingEnabled(boolean enabled) {
      performanceTracker.setEnabled(enabled);
    }
  
    public PerformanceTracker getPerformanceTracker() {
      return performanceTracker;
    }
  
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public Layer layerModelForId(long id) {
      return layerMap.get(id);
    }
  
    @SuppressWarnings("WeakerAccess") public Rect getBounds() {
      return bounds;
    }
  
    @SuppressWarnings("WeakerAccess") public float getDuration() {
      return (long) (getDurationFrames() / frameRate * 1000);
    }
  
    public float getStartFrame() {
      return startFrame;
    }
  
    public float getEndFrame() {
      return endFrame;
    }
  
    public float getFrameForProgress(float progress) {
      return MiscUtils.lerp(startFrame, endFrame, progress);
    }
  
    public float getProgressForFrame(float frame) {
      float framesSinceStart = frame - startFrame;
      float frameRange = endFrame - startFrame;
      return framesSinceStart / frameRange;
    }
  
    public float getFrameRate() {
      return frameRate;
    }
  
    public List<Layer> getLayers() {
      return layers;
    }
  
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    @Nullable
    public List<Layer> getPrecomps(String id) {
      return precomps.get(id);
    }
  
    public SparseArrayCompat<FontCharacter> getCharacters() {
      return characters;
    }
  
    public Map<String, Font> getFonts() {
      return fonts;
    }
  
    public List<Marker> getMarkers() {
      return markers;
    }
  
    @Nullable
    public Marker getMarker(String markerName) {
      int size = markers.size();
      for (int i = 0; i < size; i++) {
        Marker marker = markers.get(i);
        if (marker.matchesName(markerName)) {
          return marker;
        }
      }
      return null;
    }
  
    public boolean hasImages() {
      return !images.isEmpty();
    }
  
    /**
     * Returns a map of image asset id to {@link LottieImageAsset}. These assets contain image metadata exported
     * from After Effects or other design tool. The resulting Bitmaps can be set directly on the image asset so
     * they can be loaded once and reused across compositions.
     *
     * If the context dp scale has changed since the last time images were retrieved, images will be rescaled.
     */
    public Map<String, LottieImageAsset> getImages() {
      float dpScale = Utils.dpScale();
      if (dpScale != imagesDpScale) {
        Set<Map.Entry<String, LottieImageAsset>> entries = images.entrySet();
  
        for (Map.Entry<String, LottieImageAsset> entry : entries) {
          images.put(entry.getKey(), entry.getValue().copyWithScale(imagesDpScale / dpScale));
        }
      }
      imagesDpScale = dpScale;
      return images;
    }
  
    public float getDurationFrames() {
      return endFrame - startFrame;
    }
  
    public int getUnscaledWidth() {
      return unscaledWidth;
    }
  
    public int getUnscaledHeight() {
      return unscaledHeight;
    }
  
    @NonNull
    @Override
    public String toString() {
      final StringBuilder sb = new StringBuilder("LottieComposition:\n");
      for (Layer layer : layers) {
        sb.append(layer.toString("\t"));
      }
      return sb.toString();
    }
  
    /**
     * This will be removed in the next version of Lottie. {@link LottieCompositionFactory} has improved
     * API names, failure handlers, and will return in-progress tasks so you will never parse the same
     * animation twice in parallel.
     *
     * @see LottieCompositionFactory
     */
    @Deprecated
    public static class Factory {
  
      private Factory() {
      }
  
      /**
       * @see LottieCompositionFactory#fromAsset(Context, String)
       */
      @SuppressWarnings("deprecation")
      @Deprecated
      public static Cancellable fromAssetFileName(Context context, String fileName, OnCompositionLoadedListener l) {
        ListenerAdapter listener = new ListenerAdapter(l);
        LottieCompositionFactory.fromAsset(context, fileName).addListener(listener);
        return listener;
      }
  
      /**
       * @see LottieCompositionFactory#fromRawRes(Context, int)
       */
      @SuppressWarnings("deprecation")
      @Deprecated
      public static Cancellable fromRawFile(Context context, @RawRes int resId, OnCompositionLoadedListener l) {
        ListenerAdapter listener = new ListenerAdapter(l);
        LottieCompositionFactory.fromRawRes(context, resId).addListener(listener);
        return listener;
      }
  
      /**
       * @see LottieCompositionFactory#fromJsonInputStream(InputStream, String)
       */
      @SuppressWarnings("deprecation")
      @Deprecated
      public static Cancellable fromInputStream(InputStream stream, OnCompositionLoadedListener l) {
        ListenerAdapter listener = new ListenerAdapter(l);
        LottieCompositionFactory.fromJsonInputStream(stream, null).addListener(listener);
        return listener;
      }
  
      /**
       * @see LottieCompositionFactory#fromJsonString(String, String)
       */
      @SuppressWarnings("deprecation")
      @Deprecated
      public static Cancellable fromJsonString(String jsonString, OnCompositionLoadedListener l) {
        ListenerAdapter listener = new ListenerAdapter(l);
        LottieCompositionFactory.fromJsonString(jsonString, null).addListener(listener);
        return listener;
      }
  
      /**
       * @see LottieCompositionFactory#fromJsonReader(JsonReader, String)
       */
      @SuppressWarnings("deprecation")
      @Deprecated
      public static Cancellable fromJsonReader(JsonReader reader, OnCompositionLoadedListener l) {
        ListenerAdapter listener = new ListenerAdapter(l);
        LottieCompositionFactory.fromJsonReader(reader, null).addListener(listener);
        return listener;
      }
  
      /**
       * @see LottieCompositionFactory#fromAssetSync(Context, String)
       */
      @Nullable
      @WorkerThread
      @Deprecated
      public static LottieComposition fromFileSync(Context context, String fileName) {
        return LottieCompositionFactory.fromAssetSync(context, fileName).getValue();
      }
  
      /**
       * @see LottieCompositionFactory#fromJsonInputStreamSync(InputStream, String)
       */
      @Nullable
      @WorkerThread
      @Deprecated
      public static LottieComposition fromInputStreamSync(InputStream stream) {
        return LottieCompositionFactory.fromJsonInputStreamSync(stream, null).getValue();
      }
  
      /**
       * This will now auto-close the input stream!
       *
       * @see LottieCompositionFactory#fromJsonInputStreamSync(InputStream, String)
       */
      @Nullable
      @WorkerThread
      @Deprecated
      public static LottieComposition fromInputStreamSync(InputStream stream, boolean close) {
        if (close) {
          Logger.warning("Lottie now auto-closes input stream!");
        }
        return LottieCompositionFactory.fromJsonInputStreamSync(stream, null).getValue();
      }
  
      /**
       * @see LottieCompositionFactory#fromJsonSync(JSONObject, String)
       */
      @Nullable
      @WorkerThread
      @Deprecated
      public static LottieComposition fromJsonSync(@SuppressWarnings("unused") Resources res, JSONObject json) {
        //noinspection deprecation
        return LottieCompositionFactory.fromJsonSync(json, null).getValue();
      }
  
      /**
       * @see LottieCompositionFactory#fromJsonStringSync(String, String)
       */
      @Nullable
      @WorkerThread
      @Deprecated
      public static LottieComposition fromJsonSync(String json) {
        return LottieCompositionFactory.fromJsonStringSync(json, null).getValue();
      }
  
      /**
       * @see LottieCompositionFactory#fromJsonReaderSync(JsonReader, String)
       */
      @Nullable
      @WorkerThread
      @Deprecated
      public static LottieComposition fromJsonSync(JsonReader reader) {
        return LottieCompositionFactory.fromJsonReaderSync(reader, null).getValue();
      }
  
      @SuppressWarnings("deprecation")
      private static final class ListenerAdapter implements LottieListener<LottieComposition>, Cancellable {
  
        private final OnCompositionLoadedListener listener;
        private boolean cancelled = false;
  
        private ListenerAdapter(OnCompositionLoadedListener listener) {
          this.listener = listener;
        }
  
        @Override public void onResult(LottieComposition composition) {
          if (cancelled) {
            return;
          }
          listener.onCompositionLoaded(composition);
        }
  
        @Override public void cancel() {
          cancelled = true;
        }
      }
    }
  }
  