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