blob: bc38a9e440faf4c33295f9e0311f214eaf89a102 [file] [log] [blame]
Santiago Etchebehere888e4782019-04-12 12:54:18 -03001/*
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 */
16package com.android.wallpaper.asset;
17
18import android.app.Activity;
Santiago Etchebehere12806752019-04-15 15:33:47 -030019import android.app.ActivityManager;
20import android.content.Context;
Santiago Etchebehere888e4782019-04-12 12:54:18 -030021import android.graphics.Bitmap;
22import android.graphics.Rect;
23import android.util.LruCache;
Ching-Sung Libd001672021-04-24 12:33:04 +080024import android.widget.ImageView;
Santiago Etchebehere888e4782019-04-12 12:54:18 -030025
26import androidx.annotation.Nullable;
Santiago Etchebehere12806752019-04-15 15:33:47 -030027import androidx.core.app.ActivityManagerCompat;
Santiago Etchebehere888e4782019-04-12 12:54:18 -030028
29import java.util.Objects;
30
31/**
32 * Implementation of {@link Asset} that wraps another {@link Asset} but keeps an LRU cache of
33 * bitmaps generated by {@link #decodeBitmap(int, int, BitmapReceiver)} to avoid having to decode
34 * the same bitmap multiple times.
35 * The cache key is the wrapped Asset and the target Width and Height requested, so that we only
36 * reuse bitmaps of the same size.
37 */
38public class BitmapCachingAsset extends Asset {
39
40 private static class CacheKey {
Santiago Etchebehere69934b32021-04-19 16:44:15 -070041 final Asset mAsset;
42 final int mWidth;
43 final int mHeight;
44 final boolean mRtl;
45 final Rect mRect;
Santiago Etchebehere888e4782019-04-12 12:54:18 -030046
47 CacheKey(Asset asset, int width, int height) {
Santiago Etchebehere69934b32021-04-19 16:44:15 -070048 this(asset, width, height, false, null);
49 }
50
51 CacheKey(Asset asset, int width, int height, boolean rtl, Rect rect) {
52 mAsset = asset;
53 mWidth = width;
54 mHeight = height;
55 mRtl = rtl;
56 mRect = rect;
Santiago Etchebehere888e4782019-04-12 12:54:18 -030057 }
58
59 @Override
60 public int hashCode() {
Santiago Etchebehere69934b32021-04-19 16:44:15 -070061 return Objects.hash(mAsset, mWidth, mHeight);
Santiago Etchebehere888e4782019-04-12 12:54:18 -030062 }
63
64 @Override
65 public boolean equals(Object obj) {
66 return obj instanceof CacheKey
Santiago Etchebehere69934b32021-04-19 16:44:15 -070067 && (Objects.equals(this.mAsset, ((CacheKey) obj).mAsset))
68 && ((CacheKey) obj).mWidth == this.mWidth
69 && ((CacheKey) obj).mHeight == this.mHeight
70 && ((CacheKey) obj).mRtl == this.mRtl
71 && (Objects.equals(this.mRect, ((CacheKey) obj).mRect));
Santiago Etchebehere888e4782019-04-12 12:54:18 -030072 }
73 }
74
75 private static int cacheSize = 100 * 1024 * 1024; // 100MiB
76 private static LruCache<CacheKey, Bitmap> sCache = new LruCache<CacheKey, Bitmap>(cacheSize) {
77 @Override protected int sizeOf(CacheKey key, Bitmap value) {
78 return value.getByteCount();
79 }
80 };
81
Santiago Etchebehere12806752019-04-15 15:33:47 -030082 private final boolean mIsLowRam;
Santiago Etchebehere888e4782019-04-12 12:54:18 -030083 private final Asset mOriginalAsset;
84
Santiago Etchebehere12806752019-04-15 15:33:47 -030085 public BitmapCachingAsset(Context context, Asset originalAsset) {
Santiago Etchebehere69934b32021-04-19 16:44:15 -070086 mOriginalAsset = originalAsset instanceof BitmapCachingAsset
87 ? ((BitmapCachingAsset) originalAsset).mOriginalAsset : originalAsset;
Santiago Etchebehere12806752019-04-15 15:33:47 -030088 mIsLowRam = ActivityManagerCompat.isLowRamDevice(
89 (ActivityManager) context.getApplicationContext().getSystemService(
90 Context.ACTIVITY_SERVICE));
Santiago Etchebehere888e4782019-04-12 12:54:18 -030091 }
92
93 @Override
94 public void decodeBitmap(int targetWidth, int targetHeight, BitmapReceiver receiver) {
Santiago Etchebehere12806752019-04-15 15:33:47 -030095 // Skip the cache in low ram devices
96 if (mIsLowRam) {
97 mOriginalAsset.decodeBitmap(targetWidth, targetHeight, receiver::onBitmapDecoded);
98 return;
99 }
Santiago Etchebehere888e4782019-04-12 12:54:18 -0300100 CacheKey key = new CacheKey(mOriginalAsset, targetWidth, targetHeight);
101 Bitmap cached = sCache.get(key);
102 if (cached != null) {
103 receiver.onBitmapDecoded(cached);
104 } else {
105 mOriginalAsset.decodeBitmap(targetWidth, targetHeight, bitmap -> {
106 if (bitmap != null) {
107 sCache.put(key, bitmap);
108 }
109 receiver.onBitmapDecoded(bitmap);
110 });
111 }
112 }
113
114 @Override
115 public void decodeBitmapRegion(Rect rect, int targetWidth, int targetHeight,
Santiago Etchebehere16cf0602020-12-11 17:04:18 -0800116 boolean shouldAdjustForRtl, BitmapReceiver receiver) {
Santiago Etchebehere69934b32021-04-19 16:44:15 -0700117 // Skip the cache in low ram devices
118 if (mIsLowRam) {
119 mOriginalAsset.decodeBitmapRegion(rect, targetWidth, targetHeight, shouldAdjustForRtl,
120 receiver);
121 return;
122 }
123 CacheKey key = new CacheKey(mOriginalAsset, targetWidth, targetHeight, shouldAdjustForRtl,
124 rect);
125 Bitmap cached = sCache.get(key);
126 if (cached != null) {
127 receiver.onBitmapDecoded(cached);
128 } else {
129 mOriginalAsset.decodeBitmapRegion(rect, targetWidth, targetHeight, shouldAdjustForRtl,
130 bitmap -> {
131 if (bitmap != null) {
132 sCache.put(key, bitmap);
133 }
134 receiver.onBitmapDecoded(bitmap);
135 });
136 }
Santiago Etchebehere888e4782019-04-12 12:54:18 -0300137 }
138
139 @Override
140 public void decodeRawDimensions(@Nullable Activity activity, DimensionsReceiver receiver) {
141 mOriginalAsset.decodeRawDimensions(activity, receiver);
142 }
143
144 @Override
145 public boolean supportsTiling() {
146 return mOriginalAsset.supportsTiling();
147 }
Ching-Sung Libd001672021-04-24 12:33:04 +0800148
149 @Override
150 public void loadPreviewImage(Activity activity, ImageView imageView, int placeholderColor) {
151 // Honor the original Asset's preview image loading
152 mOriginalAsset.loadPreviewImage(activity, imageView, placeholderColor);
153 }
Santiago Etchebehere888e4782019-04-12 12:54:18 -0300154}