1 /* ownCloud Android client application
2 * Copyright (C) 2012-2014 ownCloud Inc.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2,
6 * as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package com
.owncloud
.android
.datamodel
;
21 import java
.lang
.ref
.WeakReference
;
23 import android
.content
.res
.Resources
;
24 import android
.graphics
.Bitmap
;
25 import android
.graphics
.BitmapFactory
;
26 import android
.graphics
.Bitmap
.CompressFormat
;
27 import android
.graphics
.drawable
.BitmapDrawable
;
28 import android
.graphics
.drawable
.Drawable
;
29 import android
.media
.ThumbnailUtils
;
30 import android
.os
.AsyncTask
;
31 import android
.util
.TypedValue
;
32 import android
.widget
.ImageView
;
34 import com
.owncloud
.android
.MainApp
;
35 import com
.owncloud
.android
.lib
.common
.utils
.Log_OC
;
36 import com
.owncloud
.android
.ui
.adapter
.DiskLruImageCache
;
37 import com
.owncloud
.android
.utils
.BitmapUtils
;
38 import com
.owncloud
.android
.utils
.DisplayUtils
;
41 * Manager for concurrent access to thumbnails cache.
43 * @author Tobias Kaminsky
44 * @author David A. Velasco
46 public class ThumbnailsCacheManager
{
48 private static final String TAG
= ThumbnailsCacheManager
.class.getSimpleName();
50 private static final String CACHE_FOLDER
= "thumbnailCache";
52 private static final Object mThumbnailsDiskCacheLock
= new Object();
53 private static DiskLruImageCache mThumbnailCache
;
54 private static boolean mThumbnailCacheStarting
= true
;
56 private static final int DISK_CACHE_SIZE
= 1024 * 1024 * 10; // 10MB
57 private static final CompressFormat mCompressFormat
= CompressFormat
.JPEG
;
58 private static final int mCompressQuality
= 70;
60 public static Bitmap mDefaultImg
=
61 BitmapFactory
.decodeResource(
62 MainApp
.getAppContext().getResources(),
63 DisplayUtils
.getResourceId("image/png", "default.png")
67 public static class InitDiskCacheTask
extends AsyncTask
<File
, Void
, Void
> {
69 protected Void
doInBackground(File
... params
) {
70 synchronized (mThumbnailsDiskCacheLock
) {
72 // Check if media is mounted or storage is built-in, if so,
73 // try and use external cache dir; otherwise use internal cache dir
74 final String cachePath
=
75 MainApp
.getAppContext().getExternalCacheDir().getPath() +
76 File
.separator
+ CACHE_FOLDER
;
77 Log_OC
.d(TAG
, "create dir: " + cachePath
);
78 final File diskCacheDir
= new File(cachePath
);
79 mThumbnailCache
= new DiskLruImageCache(
85 } catch (Exception e
) {
86 Log_OC
.d(TAG
, "Thumbnail cache could not be opened ", e
);
87 mThumbnailCache
= null
;
89 mThumbnailCacheStarting
= false
; // Finished initialization
90 mThumbnailsDiskCacheLock
.notifyAll(); // Wake any waiting threads
97 public static void addBitmapToCache(String key
, Bitmap bitmap
) {
98 synchronized (mThumbnailsDiskCacheLock
) {
99 if (mThumbnailCache
!= null
) {
100 mThumbnailCache
.put(key
, bitmap
);
106 public static Bitmap
getBitmapFromDiskCache(String key
) {
107 synchronized (mThumbnailsDiskCacheLock
) {
108 // Wait while disk cache is started from background thread
109 while (mThumbnailCacheStarting
) {
111 mThumbnailsDiskCacheLock
.wait();
112 } catch (InterruptedException e
) {}
114 if (mThumbnailCache
!= null
) {
115 return (Bitmap
) mThumbnailCache
.getBitmap(key
);
122 public static boolean cancelPotentialWork(OCFile file
, ImageView imageView
) {
123 final ThumbnailGenerationTask bitmapWorkerTask
= getBitmapWorkerTask(imageView
);
125 if (bitmapWorkerTask
!= null
) {
126 final OCFile bitmapData
= bitmapWorkerTask
.mFile
;
127 // If bitmapData is not yet set or it differs from the new data
128 if (bitmapData
== null
|| bitmapData
!= file
) {
129 // Cancel previous task
130 bitmapWorkerTask
.cancel(true
);
132 // The same work is already in progress
136 // No task associated with the ImageView, or an existing task was cancelled
140 public static ThumbnailGenerationTask
getBitmapWorkerTask(ImageView imageView
) {
141 if (imageView
!= null
) {
142 final Drawable drawable
= imageView
.getDrawable();
143 if (drawable
instanceof AsyncDrawable
) {
144 final AsyncDrawable asyncDrawable
= (AsyncDrawable
) drawable
;
145 return asyncDrawable
.getBitmapWorkerTask();
151 public static class ThumbnailGenerationTask
extends AsyncTask
<OCFile
, Void
, Bitmap
> {
152 private final WeakReference
<ImageView
> mImageViewReference
;
153 private OCFile mFile
;
154 private FileDataStorageManager mStorageManager
;
156 public ThumbnailGenerationTask(ImageView imageView
, FileDataStorageManager storageManager
) {
157 // Use a WeakReference to ensure the ImageView can be garbage collected
158 mImageViewReference
= new WeakReference
<ImageView
>(imageView
);
159 if (storageManager
== null
)
160 throw new IllegalArgumentException("storageManager must not be NULL");
161 mStorageManager
= storageManager
;
164 // Decode image in background.
166 protected Bitmap
doInBackground(OCFile
... params
) {
167 Bitmap thumbnail
= null
;
171 final String imageKey
= String
.valueOf(mFile
.getRemoteId());
173 // Check disk cache in background thread
174 thumbnail
= getBitmapFromDiskCache(imageKey
);
176 // Not found in disk cache
177 if (thumbnail
== null
|| mFile
.needsUpdateThumbnail()) {
178 // Converts dp to pixel
179 Resources r
= MainApp
.getAppContext().getResources();
180 int px
= (int) Math
.round(TypedValue
.applyDimension(
181 TypedValue
.COMPLEX_UNIT_DIP
, 150, r
.getDisplayMetrics()
185 Bitmap bitmap
= BitmapUtils
.decodeSampledBitmapFromFile(
186 mFile
.getStoragePath(), px
, px
);
188 if (bitmap
!= null
) {
189 thumbnail
= ThumbnailUtils
.extractThumbnail(bitmap
, px
, px
);
191 // Add thumbnail to cache
192 addBitmapToCache(imageKey
, thumbnail
);
194 mFile
.setNeedsUpdateThumbnail(false
);
195 mStorageManager
.saveFile(mFile
);
201 } catch (Throwable t
) {
202 // the app should never break due to a problem with thumbnails
203 Log_OC
.e(TAG
, "Generation of thumbnail for " + mFile
+ " failed", t
);
204 if (t
instanceof OutOfMemoryError
) {
212 protected void onPostExecute(Bitmap bitmap
){
217 if (mImageViewReference
!= null
&& bitmap
!= null
) {
218 final ImageView imageView
= mImageViewReference
.get();
219 final ThumbnailGenerationTask bitmapWorkerTask
=
220 getBitmapWorkerTask(imageView
);
221 if (this == bitmapWorkerTask
&& imageView
!= null
) {
222 if (imageView
.getTag().equals(mFile
.getFileId())) {
223 imageView
.setImageBitmap(bitmap
);
231 public static class AsyncDrawable
extends BitmapDrawable
{
232 private final WeakReference
<ThumbnailGenerationTask
> bitmapWorkerTaskReference
;
234 public AsyncDrawable(
235 Resources res
, Bitmap bitmap
, ThumbnailGenerationTask bitmapWorkerTask
239 bitmapWorkerTaskReference
=
240 new WeakReference
<ThumbnailGenerationTask
>(bitmapWorkerTask
);
243 public ThumbnailGenerationTask
getBitmapWorkerTask() {
244 return bitmapWorkerTaskReference
.get();
250 * Remove from cache the remoteId passed
251 * @param fileRemoteId: remote id of mFile passed
253 public static void removeFileFromCache(String fileRemoteId
){
254 synchronized (mThumbnailsDiskCacheLock
) {
255 if (mThumbnailCache
!= null
) {
256 mThumbnailCache
.removeKey(fileRemoteId
);
258 mThumbnailsDiskCacheLock
.notifyAll(); // Wake any waiting threads