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