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