12c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer/* 22c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * Copyright (C) 2016 The Android Open Source Project 32c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * 42c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * Licensed under the Apache License, Version 2.0 (the "License"); 52c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * you may not use this file except in compliance with the License. 62c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * You may obtain a copy of the License at 72c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * 82c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * http://www.apache.org/licenses/LICENSE-2.0 92c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * 102c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * Unless required by applicable law or agreed to in writing, software 112c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * distributed under the License is distributed on an "AS IS" BASIS, 122c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * See the License for the specific language governing permissions and 142c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * limitations under the License. 152c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer */ 162c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerpackage com.android.car.apps.common; 172c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 182c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport android.app.ActivityManager; 192c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport android.content.Context; 202c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport android.content.Intent.ShortcutIconResource; 212c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport android.content.pm.PackageManager.NameNotFoundException; 222c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport android.graphics.Bitmap; 232c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport android.graphics.drawable.BitmapDrawable; 242c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport android.graphics.drawable.Drawable; 252c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport android.util.Log; 262c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport android.util.LruCache; 272c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport android.widget.ImageView; 282c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 292c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport java.lang.ref.SoftReference; 302c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport java.util.ArrayList; 312c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport java.util.concurrent.Executor; 322c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerimport java.util.concurrent.Executors; 332c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 342c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer/** 352c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * Downloader class which loads a resource URI into an image view or triggers a callback 362c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * <p> 372c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * This class adds a LRU cache over DrawableLoader. 382c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * <p> 392c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * Calling getBitmap() or loadBitmap() will return a RefcountBitmapDrawable with initial refcount = 402c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * 2 by the cache table and by caller. You must call releaseRef() when you are done with the resource. 412c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * The most common way is using RefcountImageView, and releaseRef() for you. Once both RefcountImageView 422c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * and LRUCache removes the refcount, the underlying bitmap will be used for decoding new bitmap. 432c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * <p> 442c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * If the URI does not point to a bitmap (e.g. point to a drawable xml, we won't cache it and we 452c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * directly return a regular Drawable). 462c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer */ 472c9394097967d01f79f76148bbaebed5324a529fRakesh Iyerpublic class DrawableDownloader { 482c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 492c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private static final String TAG = "DrawableDownloader"; 502c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 512c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private static final boolean DEBUG = false; 522c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 532c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private static final int CORE_POOL_SIZE = 5; 542c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 552c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer // thread pool for loading non android-resources such as http, content 562c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private static final Executor BITMAP_DOWNLOADER_THREAD_POOL_EXECUTOR = 572c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer Executors.newFixedThreadPool(CORE_POOL_SIZE); 582c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 592c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private static final int CORE_RESOURCE_POOL_SIZE = 1; 602c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 612c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer // thread pool for loading android resources, we use separate thread pool so 622c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer // that network loading will not block local android icons 632c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private static final Executor BITMAP_RESOURCE_DOWNLOADER_THREAD_POOL_EXECUTOR = 642c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer Executors.newFixedThreadPool(CORE_RESOURCE_POOL_SIZE); 652c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 662c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer // 1/4 of max memory is used for bitmap mem cache 672c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private static final int MEM_TO_CACHE = 4; 682c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 692c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer // hard limit for bitmap mem cache in MB 702c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private static final int CACHE_HARD_LIMIT = 32; 712c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 722c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer /** 732c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * bitmap cache item structure saved in LruCache 742c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer */ 752c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private static class BitmapItem { 762c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer int mOriginalWidth; 772c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer int mOriginalHeight; 782c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer ArrayList<BitmapDrawable> mBitmaps = new ArrayList<BitmapDrawable>(3); 792c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer int mByteCount; 802c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer public BitmapItem(int originalWidth, int originalHeight) { 812c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer mOriginalWidth = originalWidth; 822c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer mOriginalHeight = originalHeight; 832c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 842c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 852c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer // get bitmap from the list 862c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer BitmapDrawable findDrawable(BitmapWorkerOptions options) { 872c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer for (int i = 0, c = mBitmaps.size(); i < c; i++) { 882c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer BitmapDrawable d = mBitmaps.get(i); 892c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer // use drawable with original size 902c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (d.getIntrinsicWidth() == mOriginalWidth 912c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer && d.getIntrinsicHeight() == mOriginalHeight) { 922c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return d; 932c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 942c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer // if specified width/height in options and is smaller than 952c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer // cached one, we can use this cached drawable 962c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (options.getHeight() != BitmapWorkerOptions.MAX_IMAGE_DIMENSION_PX) { 972c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (options.getHeight() <= d.getIntrinsicHeight()) { 982c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return d; 992c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1002c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } else if (options.getWidth() != BitmapWorkerOptions.MAX_IMAGE_DIMENSION_PX) { 1012c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (options.getWidth() <= d.getIntrinsicWidth()) { 1022c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return d; 1032c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1042c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1052c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1062c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return null; 1072c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1082c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 1092c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer @SuppressWarnings("unused") 1102c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer BitmapDrawable findLargestDrawable(BitmapWorkerOptions options) { 1112c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return mBitmaps.size() == 0 ? null : mBitmaps.get(0); 1122c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1132c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 1142c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer void addDrawable(BitmapDrawable d) { 1152c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer int i = 0, c = mBitmaps.size(); 1162c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer for (; i < c; i++) { 1172c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer BitmapDrawable drawable = mBitmaps.get(i); 1182c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (drawable.getIntrinsicHeight() < d.getIntrinsicHeight()) { 1192c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer break; 1202c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1212c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1222c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer mBitmaps.add(i, d); 1232c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer mByteCount += RecycleBitmapPool.getSize(d.getBitmap()); 1242c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1252c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 1262c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer void clear() { 1272c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer for (int i = 0, c = mBitmaps.size(); i < c; i++) { 1282c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer BitmapDrawable d = mBitmaps.get(i); 1292c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (d instanceof RefcountBitmapDrawable) { 1302c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer ((RefcountBitmapDrawable) d).getRefcountObject().releaseRef(); 1312c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1322c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1332c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer mBitmaps.clear(); 1342c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer mByteCount = 0; 1352c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1362c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1372c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 1382c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer public static abstract class BitmapCallback { 1392c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer SoftReference<DrawableLoader> mTask; 1402c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 1412c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer public abstract void onBitmapRetrieved(Drawable bitmap); 1422c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1432c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 1442c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private Context mContext; 1452c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private LruCache<String, BitmapItem> mMemoryCache; 1462c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private RecycleBitmapPool mRecycledBitmaps; 1472c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 1482c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private static DrawableDownloader sBitmapDownloader; 1492c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 1502c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private static final Object sBitmapDownloaderLock = new Object(); 1512c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 1522c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer /** 1532c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * get the singleton BitmapDownloader for the application 1542c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer */ 1552c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer public final static DrawableDownloader getInstance(Context context) { 1562c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (sBitmapDownloader == null) { 1572c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer synchronized(sBitmapDownloaderLock) { 1582c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (sBitmapDownloader == null) { 1592c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer sBitmapDownloader = new DrawableDownloader(context); 1602c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1612c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1622c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1632c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return sBitmapDownloader; 1642c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1652c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 1662c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private static String getBucketKey(String baseKey, Bitmap.Config bitmapConfig) { 1672c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return new StringBuilder(baseKey.length() + 16).append(baseKey) 1682c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer .append(":").append(bitmapConfig == null ? "" : bitmapConfig.ordinal()) 1692c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer .toString(); 1702c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1712c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 1722c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer public static Drawable getDrawable(Context context, ShortcutIconResource iconResource) 1732c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer throws NameNotFoundException { 1742c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return DrawableLoader.getDrawable(context, iconResource); 1752c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1762c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 1772c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private DrawableDownloader(Context context) { 1782c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer mContext = context; 1792c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)) 1802c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer .getMemoryClass(); 1812c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer memClass = memClass / MEM_TO_CACHE; 1822c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (memClass > CACHE_HARD_LIMIT) { 1832c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer memClass = CACHE_HARD_LIMIT; 1842c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1852c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer int cacheSize = 1024 * 1024 * memClass; 1862c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer mMemoryCache = new LruCache<String, BitmapItem>(cacheSize) { 1872c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer @Override 1882c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer protected int sizeOf(String key, BitmapItem bitmap) { 1892c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return bitmap.mByteCount; 1902c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1912c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer @Override 1922c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer protected void entryRemoved( 1932c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer boolean evicted, String key, BitmapItem oldValue, BitmapItem newValue) { 1942c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (evicted) { 1952c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer oldValue.clear(); 1962c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1972c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 1982c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer }; 1992c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer mRecycledBitmaps = new RecycleBitmapPool(); 2002c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2012c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 2022c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer /** 2032c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * trim memory cache to 0~1 * maxSize 2042c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer */ 2052c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer public void trimTo(float amount) { 2062c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (amount == 0f) { 2072c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer mMemoryCache.evictAll(); 2082c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } else { 2092c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer mMemoryCache.trimToSize((int) (amount * mMemoryCache.maxSize())); 2102c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2112c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2122c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 2132c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer /** 2142c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * load bitmap in current thread, will *block* current thread. 2152c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * @deprecated 2162c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer */ 2172c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer @Deprecated 2182c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer public final Drawable loadBitmapBlocking(BitmapWorkerOptions options) { 2192c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer final boolean hasAccountImageUri = UriUtils.isAccountImageUri(options.getResourceUri()); 2202c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer Drawable bitmap = null; 2212c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (hasAccountImageUri) { 2222c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer AccountImageChangeObserver.getInstance().registerChangeUriIfPresent(options); 2232c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } else { 2242c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer bitmap = getBitmapFromMemCache(options); 2252c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2262c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 2272c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (bitmap == null) { 2282c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer DrawableLoader task = new DrawableLoader(null, mRecycledBitmaps) { 2292c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer @Override 2302c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer protected Drawable doInBackground(BitmapWorkerOptions... params) { 2312c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer final Drawable bitmap = super.doInBackground(params); 2322c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (bitmap != null && !hasAccountImageUri) { 2332c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer addBitmapToMemoryCache(params[0], bitmap, this); 2342c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2352c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return bitmap; 2362c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2372c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer }; 2382c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return task.doInBackground(options); 2392c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2402c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return bitmap; 2412c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2422c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 2432c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer /** 2442c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * Loads the bitmap into the image view. 2452c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer */ 2462c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer public void loadBitmap(BitmapWorkerOptions options, final ImageView imageView) { 2472c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer cancelDownload(imageView); 2482c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer final boolean hasAccountImageUri = UriUtils.isAccountImageUri(options.getResourceUri()); 2492c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer Drawable bitmap = null; 2502c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (hasAccountImageUri) { 2512c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer AccountImageChangeObserver.getInstance().registerChangeUriIfPresent(options); 2522c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } else { 2532c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer bitmap = getBitmapFromMemCache(options); 2542c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2552c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 2562c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (bitmap != null) { 2572c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer imageView.setImageDrawable(bitmap); 2582c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } else { 2592c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer DrawableLoader task = new DrawableLoader(imageView, mRecycledBitmaps) { 2602c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer @Override 2612c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer protected Drawable doInBackground(BitmapWorkerOptions... params) { 2622c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer Drawable bitmap = super.doInBackground(params); 2632c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (bitmap != null && !hasAccountImageUri) { 2642c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer addBitmapToMemoryCache(params[0], bitmap, this); 2652c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2662c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return bitmap; 2672c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2682c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer }; 2692c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer imageView.setTag(R.id.imageDownloadTask, new SoftReference<DrawableLoader>(task)); 2702c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer scheduleTask(task, options); 2712c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2722c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2732c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 2742c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer /** 2752c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * Loads the bitmap. 2762c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * <p> 2772c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * This will be sent back to the callback object. 2782c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer */ 2792c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer public void getBitmap(BitmapWorkerOptions options, final BitmapCallback callback) { 2802c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer cancelDownload(callback); 2812c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer final boolean hasAccountImageUri = UriUtils.isAccountImageUri(options.getResourceUri()); 2822c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer final Drawable bitmap = hasAccountImageUri ? null : getBitmapFromMemCache(options); 2832c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (hasAccountImageUri) { 2842c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer AccountImageChangeObserver.getInstance().registerChangeUriIfPresent(options); 2852c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2862c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 2872c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (bitmap != null) { 2882c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer callback.onBitmapRetrieved(bitmap); 2892c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return; 2902c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2912c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer DrawableLoader task = new DrawableLoader(null, mRecycledBitmaps) { 2922c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer @Override 2932c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer protected Drawable doInBackground(BitmapWorkerOptions... params) { 2942c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer final Drawable bitmap = super.doInBackground(params); 2952c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (bitmap != null && !hasAccountImageUri) { 2962c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer addBitmapToMemoryCache(params[0], bitmap, this); 2972c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 2982c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return bitmap; 2992c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3002c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 3012c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer @Override 3022c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer protected void onPostExecute(Drawable bitmap) { 3032c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer callback.onBitmapRetrieved(bitmap); 3042c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3052c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer }; 3062c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer callback.mTask = new SoftReference<DrawableLoader>(task); 3072c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer scheduleTask(task, options); 3082c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3092c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 3102c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private static void scheduleTask(DrawableLoader task, BitmapWorkerOptions options) { 3112c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (options.isFromResource()) { 3122c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer task.executeOnExecutor(BITMAP_RESOURCE_DOWNLOADER_THREAD_POOL_EXECUTOR, options); 3132c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } else { 3142c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer task.executeOnExecutor(BITMAP_DOWNLOADER_THREAD_POOL_EXECUTOR, options); 3152c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3162c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3172c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 3182c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer /** 3192c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * Cancel download<p> 3202c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer * @param key {@link BitmapCallback} or {@link ImageView} 3212c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer */ 3222c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer @SuppressWarnings("unchecked") 3232c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer public boolean cancelDownload(Object key) { 3242c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer DrawableLoader task = null; 3252c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (key instanceof ImageView) { 3262c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer ImageView imageView = (ImageView)key; 3272c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer SoftReference<DrawableLoader> softReference = 3282c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer (SoftReference<DrawableLoader>) imageView.getTag(R.id.imageDownloadTask); 3292c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (softReference != null) { 3302c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer task = softReference.get(); 3312c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer softReference.clear(); 3322c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3332c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } else if (key instanceof BitmapCallback) { 3342c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer BitmapCallback callback = (BitmapCallback)key; 3352c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (callback.mTask != null) { 3362c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer task = callback.mTask.get(); 3372c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer callback.mTask = null; 3382c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3392c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3402c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (task != null) { 3412c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return task.cancel(true); 3422c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3432c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return false; 3442c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3452c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 3462c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private void addBitmapToMemoryCache(BitmapWorkerOptions key, Drawable bitmap, 3472c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer DrawableLoader loader) { 3482c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (!key.isMemCacheEnabled()) { 3492c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return; 3502c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3512c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (!(bitmap instanceof BitmapDrawable)) { 3522c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return; 3532c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3542c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer String bucketKey = getBucketKey(key.getCacheKey(), key.getBitmapConfig()); 3552c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer BitmapItem bitmapItem = mMemoryCache.get(bucketKey); 3562c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (DEBUG) { 3572c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer Log.d(TAG, "add cache "+bucketKey); 3582c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3592c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (bitmapItem != null) { 3602c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer // remove and re-add to update size 3612c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer mMemoryCache.remove(bucketKey); 3622c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } else { 3632c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer bitmapItem = new BitmapItem(loader.getOriginalWidth(), loader.getOriginalHeight()); 3642c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3652c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (bitmap instanceof RefcountBitmapDrawable) { 3662c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer RefcountBitmapDrawable refcountDrawable = (RefcountBitmapDrawable) bitmap; 3672c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer refcountDrawable.getRefcountObject().addRef(); 3682c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3692c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer bitmapItem.addDrawable((BitmapDrawable) bitmap); 3702c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer mMemoryCache.put(bucketKey, bitmapItem); 3712c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3722c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 3732c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private Drawable getBitmapFromMemCache(BitmapWorkerOptions key) { 3742c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer String bucketKey = 3752c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer getBucketKey(key.getCacheKey(), key.getBitmapConfig()); 3762c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer BitmapItem item = mMemoryCache.get(bucketKey); 3772c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (item != null) { 3782c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return createRefCopy(item.findDrawable(key)); 3792c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3802c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return null; 3812c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3822c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 3832c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer public BitmapDrawable getLargestBitmapFromMemCache(BitmapWorkerOptions key) { 3842c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer String bucketKey = 3852c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer getBucketKey(key.getCacheKey(), key.getBitmapConfig()); 3862c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer BitmapItem item = mMemoryCache.get(bucketKey); 3872c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (item != null) { 3882c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return (BitmapDrawable) createRefCopy(item.findLargestDrawable(key)); 3892c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3902c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return null; 3912c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 3922c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 3932c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer private Drawable createRefCopy(Drawable d) { 3942c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (d != null) { 3952c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer if (d instanceof RefcountBitmapDrawable) { 3962c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer RefcountBitmapDrawable refcountDrawable = (RefcountBitmapDrawable) d; 3972c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer refcountDrawable.getRefcountObject().addRef(); 3982c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer d = new RefcountBitmapDrawable(mContext.getResources(), 3992c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer refcountDrawable); 4002c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 4012c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return d; 4022c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 4032c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer return null; 4042c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer } 4052c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer 4062c9394097967d01f79f76148bbaebed5324a529fRakesh Iyer} 407