1a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim/* 2a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * Copyright (C) 2015 The Android Open Source Project 3a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * 4a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * Licensed under the Apache License, Version 2.0 (the "License"); 5a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * you may not use this file except in compliance with the License. 6a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * You may obtain a copy of the License at 7a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * 8a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * http://www.apache.org/licenses/LICENSE-2.0 9a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * 10a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * Unless required by applicable law or agreed to in writing, software 11a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * distributed under the License is distributed on an "AS IS" BASIS, 12a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * See the License for the specific language governing permissions and 14a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * limitations under the License. 15a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim */ 16a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 17a907614755847b2630561a1e5949b2b416600d97Sungsoo Limpackage com.example.android.supportv4.media; 18a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 19a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.graphics.Bitmap; 20a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.os.AsyncTask; 217a37b5e2010dd1e9a8775fc217b0e7043ed06417Donghyun Choimport android.support.v4.graphics.BitmapCompat; 227a37b5e2010dd1e9a8775fc217b0e7043ed06417Donghyun Choimport android.support.v4.util.LruCache; 23a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.util.Log; 24a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 25a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport com.example.android.supportv4.media.utils.BitmapHelper; 26a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 27a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.io.IOException; 28a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 29a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim/** 30a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * Implements a basic cache of album arts, with async loading support. 31a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim */ 32a907614755847b2630561a1e5949b2b416600d97Sungsoo Limpublic final class AlbumArtCache { 33a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim private static final String TAG = "AlbumArtCache"; 34a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 35a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim private static final int MAX_ALBUM_ART_CACHE_SIZE = 12*1024*1024; // 12 MB 36a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim private static final int MAX_ART_WIDTH = 800; // pixels 37a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim private static final int MAX_ART_HEIGHT = 480; // pixels 38a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 39a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim // Resolution reasonable for carrying around as an icon (generally in 40a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim // MediaDescription.getIconBitmap). This should not be bigger than necessary, because 41a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim // the MediaDescription object should be lightweight. If you set it too high and try to 42a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim // serialize the MediaDescription, you may get FAILED BINDER TRANSACTION errors. 43a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim private static final int MAX_ART_WIDTH_ICON = 128; // pixels 44a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim private static final int MAX_ART_HEIGHT_ICON = 128; // pixels 45a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 46a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim private static final int BIG_BITMAP_INDEX = 0; 47a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim private static final int ICON_BITMAP_INDEX = 1; 48a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 49a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim private final LruCache<String, Bitmap[]> mCache; 50a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 51a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim private static final AlbumArtCache sInstance = new AlbumArtCache(); 52a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 53a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim public static AlbumArtCache getInstance() { 54a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim return sInstance; 55a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } 56a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 57a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim private AlbumArtCache() { 58a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim // Holds no more than MAX_ALBUM_ART_CACHE_SIZE bytes, bounded by maxmemory/4 and 59a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim // Integer.MAX_VALUE: 60a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim int maxSize = Math.min(MAX_ALBUM_ART_CACHE_SIZE, 61a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim (int) (Math.min(Integer.MAX_VALUE, Runtime.getRuntime().maxMemory()/4))); 62a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim mCache = new LruCache<String, Bitmap[]>(maxSize) { 63a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim @Override 64a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim protected int sizeOf(String key, Bitmap[] value) { 657a37b5e2010dd1e9a8775fc217b0e7043ed06417Donghyun Cho return BitmapCompat.getAllocationByteCount(value[BIG_BITMAP_INDEX]) 667a37b5e2010dd1e9a8775fc217b0e7043ed06417Donghyun Cho + BitmapCompat.getAllocationByteCount(value[ICON_BITMAP_INDEX]); 67a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } 68a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim }; 69a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } 70a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 71a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim public Bitmap getBigImage(String artUrl) { 72a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim Bitmap[] result = mCache.get(artUrl); 73a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim return result == null ? null : result[BIG_BITMAP_INDEX]; 74a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } 75a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 76a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim public Bitmap getIconImage(String artUrl) { 77a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim Bitmap[] result = mCache.get(artUrl); 78a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim return result == null ? null : result[ICON_BITMAP_INDEX]; 79a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } 80a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 81a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim public void fetch(final String artUrl, final FetchListener listener) { 82a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim // WARNING: for the sake of simplicity, simultaneous multi-thread fetch requests 83a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim // are not handled properly: they may cause redundant costly operations, like HTTP 84a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim // requests and bitmap rescales. For production-level apps, we recommend you use 85a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim // a proper image loading library, like Glide. 86a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim Bitmap[] bitmap = mCache.get(artUrl); 87a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim if (bitmap != null) { 88a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim Log.d(TAG, "getOrFetch: album art is in cache, using it " + artUrl); 89a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim listener.onFetched(artUrl, bitmap[BIG_BITMAP_INDEX], bitmap[ICON_BITMAP_INDEX]); 90a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim return; 91a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } 92a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim Log.d(TAG, "getOrFetch: starting asynctask to fetch " + artUrl); 93a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 94a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim new AsyncTask<Void, Void, Bitmap[]>() { 95a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim @Override 96a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim protected Bitmap[] doInBackground(Void[] objects) { 97a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim Bitmap[] bitmaps; 98a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim try { 99a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim Bitmap bitmap = BitmapHelper.fetchAndRescaleBitmap(artUrl, 100a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim MAX_ART_WIDTH, MAX_ART_HEIGHT); 101a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim Bitmap icon = BitmapHelper.scaleBitmap(bitmap, 102a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim MAX_ART_WIDTH_ICON, MAX_ART_HEIGHT_ICON); 103a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim bitmaps = new Bitmap[] {bitmap, icon}; 104a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim mCache.put(artUrl, bitmaps); 105a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } catch (IOException e) { 106a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim return null; 107a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } 108a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim Log.d(TAG, "doInBackground: putting bitmap in cache. cache size=" + mCache.size()); 109a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim return bitmaps; 110a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } 111a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 112a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim @Override 113a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim protected void onPostExecute(Bitmap[] bitmaps) { 114a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim if (bitmaps == null) { 115a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim listener.onError(artUrl, new IllegalArgumentException("got null bitmaps")); 116a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } else { 117a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim listener.onFetched(artUrl, 118a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim bitmaps[BIG_BITMAP_INDEX], bitmaps[ICON_BITMAP_INDEX]); 119a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } 120a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } 121a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim }.execute(); 122a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } 123a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim 124a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim public static abstract class FetchListener { 125a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim public abstract void onFetched(String artUrl, Bitmap bigImage, Bitmap iconImage); 126a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim public void onError(String artUrl, Exception e) { 127a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim Log.e(TAG, "AlbumArtFetchListener: error while downloading " + artUrl, e); 128a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } 129a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim } 130a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim} 131