107ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor/* 207ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * Copyright (C) 2012 The Android Open Source Project 307ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * 407ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * Licensed under the Apache License, Version 2.0 (the "License"); 507ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * you may not use this file except in compliance with the License. 607ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * You may obtain a copy of the License at 707ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * 807ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * http://www.apache.org/licenses/LICENSE-2.0 907ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * 1007ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * Unless required by applicable law or agreed to in writing, software 1107ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * distributed under the License is distributed on an "AS IS" BASIS, 1207ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1307ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * See the License for the specific language governing permissions and 1407ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * limitations under the License. 1507ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor */ 1651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 1751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorpackage com.android.mms.util; 1851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 19721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport java.io.ByteArrayOutputStream; 20721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport java.io.Closeable; 21721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport java.io.FileNotFoundException; 22721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport java.io.InputStream; 2351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.Set; 2451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 2551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.content.Context; 2651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.graphics.Bitmap; 27d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport android.graphics.Bitmap.Config; 2851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.graphics.BitmapFactory; 29d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport android.graphics.BitmapFactory.Options; 30721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport android.graphics.Canvas; 31721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport android.graphics.Paint; 3279bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylorimport android.media.MediaMetadataRetriever; 3351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.net.Uri; 3451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.util.Log; 3551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 3651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport com.android.mms.LogTag; 37721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport com.android.mms.R; 38b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylorimport com.android.mms.TempFileProvider; 3951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport com.android.mms.ui.UriImage; 40721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport com.android.mms.util.ImageCacheService.ImageData; 4151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 4251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor/** 4351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Primary {@link ThumbnailManager} implementation used by {@link MessagingApplication}. 4451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * <p> 4551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Public methods should only be used from a single thread (typically the UI 4651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * thread). Callbacks will be invoked on the thread where the ThumbnailManager 4751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * was instantiated. 4851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * <p> 4951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Uses a thread-pool ExecutorService instead of AsyncTasks since clients may 5051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * request lots of pdus around the same time, and AsyncTask may reject tasks 5151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * in that case and has no way of bounding the number of threads used by those 5251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * tasks. 5351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * <p> 5451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * ThumbnailManager is used to asynchronously load pictures and create thumbnails. The thumbnails 5551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * are stored in a local cache with SoftReferences. Once a thumbnail is loaded, it will call the 5651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * passed in callback with the result. If a thumbnail is immediately available in the cache, 5751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * the callback will be called immediately as well. 5851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * 5951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Based on BooksImageManager by Virgil King. 6051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor */ 6151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorpublic class ThumbnailManager extends BackgroundLoaderManager { 6251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private static final String TAG = "ThumbnailManager"; 6351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 64721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private static final boolean DEBUG_DISABLE_CACHE = false; 65721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private static final boolean DEBUG_DISABLE_CALLBACK = false; 6651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private static final boolean DEBUG_DISABLE_LOAD = false; 6751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private static final boolean DEBUG_LONG_WAIT = false; 6851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 69721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private static final int COMPRESS_JPEG_QUALITY = 90; 7051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 7151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private final SimpleCache<Uri, Bitmap> mThumbnailCache; 7251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private final Context mContext; 73721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private ImageCacheService mImageCacheService; 74721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private static Bitmap mEmptyImageBitmap; 75721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private static Bitmap mEmptyVideoBitmap; 76721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 77721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // NOTE: These type numbers are stored in the image cache, so it should not 78721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // not be changed without resetting the cache. 79721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor public static final int TYPE_THUMBNAIL = 1; 80721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor public static final int TYPE_MICROTHUMBNAIL = 2; 81721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 82721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor public static final int THUMBNAIL_TARGET_SIZE = 640; 8351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 8451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public ThumbnailManager(final Context context) { 8551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor super(context); 8651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 87721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor mThumbnailCache = new SimpleCache<Uri, Bitmap>(8, 16, 0.75f, true); 8851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mContext = context; 89721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 90721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor mEmptyImageBitmap = BitmapFactory.decodeResource(context.getResources(), 91721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor R.drawable.ic_missing_thumbnail_picture); 92721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 93721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor mEmptyVideoBitmap = BitmapFactory.decodeResource(context.getResources(), 94721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor R.drawable.ic_missing_thumbnail_video); 9551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 9651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 9751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor /** 9851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * getThumbnail must be called on the same thread that created ThumbnailManager. This is 9951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * normally the UI thread. 10051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @param uri the uri of the image 10151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @param width the original full width of the image 10251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @param height the original full height of the image 10351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @param callback the callback to call when the thumbnail is fully loaded 10451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * @return 10551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor */ 106721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor public ItemLoadedFuture getThumbnail(Uri uri, 10779bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor final ItemLoadedCallback<ImageLoaded> callback) { 108721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return getThumbnail(uri, false, callback); 10979bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor } 11079bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor 11179bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor /** 11279bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor * getVideoThumbnail must be called on the same thread that created ThumbnailManager. This is 11379bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor * normally the UI thread. 11479bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor * @param uri the uri of the image 11579bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor * @param callback the callback to call when the thumbnail is fully loaded 11679bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor * @return 11779bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor */ 11879bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor public ItemLoadedFuture getVideoThumbnail(Uri uri, 11979bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor final ItemLoadedCallback<ImageLoaded> callback) { 120721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return getThumbnail(uri, true, callback); 12179bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor } 12279bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor 123721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private ItemLoadedFuture getThumbnail(Uri uri, boolean isVideo, 12479bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor final ItemLoadedCallback<ImageLoaded> callback) { 12551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (uri == null) { 12651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor throw new NullPointerException(); 12751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 12851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 129721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor final Bitmap thumbnail = DEBUG_DISABLE_CACHE ? null : mThumbnailCache.get(uri); 13051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 13151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final boolean thumbnailExists = (thumbnail != null); 13251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final boolean taskExists = mPendingTaskUris.contains(uri); 13351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final boolean newTaskRequired = !thumbnailExists && !taskExists; 13451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final boolean callbackRequired = (callback != null); 13551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 13651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (Log.isLoggable(LogTag.THUMBNAIL_CACHE, Log.DEBUG)) { 13751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.v(TAG, "getThumbnail mThumbnailCache.get for uri: " + uri + " thumbnail: " + 13851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor thumbnail + " callback: " + callback + " thumbnailExists: " + 13951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor thumbnailExists + " taskExists: " + taskExists + 14051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor " newTaskRequired: " + newTaskRequired + 14151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor " callbackRequired: " + callbackRequired); 14251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 14351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 14451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (thumbnailExists) { 145721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (callbackRequired && !DEBUG_DISABLE_CALLBACK) { 14679bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor ImageLoaded imageLoaded = new ImageLoaded(thumbnail, isVideo); 14779bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor callback.onItemLoaded(imageLoaded, null); 14851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 14951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor return new NullItemLoadedFuture(); 15051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 15151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 15251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (callbackRequired) { 15351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor addCallback(uri, callback); 15451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 15551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 15651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (newTaskRequired) { 15751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mPendingTaskUris.add(uri); 158721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Runnable task = new ThumbnailTask(uri, isVideo); 15951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mExecutor.execute(task); 16051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 16151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor return new ItemLoadedFuture() { 1629c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor private boolean mIsDone; 1639c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor 164ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev @Override 1659c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor public void cancel(Uri uri) { 16651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor cancelCallback(callback); 1679c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor removeThumbnail(uri); // if the thumbnail is half loaded, force a reload next time 16851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 1699c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor 1709c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor @Override 1719c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor public void setIsDone(boolean done) { 1729c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor mIsDone = done; 1739c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor } 1749c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor 175ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev @Override 17651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public boolean isDone() { 1779c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor return mIsDone; 17851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 17951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor }; 18051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 18151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 18251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor @Override 1837b6fe946f2e1020432e3600c8863f72449cd4e68Tom Taylor public synchronized void clear() { 18451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor super.clear(); 18551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 1867b6fe946f2e1020432e3600c8863f72449cd4e68Tom Taylor mThumbnailCache.clear(); // clear in-memory cache 1877b6fe946f2e1020432e3600c8863f72449cd4e68Tom Taylor clearBackingStore(); // clear on-disk cache 1887b6fe946f2e1020432e3600c8863f72449cd4e68Tom Taylor } 1897b6fe946f2e1020432e3600c8863f72449cd4e68Tom Taylor 1907b6fe946f2e1020432e3600c8863f72449cd4e68Tom Taylor // Delete the on-disk cache, but leave the in-memory cache intact 1917b6fe946f2e1020432e3600c8863f72449cd4e68Tom Taylor public synchronized void clearBackingStore() { 192ffe45d561b2d756d010face2018f411c7d69ae80Roger Chen if (mImageCacheService == null) { 193ffe45d561b2d756d010face2018f411c7d69ae80Roger Chen // No need to call getImageCacheService() to renew the instance if it's null. 194ffe45d561b2d756d010face2018f411c7d69ae80Roger Chen // It's enough to only delete the image cache files for the sake of safety. 195ffe45d561b2d756d010face2018f411c7d69ae80Roger Chen CacheManager.clear(mContext); 196ffe45d561b2d756d010face2018f411c7d69ae80Roger Chen } else { 197ffe45d561b2d756d010face2018f411c7d69ae80Roger Chen getImageCacheService().clear(); 198ffe45d561b2d756d010face2018f411c7d69ae80Roger Chen 199ffe45d561b2d756d010face2018f411c7d69ae80Roger Chen // force a re-init the next time getImageCacheService requested 200ffe45d561b2d756d010face2018f411c7d69ae80Roger Chen mImageCacheService = null; 201ffe45d561b2d756d010face2018f411c7d69ae80Roger Chen } 20251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 20351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 204b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor public void removeThumbnail(Uri uri) { 2059c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor if (Log.isLoggable(TAG, Log.DEBUG)) { 2069c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor Log.d(TAG, "removeThumbnail: " + uri); 2079c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor } 2089c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor if (uri != null) { 2099c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor mThumbnailCache.remove(uri); 2109c9df2773645d195dbf17df91d7cccdf5154fe84Tom Taylor } 211b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor } 212b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor 213ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev @Override 21451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public String getTag() { 21551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor return TAG; 21651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 21751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 218721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private synchronized ImageCacheService getImageCacheService() { 219721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (mImageCacheService == null) { 220721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor mImageCacheService = new ImageCacheService(mContext); 221721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 222721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return mImageCacheService; 223721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 224721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 22551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public class ThumbnailTask implements Runnable { 22651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor private final Uri mUri; 22779bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor private final boolean mIsVideo; 22851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 229721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor public ThumbnailTask(Uri uri, boolean isVideo) { 23051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (uri == null) { 23151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor throw new NullPointerException(); 23251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 23351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mUri = uri; 23479bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor mIsVideo = isVideo; 23551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 23651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 23751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor /** {@inheritDoc} */ 238ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev @Override 23951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public void run() { 24051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (DEBUG_DISABLE_LOAD) { 24151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor return; 24251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 24351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (DEBUG_LONG_WAIT) { 24451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor try { 24551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Thread.sleep(10000); 24651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } catch (InterruptedException e) { 24751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 24851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 24951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 2505d403c7cb873004bbc203536e818075a7223c0a5Tom Taylor Bitmap bitmap = null; 2515d403c7cb873004bbc203536e818075a7223c0a5Tom Taylor try { 2525d403c7cb873004bbc203536e818075a7223c0a5Tom Taylor bitmap = getBitmap(mIsVideo); 2535d403c7cb873004bbc203536e818075a7223c0a5Tom Taylor } catch (IllegalArgumentException e) { 2545d403c7cb873004bbc203536e818075a7223c0a5Tom Taylor Log.e(TAG, "Couldn't load bitmap for " + mUri, e); 2555d403c7cb873004bbc203536e818075a7223c0a5Tom Taylor } 2565d403c7cb873004bbc203536e818075a7223c0a5Tom Taylor final Bitmap resultBitmap = bitmap; 257721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 25851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mCallbackHandler.post(new Runnable() { 259ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev @Override 26051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor public void run() { 26151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor final Set<ItemLoadedCallback> callbacks = mCallbacks.get(mUri); 26251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (callbacks != null) { 263721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Bitmap bitmap = resultBitmap == null ? 264721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor (mIsVideo ? mEmptyVideoBitmap : mEmptyImageBitmap) 265721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor : resultBitmap; 266721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 26751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor // Make a copy so that the callback can unregister itself 26879bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor for (final ItemLoadedCallback<ImageLoaded> callback : asList(callbacks)) { 269721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (Log.isLoggable(LogTag.THUMBNAIL_CACHE, Log.DEBUG)) { 27051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.d(TAG, "Invoking item loaded callback " + callback); 27151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 272721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (!DEBUG_DISABLE_CALLBACK) { 273721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor ImageLoaded imageLoaded = new ImageLoaded(bitmap, mIsVideo); 274721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor callback.onItemLoaded(imageLoaded, null); 275721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 27651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 27751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } else { 27851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (Log.isLoggable(TAG, Log.DEBUG)) { 27951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.d(TAG, "No image callback!"); 28051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 28151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 28251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 283721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // Add the bitmap to the soft cache if the load succeeded. Don't cache the 284721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // stand-ins for empty bitmaps. 28551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (resultBitmap != null) { 28651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mThumbnailCache.put(mUri, resultBitmap); 287721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (Log.isLoggable(LogTag.THUMBNAIL_CACHE, Log.DEBUG)) { 28851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.v(TAG, "in callback runnable: bitmap uri: " + mUri + 28951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor " width: " + resultBitmap.getWidth() + " height: " + 29051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor resultBitmap.getHeight() + " size: " + 29151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor resultBitmap.getByteCount()); 29251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 29351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 29451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 29551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mCallbacks.remove(mUri); 29651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor mPendingTaskUris.remove(mUri); 29751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor 29851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor if (Log.isLoggable(LogTag.THUMBNAIL_CACHE, Log.DEBUG)) { 29951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor Log.d(TAG, "Image task for " + mUri + "exiting " + mPendingTaskUris.size() 30051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor + " remain"); 30151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 30251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 30351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor }); 30451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 305721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 306721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private Bitmap getBitmap(boolean isVideo) { 307721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor ImageCacheService cacheService = getImageCacheService(); 308721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 309721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor UriImage uriImage = new UriImage(mContext, mUri); 310721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor String path = uriImage.getPath(); 311b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor 312f0190cdad812e64f3f3a67931cb2d0bf0edf4b05Tom Taylor if (path == null) { 313f0190cdad812e64f3f3a67931cb2d0bf0edf4b05Tom Taylor return null; 314f0190cdad812e64f3f3a67931cb2d0bf0edf4b05Tom Taylor } 315f0190cdad812e64f3f3a67931cb2d0bf0edf4b05Tom Taylor 316b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor // We never want to store thumbnails of temp files in the thumbnail cache on disk 317b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor // because those temp filenames are recycled (and reused when capturing images 318b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor // or videos). 319b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor boolean isTempFile = TempFileProvider.isTempFile(path); 320b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor 321b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor ImageData data = null; 322b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor if (!isTempFile) { 323b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor data = cacheService.getImageData(path, TYPE_THUMBNAIL); 324b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor } 325721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 326721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (data != null) { 327721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor BitmapFactory.Options options = new BitmapFactory.Options(); 328721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor options.inPreferredConfig = Bitmap.Config.ARGB_8888; 329721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Bitmap bitmap = requestDecode(data.mData, 330721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor data.mOffset, data.mData.length - data.mOffset, options); 331721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (bitmap == null) { 332721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Log.w(TAG, "decode cached failed " + path); 333721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 334721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return bitmap; 335721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } else { 336721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Bitmap bitmap; 337721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (isVideo) { 338721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor bitmap = getVideoBitmap(); 339721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } else { 340721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor bitmap = onDecodeOriginal(mUri, TYPE_THUMBNAIL); 341721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 342721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (bitmap == null) { 343721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Log.w(TAG, "decode orig failed " + path); 344721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return null; 345721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 346721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 347721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor bitmap = resizeDownBySideLength(bitmap, THUMBNAIL_TARGET_SIZE, true); 348721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 349b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor if (!isTempFile) { 350b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor byte[] array = compressBitmap(bitmap); 351b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor cacheService.putImageData(path, TYPE_THUMBNAIL, array); 352b8eae066a7ba60722de07300bf990d519f5c81d8Tom Taylor } 353721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return bitmap; 354721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 355721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 356721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 357721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private Bitmap getVideoBitmap() { 358721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor MediaMetadataRetriever retriever = new MediaMetadataRetriever(); 359721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor try { 360721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor retriever.setDataSource(mContext, mUri); 361721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return retriever.getFrameAtTime(-1); 362721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } catch (RuntimeException ex) { 363721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // Assume this is a corrupt video file. 364721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } finally { 365721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor try { 366721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor retriever.release(); 367721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } catch (RuntimeException ex) { 368721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // Ignore failures while cleaning up. 369721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 370721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 371721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return null; 372721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 373721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 374721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private byte[] compressBitmap(Bitmap bitmap) { 375721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor ByteArrayOutputStream os = new ByteArrayOutputStream(); 376721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor bitmap.compress(Bitmap.CompressFormat.JPEG, 377721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor COMPRESS_JPEG_QUALITY, os); 378721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return os.toByteArray(); 379721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 380721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 381721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private Bitmap requestDecode(byte[] bytes, int offset, 382721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor int length, Options options) { 383721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (options == null) { 384721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor options = new Options(); 385721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 386721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return ensureGLCompatibleBitmap( 387721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor BitmapFactory.decodeByteArray(bytes, offset, length, options)); 388721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 389721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 390721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private Bitmap resizeDownBySideLength( 391721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Bitmap bitmap, int maxLength, boolean recycle) { 392721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor int srcWidth = bitmap.getWidth(); 393721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor int srcHeight = bitmap.getHeight(); 394721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor float scale = Math.min( 395721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor (float) maxLength / srcWidth, (float) maxLength / srcHeight); 396721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (scale >= 1.0f) return bitmap; 397721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return resizeBitmapByScale(bitmap, scale, recycle); 398721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 399721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 400721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private Bitmap resizeBitmapByScale( 401721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Bitmap bitmap, float scale, boolean recycle) { 402721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor int width = Math.round(bitmap.getWidth() * scale); 403721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor int height = Math.round(bitmap.getHeight() * scale); 404721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (width == bitmap.getWidth() 405721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor && height == bitmap.getHeight()) return bitmap; 406721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Bitmap target = Bitmap.createBitmap(width, height, getConfig(bitmap)); 407721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Canvas canvas = new Canvas(target); 408721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor canvas.scale(scale, scale); 409721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG); 410721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor canvas.drawBitmap(bitmap, 0, 0, paint); 411721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (recycle) bitmap.recycle(); 412721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return target; 413721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 414721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 415721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private Bitmap.Config getConfig(Bitmap bitmap) { 416721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Bitmap.Config config = bitmap.getConfig(); 417721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (config == null) { 418721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor config = Bitmap.Config.ARGB_8888; 419721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 420721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return config; 421721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 422721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 423721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // TODO: This function should not be called directly from 424721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // DecodeUtils.requestDecode(...), since we don't have the knowledge 425721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // if the bitmap will be uploaded to GL. 426721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private Bitmap ensureGLCompatibleBitmap(Bitmap bitmap) { 427721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (bitmap == null || bitmap.getConfig() != null) return bitmap; 428721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Bitmap newBitmap = bitmap.copy(Config.ARGB_8888, false); 429721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor bitmap.recycle(); 430721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return newBitmap; 431721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 432721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 433721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private Bitmap onDecodeOriginal(Uri uri, int type) { 434721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor BitmapFactory.Options options = new BitmapFactory.Options(); 435721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor options.inPreferredConfig = Bitmap.Config.ARGB_8888; 436721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 437721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return requestDecode(uri, options, THUMBNAIL_TARGET_SIZE); 438721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 439721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 440721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private void closeSilently(Closeable c) { 441721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (c == null) return; 442721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor try { 443721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor c.close(); 444721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } catch (Throwable t) { 445721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Log.w(TAG, "close fail", t); 446721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 447721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 448721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 449721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private Bitmap requestDecode(final Uri uri, Options options, int targetSize) { 450721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (options == null) options = new Options(); 451721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 452721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor InputStream inputStream; 453721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor try { 454721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor inputStream = mContext.getContentResolver().openInputStream(uri); 455721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } catch (FileNotFoundException e) { 456721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Log.e(TAG, "Can't open uri: " + uri, e); 457721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return null; 458721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 459721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 460721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor options.inJustDecodeBounds = true; 461721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor BitmapFactory.decodeStream(inputStream, null, options); 462721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor closeSilently(inputStream); 463721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 464721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // No way to reset the stream. Have to open it again :-( 465721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor try { 466721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor inputStream = mContext.getContentResolver().openInputStream(uri); 467721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } catch (FileNotFoundException e) { 468721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Log.e(TAG, "Can't open uri: " + uri, e); 469721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return null; 470721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 471721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 472721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor options.inSampleSize = computeSampleSizeLarger( 473721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor options.outWidth, options.outHeight, targetSize); 474721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor options.inJustDecodeBounds = false; 475721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 476721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Bitmap result = BitmapFactory.decodeStream(inputStream, null, options); 477721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor closeSilently(inputStream); 478721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 4795d403c7cb873004bbc203536e818075a7223c0a5Tom Taylor if (result == null) { 4805d403c7cb873004bbc203536e818075a7223c0a5Tom Taylor return null; 4815d403c7cb873004bbc203536e818075a7223c0a5Tom Taylor } 4825d403c7cb873004bbc203536e818075a7223c0a5Tom Taylor 483721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // We need to resize down if the decoder does not support inSampleSize. 484721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // (For example, GIF images.) 485721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor result = resizeDownIfTooBig(result, targetSize, true); 486721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return ensureGLCompatibleBitmap(result); 487721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 488721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 489721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // This computes a sample size which makes the longer side at least 490721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // minSideLength long. If that's not possible, return 1. 491721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private int computeSampleSizeLarger(int w, int h, 492721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor int minSideLength) { 493721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor int initialSize = Math.max(w / minSideLength, h / minSideLength); 494721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (initialSize <= 1) return 1; 495721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 496721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return initialSize <= 8 497721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor ? prevPowerOf2(initialSize) 498721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor : initialSize / 8 * 8; 499721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 500721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 501721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // Returns the previous power of two. 502721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // Returns the input if it is already power of 2. 503721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // Throws IllegalArgumentException if the input is <= 0 504721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private int prevPowerOf2(int n) { 505721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (n <= 0) throw new IllegalArgumentException(); 506721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return Integer.highestOneBit(n); 507721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 508721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 509721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor // Resize the bitmap if each side is >= targetSize * 2 510721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor private Bitmap resizeDownIfTooBig( 511721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor Bitmap bitmap, int targetSize, boolean recycle) { 512721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor int srcWidth = bitmap.getWidth(); 513721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor int srcHeight = bitmap.getHeight(); 514721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor float scale = Math.max( 515721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor (float) targetSize / srcWidth, (float) targetSize / srcHeight); 516721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor if (scale > 0.5f) return bitmap; 517721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor return resizeBitmapByScale(bitmap, scale, recycle); 518721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor } 519721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 52051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor } 52179bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor 52279bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor public static class ImageLoaded { 52379bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor public final Bitmap mBitmap; 52479bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor public final boolean mIsVideo; 52579bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor 52679bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor public ImageLoaded(Bitmap bitmap, boolean isVideo) { 52779bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor mBitmap = bitmap; 52879bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor mIsVideo = isVideo; 52979bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor } 53079bf6f70c54d08dc9c3481b8461a3a46a3cefb83Tom Taylor } 53151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor} 532