ThumbnailManager.java revision 51e4621fa12400b1e79cc18b7bb0f9a83af6b622
151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor// Copyright 2012 Google Inc. All Rights Reserved. 251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorpackage com.android.mms.util; 451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.Set; 651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.content.Context; 851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.graphics.Bitmap; 951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.graphics.BitmapFactory; 1051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.net.Uri; 1151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.util.Log; 1251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 1351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport com.android.mms.LogTag; 1451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport com.android.mms.ui.UriImage; 1551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 1651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor/** 1751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Primary {@link ThumbnailManager} implementation used by {@link MessagingApplication}. 1851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * <p> 1951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Public methods should only be used from a single thread (typically the UI 2051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * thread). Callbacks will be invoked on the thread where the ThumbnailManager 2151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * was instantiated. 2251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * <p> 2351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Uses a thread-pool ExecutorService instead of AsyncTasks since clients may 2451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * request lots of pdus around the same time, and AsyncTask may reject tasks 2551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * in that case and has no way of bounding the number of threads used by those 2651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * tasks. 2751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * <p> 2851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * ThumbnailManager is used to asynchronously load pictures and create thumbnails. The thumbnails 2951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * are stored in a local cache with SoftReferences. Once a thumbnail is loaded, it will call the 3051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * passed in callback with the result. If a thumbnail is immediately available in the cache, 3151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * the callback will be called immediately as well. 3251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * 3351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Based on BooksImageManager by Virgil King. 3451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor */ 3551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorpublic class ThumbnailManager extends BackgroundLoaderManager { 3651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private static final String TAG = "ThumbnailManager"; 3751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 3851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private static final boolean DEBUG_DISABLE_LOAD = false; 3951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private static final boolean DEBUG_LONG_WAIT = false; 4051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 4151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private static final int THUMBNAIL_BOUNDS_LIMIT = 480; 4251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private static final int PICTURE_SIZE_LIMIT = 20 * 1024; 4351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 4451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private final SimpleCache<Uri, Bitmap> mThumbnailCache; 4551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private final Context mContext; 4651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 4751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public ThumbnailManager(final Context context) { 4851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor super(context); 4951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 5051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mThumbnailCache = new SimpleCache<Uri, Bitmap>(32, 256, 0.75f); 5151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mContext = context; 5251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 5351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 5451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor /** 5551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * getThumbnail must be called on the same thread that created ThumbnailManager. This is 5651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * normally the UI thread. 5751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @param uri the uri of the image 5851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @param width the original full width of the image 5951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @param height the original full height of the image 6051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @param callback the callback to call when the thumbnail is fully loaded 6151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @return 6251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor */ 6351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public ItemLoadedFuture getThumbnail(Uri uri, int width, int height, 6451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final ItemLoadedCallback<Bitmap> callback) { 6551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (uri == null) { 6651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor throw new NullPointerException(); 6751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 6851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 6951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final Bitmap thumbnail = mThumbnailCache.get(uri); 7051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 7151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final boolean thumbnailExists = (thumbnail != null); 7251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final boolean taskExists = mPendingTaskUris.contains(uri); 7351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final boolean newTaskRequired = !thumbnailExists && !taskExists; 7451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final boolean callbackRequired = (callback != null); 7551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 7651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (Log.isLoggable(LogTag.THUMBNAIL_CACHE, Log.DEBUG)) { 7751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.v(TAG, "getThumbnail mThumbnailCache.get for uri: " + uri + " thumbnail: " + 7851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor thumbnail + " callback: " + callback + " thumbnailExists: " + 7951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor thumbnailExists + " taskExists: " + taskExists + 8051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor " newTaskRequired: " + newTaskRequired + 8151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor " callbackRequired: " + callbackRequired); 8251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 8351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 8451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (thumbnailExists) { 8551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (callbackRequired) { 8651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor callback.onItemLoaded(thumbnail, null); 8751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 8851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor return new NullItemLoadedFuture(); 8951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 9051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 9151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (callbackRequired) { 9251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor addCallback(uri, callback); 9351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 9451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 9551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (newTaskRequired) { 9651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mPendingTaskUris.add(uri); 9751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Runnable task = new ThumbnailTask(uri, width, height); 9851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mExecutor.execute(task); 9951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 10051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor return new ItemLoadedFuture() { 10151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public void cancel() { 10251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor cancelCallback(callback); 10351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 10451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public boolean isDone() { 10551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor return false; 10651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 10751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor }; 10851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 10951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 11051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor @Override 11151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public void clear() { 11251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor super.clear(); 11351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 11451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mThumbnailCache.clear(); 11551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 11651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 11751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public String getTag() { 11851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor return TAG; 11951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 12051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 12151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public class ThumbnailTask implements Runnable { 12251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private final Uri mUri; 12351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private final int mWidth; 12451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private final int mHeight; 12551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 12651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public ThumbnailTask(Uri uri, int width, int height) { 12751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (uri == null) { 12851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor throw new NullPointerException(); 12951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 13051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mUri = uri; 13151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mWidth = width; 13251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mHeight = height; 13351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 13451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 13551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor /** {@inheritDoc} */ 13651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public void run() { 13751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (DEBUG_DISABLE_LOAD) { 13851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor return; 13951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 14051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (DEBUG_LONG_WAIT) { 14151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor try { 14251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Thread.sleep(10000); 14351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } catch (InterruptedException e) { 14451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 14551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 14651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 14751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor byte[] data = UriImage.getResizedImageData(mWidth, mHeight, 14851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor THUMBNAIL_BOUNDS_LIMIT, THUMBNAIL_BOUNDS_LIMIT, 14951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor PICTURE_SIZE_LIMIT, mUri, mContext); 15051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (Log.isLoggable(LogTag.THUMBNAIL_CACHE, Log.DEBUG)) { 15151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.v(TAG, "createBitmap size: " + (data == null ? data : data.length)); 15251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 15351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Bitmap bitmap = data == null ? null : 15451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor BitmapFactory.decodeByteArray(data, 0, data.length); 15551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (Log.isLoggable(LogTag.THUMBNAIL_CACHE, Log.DEBUG) && bitmap == null) { 15651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.v(TAG, "DECODED BITMAP IS NULL!!!"); 15751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 15851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 15951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final Bitmap resultBitmap = bitmap; 16051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mCallbackHandler.post(new Runnable() { 16151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public void run() { 16251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final Set<ItemLoadedCallback> callbacks = mCallbacks.get(mUri); 16351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (callbacks != null) { 16451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor // Make a copy so that the callback can unregister itself 16551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor for (final ItemLoadedCallback<Bitmap> callback : asList(callbacks)) { 16651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (Log.isLoggable(TAG, Log.DEBUG)) { 16751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.d(TAG, "Invoking item loaded callback " + callback); 16851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 16951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor callback.onItemLoaded(resultBitmap, null); 17051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 17151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } else { 17251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (Log.isLoggable(TAG, Log.DEBUG)) { 17351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.d(TAG, "No image callback!"); 17451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 17551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 17651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 17751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor // Add the bitmap to the soft cache if the load succeeded 17851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (resultBitmap != null) { 17951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mThumbnailCache.put(mUri, resultBitmap); 18051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (Log.isLoggable(TAG, Log.DEBUG)) { 18151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.v(TAG, "in callback runnable: bitmap uri: " + mUri + 18251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor " width: " + resultBitmap.getWidth() + " height: " + 18351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor resultBitmap.getHeight() + " size: " + 18451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor resultBitmap.getByteCount()); 18551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 18651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 18751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 18851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mCallbacks.remove(mUri); 18951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mPendingTaskUris.remove(mUri); 19051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 19151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (Log.isLoggable(LogTag.THUMBNAIL_CACHE, Log.DEBUG)) { 19251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.d(TAG, "Image task for " + mUri + "exiting " + mPendingTaskUris.size() 19351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor + " remain"); 19451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 19551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 19651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor }); 19751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 19851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 19951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor} 200