1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.wallpaper.asset; 17 18 import android.app.Activity; 19 import android.app.ActivityManager; 20 import android.content.Context; 21 import android.graphics.Bitmap; 22 import android.graphics.Rect; 23 import android.util.LruCache; 24 25 import androidx.annotation.Nullable; 26 import androidx.core.app.ActivityManagerCompat; 27 28 import java.util.Objects; 29 30 /** 31 * Implementation of {@link Asset} that wraps another {@link Asset} but keeps an LRU cache of 32 * bitmaps generated by {@link #decodeBitmap(int, int, BitmapReceiver)} to avoid having to decode 33 * the same bitmap multiple times. 34 * The cache key is the wrapped Asset and the target Width and Height requested, so that we only 35 * reuse bitmaps of the same size. 36 */ 37 public class BitmapCachingAsset extends Asset { 38 39 private static class CacheKey { 40 final Asset asset; 41 final int width; 42 final int height; 43 CacheKey(Asset asset, int width, int height)44 CacheKey(Asset asset, int width, int height) { 45 this.asset = asset; 46 this.width = width; 47 this.height = height; 48 } 49 50 @Override hashCode()51 public int hashCode() { 52 return Objects.hash(asset, width, height); 53 } 54 55 @Override equals(Object obj)56 public boolean equals(Object obj) { 57 return obj instanceof CacheKey 58 && ((CacheKey)obj).asset == this.asset 59 && ((CacheKey)obj).width == this.width 60 && ((CacheKey)obj).height == this.height; 61 } 62 } 63 64 private static int cacheSize = 100 * 1024 * 1024; // 100MiB 65 private static LruCache<CacheKey, Bitmap> sCache = new LruCache<CacheKey, Bitmap>(cacheSize) { 66 @Override protected int sizeOf(CacheKey key, Bitmap value) { 67 return value.getByteCount(); 68 } 69 }; 70 71 private final boolean mIsLowRam; 72 private final Asset mOriginalAsset; 73 BitmapCachingAsset(Context context, Asset originalAsset)74 public BitmapCachingAsset(Context context, Asset originalAsset) { 75 mOriginalAsset = originalAsset; 76 mIsLowRam = ActivityManagerCompat.isLowRamDevice( 77 (ActivityManager) context.getApplicationContext().getSystemService( 78 Context.ACTIVITY_SERVICE)); 79 } 80 81 @Override decodeBitmap(int targetWidth, int targetHeight, BitmapReceiver receiver)82 public void decodeBitmap(int targetWidth, int targetHeight, BitmapReceiver receiver) { 83 // Skip the cache in low ram devices 84 if (mIsLowRam) { 85 mOriginalAsset.decodeBitmap(targetWidth, targetHeight, receiver::onBitmapDecoded); 86 return; 87 } 88 CacheKey key = new CacheKey(mOriginalAsset, targetWidth, targetHeight); 89 Bitmap cached = sCache.get(key); 90 if (cached != null) { 91 receiver.onBitmapDecoded(cached); 92 } else { 93 mOriginalAsset.decodeBitmap(targetWidth, targetHeight, bitmap -> { 94 if (bitmap != null) { 95 sCache.put(key, bitmap); 96 } 97 receiver.onBitmapDecoded(bitmap); 98 }); 99 } 100 } 101 102 @Override decodeBitmapRegion(Rect rect, int targetWidth, int targetHeight, BitmapReceiver receiver)103 public void decodeBitmapRegion(Rect rect, int targetWidth, int targetHeight, 104 BitmapReceiver receiver) { 105 mOriginalAsset.decodeBitmapRegion(rect, targetWidth, targetHeight, receiver); 106 } 107 108 @Override decodeRawDimensions(@ullable Activity activity, DimensionsReceiver receiver)109 public void decodeRawDimensions(@Nullable Activity activity, DimensionsReceiver receiver) { 110 mOriginalAsset.decodeRawDimensions(activity, receiver); 111 } 112 113 @Override supportsTiling()114 public boolean supportsTiling() { 115 return mOriginalAsset.supportsTiling(); 116 } 117 } 118