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