• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.airbnb.lottie.manager;
2 
3 import android.app.Application;
4 import android.content.Context;
5 import android.graphics.Bitmap;
6 import android.graphics.BitmapFactory;
7 import android.graphics.drawable.Drawable;
8 import android.text.TextUtils;
9 import android.util.Base64;
10 import android.view.View;
11 
12 import androidx.annotation.Nullable;
13 
14 import com.airbnb.lottie.ImageAssetDelegate;
15 import com.airbnb.lottie.LottieImageAsset;
16 import com.airbnb.lottie.utils.Logger;
17 import com.airbnb.lottie.utils.Utils;
18 
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.util.Map;
22 
23 public class ImageAssetManager {
24   private static final Object bitmapHashLock = new Object();
25   @Nullable private final Context context;
26   private final String imagesFolder;
27   @Nullable private ImageAssetDelegate delegate;
28   private final Map<String, LottieImageAsset> imageAssets;
29 
ImageAssetManager(Drawable.Callback callback, String imagesFolder, ImageAssetDelegate delegate, Map<String, LottieImageAsset> imageAssets)30   public ImageAssetManager(Drawable.Callback callback, String imagesFolder,
31       ImageAssetDelegate delegate, Map<String, LottieImageAsset> imageAssets) {
32     if (!TextUtils.isEmpty(imagesFolder) && imagesFolder.charAt(imagesFolder.length() - 1) != '/') {
33       this.imagesFolder = imagesFolder + '/';
34     } else {
35       this.imagesFolder = imagesFolder;
36     }
37     this.imageAssets = imageAssets;
38     setDelegate(delegate);
39     if (!(callback instanceof View)) {
40       context = null;
41       return;
42     }
43 
44     context = ((View) callback).getContext().getApplicationContext();
45   }
46 
setDelegate(@ullable ImageAssetDelegate assetDelegate)47   public void setDelegate(@Nullable ImageAssetDelegate assetDelegate) {
48     this.delegate = assetDelegate;
49   }
50 
51   /**
52    * Returns the previously set bitmap or null.
53    */
updateBitmap(String id, @Nullable Bitmap bitmap)54   @Nullable public Bitmap updateBitmap(String id, @Nullable Bitmap bitmap) {
55     if (bitmap == null) {
56       LottieImageAsset asset = imageAssets.get(id);
57       Bitmap ret = asset.getBitmap();
58       asset.setBitmap(null);
59       return ret;
60     }
61     Bitmap prevBitmap = imageAssets.get(id).getBitmap();
62     putBitmap(id, bitmap);
63     return prevBitmap;
64   }
65 
getImageAssetById(String id)66   @Nullable public LottieImageAsset getImageAssetById(String id) {
67     return imageAssets.get(id);
68   }
69 
bitmapForId(String id)70   @Nullable public Bitmap bitmapForId(String id) {
71     LottieImageAsset asset = imageAssets.get(id);
72     if (asset == null) {
73       return null;
74     }
75 
76     Bitmap bitmap = asset.getBitmap();
77     if (bitmap != null) {
78       return bitmap;
79     }
80 
81     if (delegate != null) {
82       bitmap = delegate.fetchBitmap(asset);
83       if (bitmap != null) {
84         putBitmap(id, bitmap);
85       }
86       return bitmap;
87     }
88     Context context = this.context;
89     if (context == null) {
90       // If there is no context, the image has to be embedded or provided via
91       // a delegate.
92       return null;
93     }
94 
95     String filename = asset.getFileName();
96     BitmapFactory.Options opts = new BitmapFactory.Options();
97     opts.inScaled = true;
98     opts.inDensity = 160;
99 
100     if (filename.startsWith("data:") && filename.indexOf("base64,") > 0) {
101       // Contents look like a base64 data URI, with the format data:image/png;base64,<data>.
102       byte[] data;
103       try {
104         data = Base64.decode(filename.substring(filename.indexOf(',') + 1), Base64.DEFAULT);
105       } catch (IllegalArgumentException e) {
106         Logger.warning("data URL did not have correct base64 format.", e);
107         return null;
108       }
109       bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opts);
110       Bitmap resizedBitmap = Utils.resizeBitmapIfNeeded(bitmap, asset.getWidth(), asset.getHeight());
111       return putBitmap(id, resizedBitmap);
112     }
113 
114     InputStream is;
115     try {
116       if (TextUtils.isEmpty(imagesFolder)) {
117         throw new IllegalStateException("You must set an images folder before loading an image." +
118             " Set it with LottieComposition#setImagesFolder or LottieDrawable#setImagesFolder");
119       }
120       is = context.getAssets().open(imagesFolder + filename);
121     } catch (IOException e) {
122       Logger.warning("Unable to open asset.", e);
123       return null;
124     }
125 
126     try {
127       bitmap = BitmapFactory.decodeStream(is, null, opts);
128     } catch (IllegalArgumentException e) {
129       Logger.warning("Unable to decode image `" + id + "`.", e);
130       return null;
131     }
132     if (bitmap == null) {
133       Logger.warning("Decoded image `" + id + "` is null.");
134       return null;
135     }
136     bitmap = Utils.resizeBitmapIfNeeded(bitmap, asset.getWidth(), asset.getHeight());
137     return putBitmap(id, bitmap);
138   }
139 
hasSameContext(Context context)140   public boolean hasSameContext(Context context) {
141     if (context == null) {
142       return this.context == null;
143     }
144     Context contextToCompare = this.context instanceof Application ? context.getApplicationContext() : context;
145     return contextToCompare == this.context;
146   }
147 
putBitmap(String key, @Nullable Bitmap bitmap)148   private Bitmap putBitmap(String key, @Nullable Bitmap bitmap) {
149     synchronized (bitmapHashLock) {
150       imageAssets.get(key).setBitmap(bitmap);
151       return bitmap;
152     }
153   }
154 }
155