• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.airbnb.lottie;
2 
3 import android.content.Context;
4 import android.content.res.Resources;
5 import android.graphics.Rect;
6 
7 import androidx.annotation.NonNull;
8 import androidx.annotation.Nullable;
9 import androidx.annotation.RawRes;
10 import androidx.annotation.RestrictTo;
11 import androidx.annotation.WorkerThread;
12 import androidx.collection.LongSparseArray;
13 import androidx.collection.SparseArrayCompat;
14 
15 import com.airbnb.lottie.model.Font;
16 import com.airbnb.lottie.model.FontCharacter;
17 import com.airbnb.lottie.model.Marker;
18 import com.airbnb.lottie.model.layer.Layer;
19 import com.airbnb.lottie.parser.moshi.JsonReader;
20 import com.airbnb.lottie.utils.Logger;
21 import com.airbnb.lottie.utils.MiscUtils;
22 
23 import org.json.JSONObject;
24 
25 import java.io.InputStream;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 
32 /**
33  * After Effects/Bodymovin composition model. This is the serialized model from which the
34  * animation will be created. It is designed to be stateless, cacheable, and shareable.
35  * <p>
36  * To create one, use {@link LottieCompositionFactory}.
37  * <p>
38  * It can be used with a {@link com.airbnb.lottie.LottieAnimationView} or
39  * {@link com.airbnb.lottie.LottieDrawable}.
40  */
41 public class LottieComposition {
42 
43   private final PerformanceTracker performanceTracker = new PerformanceTracker();
44   private final HashSet<String> warnings = new HashSet<>();
45   private Map<String, List<Layer>> precomps;
46   private Map<String, LottieImageAsset> images;
47   /**
48    * Map of font names to fonts
49    */
50   private Map<String, Font> fonts;
51   private List<Marker> markers;
52   private SparseArrayCompat<FontCharacter> characters;
53   private LongSparseArray<Layer> layerMap;
54   private List<Layer> layers;
55   // This is stored as a set to avoid duplicates.
56   private Rect bounds;
57   private float startFrame;
58   private float endFrame;
59   private float frameRate;
60   /**
61    * Used to determine if an animation can be drawn with hardware acceleration.
62    */
63   private boolean hasDashPattern;
64   /**
65    * Counts the number of mattes and masks. Before Android switched to SKIA
66    * for drawing in Oreo (API 28), using hardware acceleration with mattes and masks
67    * was only faster until you had ~4 masks after which it would actually become slower.
68    */
69   private int maskAndMatteCount = 0;
70 
71   @RestrictTo(RestrictTo.Scope.LIBRARY)
init(Rect bounds, float startFrame, float endFrame, float frameRate, List<Layer> layers, LongSparseArray<Layer> layerMap, Map<String, List<Layer>> precomps, Map<String, LottieImageAsset> images, SparseArrayCompat<FontCharacter> characters, Map<String, Font> fonts, List<Marker> markers)72   public void init(Rect bounds, float startFrame, float endFrame, float frameRate,
73       List<Layer> layers, LongSparseArray<Layer> layerMap, Map<String,
74       List<Layer>> precomps, Map<String, LottieImageAsset> images,
75       SparseArrayCompat<FontCharacter> characters, Map<String, Font> fonts,
76       List<Marker> markers) {
77     this.bounds = bounds;
78     this.startFrame = startFrame;
79     this.endFrame = endFrame;
80     this.frameRate = frameRate;
81     this.layers = layers;
82     this.layerMap = layerMap;
83     this.precomps = precomps;
84     this.images = images;
85     this.characters = characters;
86     this.fonts = fonts;
87     this.markers = markers;
88   }
89 
90   @RestrictTo(RestrictTo.Scope.LIBRARY)
addWarning(String warning)91   public void addWarning(String warning) {
92     Logger.warning(warning);
93     warnings.add(warning);
94   }
95 
96   @RestrictTo(RestrictTo.Scope.LIBRARY)
setHasDashPattern(boolean hasDashPattern)97   public void setHasDashPattern(boolean hasDashPattern) {
98     this.hasDashPattern = hasDashPattern;
99   }
100 
101   @RestrictTo(RestrictTo.Scope.LIBRARY)
incrementMatteOrMaskCount(int amount)102   public void incrementMatteOrMaskCount(int amount) {
103     maskAndMatteCount += amount;
104   }
105 
106   /**
107    * Used to determine if an animation can be drawn with hardware acceleration.
108    */
109   @RestrictTo(RestrictTo.Scope.LIBRARY)
hasDashPattern()110   public boolean hasDashPattern() {
111     return hasDashPattern;
112   }
113 
114   /**
115    * Used to determine if an animation can be drawn with hardware acceleration.
116    */
117   @RestrictTo(RestrictTo.Scope.LIBRARY)
getMaskAndMatteCount()118   public int getMaskAndMatteCount() {
119     return maskAndMatteCount;
120   }
121 
getWarnings()122   public ArrayList<String> getWarnings() {
123     return new ArrayList<>(Arrays.asList(warnings.toArray(new String[warnings.size()])));
124   }
125 
setPerformanceTrackingEnabled(boolean enabled)126   @SuppressWarnings("WeakerAccess") public void setPerformanceTrackingEnabled(boolean enabled) {
127     performanceTracker.setEnabled(enabled);
128   }
129 
getPerformanceTracker()130   public PerformanceTracker getPerformanceTracker() {
131     return performanceTracker;
132   }
133 
134   @RestrictTo(RestrictTo.Scope.LIBRARY)
layerModelForId(long id)135   public Layer layerModelForId(long id) {
136     return layerMap.get(id);
137   }
138 
getBounds()139   @SuppressWarnings("WeakerAccess") public Rect getBounds() {
140     return bounds;
141   }
142 
getDuration()143   @SuppressWarnings("WeakerAccess") public float getDuration() {
144     return (long) (getDurationFrames() / frameRate * 1000);
145   }
146 
getStartFrame()147   public float getStartFrame() {
148     return startFrame;
149   }
150 
getEndFrame()151   public float getEndFrame() {
152     return endFrame;
153   }
154 
getFrameForProgress(float progress)155   public float getFrameForProgress(float progress) {
156     return MiscUtils.lerp(startFrame, endFrame, progress);
157   }
158 
getProgressForFrame(float frame)159   public float getProgressForFrame(float frame) {
160     float framesSinceStart = frame - startFrame;
161     float frameRange = endFrame - startFrame;
162     return framesSinceStart / frameRange;
163   }
164 
getFrameRate()165   public float getFrameRate() {
166     return frameRate;
167   }
168 
getLayers()169   public List<Layer> getLayers() {
170     return layers;
171   }
172 
173   @RestrictTo(RestrictTo.Scope.LIBRARY)
174   @Nullable
getPrecomps(String id)175   public List<Layer> getPrecomps(String id) {
176     return precomps.get(id);
177   }
178 
getCharacters()179   public SparseArrayCompat<FontCharacter> getCharacters() {
180     return characters;
181   }
182 
getFonts()183   public Map<String, Font> getFonts() {
184     return fonts;
185   }
186 
getMarkers()187   public List<Marker> getMarkers() {
188     return markers;
189   }
190 
191   @Nullable
getMarker(String markerName)192   public Marker getMarker(String markerName) {
193     int size = markers.size();
194     for (int i = 0; i < size; i++) {
195       Marker marker = markers.get(i);
196       if (marker.matchesName(markerName)) {
197         return marker;
198       }
199     }
200     return null;
201   }
202 
hasImages()203   public boolean hasImages() {
204     return !images.isEmpty();
205   }
206 
207   /**
208    * Returns a map of image asset id to {@link LottieImageAsset}. These assets contain image metadata exported
209    * from After Effects or other design tool. The resulting Bitmaps can be set directly on the image asset so
210    * they can be loaded once and reused across compositions.
211    */
getImages()212   public Map<String, LottieImageAsset> getImages() {
213     return images;
214   }
215 
getDurationFrames()216   public float getDurationFrames() {
217     return endFrame - startFrame;
218   }
219 
220 
221   @NonNull
222   @Override
toString()223   public String toString() {
224     final StringBuilder sb = new StringBuilder("LottieComposition:\n");
225     for (Layer layer : layers) {
226       sb.append(layer.toString("\t"));
227     }
228     return sb.toString();
229   }
230 
231   /**
232    * This will be removed in the next version of Lottie. {@link LottieCompositionFactory} has improved
233    * API names, failure handlers, and will return in-progress tasks so you will never parse the same
234    * animation twice in parallel.
235    *
236    * @see LottieCompositionFactory
237    */
238   @Deprecated
239   public static class Factory {
Factory()240     private Factory() {
241     }
242 
243     /**
244      * @see LottieCompositionFactory#fromAsset(Context, String)
245      */
246     @SuppressWarnings("deprecation")
247     @Deprecated
fromAssetFileName(Context context, String fileName, OnCompositionLoadedListener l)248     public static Cancellable fromAssetFileName(Context context, String fileName, OnCompositionLoadedListener l) {
249       ListenerAdapter listener = new ListenerAdapter(l);
250       LottieCompositionFactory.fromAsset(context, fileName).addListener(listener);
251       return listener;
252     }
253 
254     /**
255      * @see LottieCompositionFactory#fromRawRes(Context, int)
256      */
257     @SuppressWarnings("deprecation")
258     @Deprecated
fromRawFile(Context context, @RawRes int resId, OnCompositionLoadedListener l)259     public static Cancellable fromRawFile(Context context, @RawRes int resId, OnCompositionLoadedListener l) {
260       ListenerAdapter listener = new ListenerAdapter(l);
261       LottieCompositionFactory.fromRawRes(context, resId).addListener(listener);
262       return listener;
263     }
264 
265     /**
266      * @see LottieCompositionFactory#fromJsonInputStream(InputStream, String)
267      */
268     @SuppressWarnings("deprecation")
269     @Deprecated
fromInputStream(InputStream stream, OnCompositionLoadedListener l)270     public static Cancellable fromInputStream(InputStream stream, OnCompositionLoadedListener l) {
271       ListenerAdapter listener = new ListenerAdapter(l);
272       LottieCompositionFactory.fromJsonInputStream(stream, null).addListener(listener);
273       return listener;
274     }
275 
276     /**
277      * @see LottieCompositionFactory#fromJsonString(String, String)
278      */
279     @SuppressWarnings("deprecation")
280     @Deprecated
fromJsonString(String jsonString, OnCompositionLoadedListener l)281     public static Cancellable fromJsonString(String jsonString, OnCompositionLoadedListener l) {
282       ListenerAdapter listener = new ListenerAdapter(l);
283       LottieCompositionFactory.fromJsonString(jsonString, null).addListener(listener);
284       return listener;
285     }
286 
287     /**
288      * @see LottieCompositionFactory#fromJsonReader(JsonReader, String)
289      */
290     @SuppressWarnings("deprecation")
291     @Deprecated
fromJsonReader(JsonReader reader, OnCompositionLoadedListener l)292     public static Cancellable fromJsonReader(JsonReader reader, OnCompositionLoadedListener l) {
293       ListenerAdapter listener = new ListenerAdapter(l);
294       LottieCompositionFactory.fromJsonReader(reader, null).addListener(listener);
295       return listener;
296     }
297 
298     /**
299      * @see LottieCompositionFactory#fromAssetSync(Context, String)
300      */
301     @Nullable
302     @WorkerThread
303     @Deprecated
fromFileSync(Context context, String fileName)304     public static LottieComposition fromFileSync(Context context, String fileName) {
305       return LottieCompositionFactory.fromAssetSync(context, fileName).getValue();
306     }
307 
308     /**
309      * @see LottieCompositionFactory#fromJsonInputStreamSync(InputStream, String)
310      */
311     @Nullable
312     @WorkerThread
313     @Deprecated
fromInputStreamSync(InputStream stream)314     public static LottieComposition fromInputStreamSync(InputStream stream) {
315       return LottieCompositionFactory.fromJsonInputStreamSync(stream, null).getValue();
316     }
317 
318     /**
319      * This will now auto-close the input stream!
320      *
321      * @see LottieCompositionFactory#fromJsonInputStreamSync(InputStream, String)
322      */
323     @Nullable
324     @WorkerThread
325     @Deprecated
fromInputStreamSync(InputStream stream, boolean close)326     public static LottieComposition fromInputStreamSync(InputStream stream, boolean close) {
327       if (close) {
328         Logger.warning("Lottie now auto-closes input stream!");
329       }
330       return LottieCompositionFactory.fromJsonInputStreamSync(stream, null).getValue();
331     }
332 
333     /**
334      * @see LottieCompositionFactory#fromJsonSync(JSONObject, String)
335      */
336     @Nullable
337     @WorkerThread
338     @Deprecated
fromJsonSync(@uppressWarnings"unused") Resources res, JSONObject json)339     public static LottieComposition fromJsonSync(@SuppressWarnings("unused") Resources res, JSONObject json) {
340       //noinspection deprecation
341       return LottieCompositionFactory.fromJsonSync(json, null).getValue();
342     }
343 
344     /**
345      * @see LottieCompositionFactory#fromJsonStringSync(String, String)
346      */
347     @Nullable
348     @WorkerThread
349     @Deprecated
fromJsonSync(String json)350     public static LottieComposition fromJsonSync(String json) {
351       return LottieCompositionFactory.fromJsonStringSync(json, null).getValue();
352     }
353 
354     /**
355      * @see LottieCompositionFactory#fromJsonReaderSync(JsonReader, String)
356      */
357     @Nullable
358     @WorkerThread
359     @Deprecated
fromJsonSync(JsonReader reader)360     public static LottieComposition fromJsonSync(JsonReader reader) {
361       return LottieCompositionFactory.fromJsonReaderSync(reader, null).getValue();
362     }
363 
364     @SuppressWarnings("deprecation")
365     private static final class ListenerAdapter implements LottieListener<LottieComposition>, Cancellable {
366       private final OnCompositionLoadedListener listener;
367       private boolean cancelled = false;
368 
ListenerAdapter(OnCompositionLoadedListener listener)369       private ListenerAdapter(OnCompositionLoadedListener listener) {
370         this.listener = listener;
371       }
372 
onResult(LottieComposition composition)373       @Override public void onResult(LottieComposition composition) {
374         if (cancelled) {
375           return;
376         }
377         listener.onCompositionLoaded(composition);
378       }
379 
cancel()380       @Override public void cancel() {
381         cancelled = true;
382       }
383     }
384   }
385 }