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