- removed uneccessary improts
[pub/Android/ownCloud.git] / src / com / owncloud / android / datamodel / ThumbnailsCacheManager.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012-2014 ownCloud Inc.
3 *
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.
7 *
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.
12 *
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/>.
15 *
16 */
17
18 package com.owncloud.android.datamodel;
19
20 import java.io.File;
21 import java.lang.ref.WeakReference;
22
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;
33
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;
39
40 /**
41 * Manager for concurrent access to thumbnails cache.
42 *
43 * @author Tobias Kaminsky
44 * @author David A. Velasco
45 */
46 public class ThumbnailsCacheManager {
47
48 private static final String TAG = ThumbnailsCacheManager.class.getSimpleName();
49
50 private static final String CACHE_FOLDER = "thumbnailCache";
51
52 private static final Object mThumbnailsDiskCacheLock = new Object();
53 private static DiskLruImageCache mThumbnailCache = null;
54 private static boolean mThumbnailCacheStarting = true;
55
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;
59
60 public static Bitmap mDefaultImg =
61 BitmapFactory.decodeResource(
62 MainApp.getAppContext().getResources(),
63 DisplayUtils.getResourceId("image/png", "default.png")
64 );
65
66
67 public static class InitDiskCacheTask extends AsyncTask<File, Void, Void> {
68 @Override
69 protected Void doInBackground(File... params) {
70 synchronized (mThumbnailsDiskCacheLock) {
71 mThumbnailCacheStarting = true;
72 if (mThumbnailCache == null) {
73 try {
74 // Check if media is mounted or storage is built-in, if so,
75 // try and use external cache dir; otherwise use internal cache dir
76 final String cachePath =
77 MainApp.getAppContext().getExternalCacheDir().getPath() +
78 File.separator + CACHE_FOLDER;
79 Log_OC.d(TAG, "create dir: " + cachePath);
80 final File diskCacheDir = new File(cachePath);
81 mThumbnailCache = new DiskLruImageCache(
82 diskCacheDir,
83 DISK_CACHE_SIZE,
84 mCompressFormat,
85 mCompressQuality
86 );
87 } catch (Exception e) {
88 Log_OC.d(TAG, "Thumbnail cache could not be opened ", e);
89 mThumbnailCache = null;
90 }
91 }
92 mThumbnailCacheStarting = false; // Finished initialization
93 mThumbnailsDiskCacheLock.notifyAll(); // Wake any waiting threads
94 }
95 return null;
96 }
97 }
98
99
100 public static void addBitmapToCache(String key, Bitmap bitmap) {
101 synchronized (mThumbnailsDiskCacheLock) {
102 if (mThumbnailCache != null) {
103 mThumbnailCache.put(key, bitmap);
104 }
105 }
106 }
107
108
109 public static Bitmap getBitmapFromDiskCache(String key) {
110 synchronized (mThumbnailsDiskCacheLock) {
111 // Wait while disk cache is started from background thread
112 while (mThumbnailCacheStarting) {
113 try {
114 mThumbnailsDiskCacheLock.wait();
115 } catch (InterruptedException e) {}
116 }
117 if (mThumbnailCache != null) {
118 return (Bitmap) mThumbnailCache.getBitmap(key);
119 }
120 }
121 return null;
122 }
123
124
125 public static boolean cancelPotentialWork(OCFile file, ImageView imageView) {
126 final ThumbnailGenerationTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
127
128 if (bitmapWorkerTask != null) {
129 final OCFile bitmapData = bitmapWorkerTask.mFile;
130 // If bitmapData is not yet set or it differs from the new data
131 if (bitmapData == null || bitmapData != file) {
132 // Cancel previous task
133 bitmapWorkerTask.cancel(true);
134 } else {
135 // The same work is already in progress
136 return false;
137 }
138 }
139 // No task associated with the ImageView, or an existing task was cancelled
140 return true;
141 }
142
143 public static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) {
144 if (imageView != null) {
145 final Drawable drawable = imageView.getDrawable();
146 if (drawable instanceof AsyncDrawable) {
147 final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
148 return asyncDrawable.getBitmapWorkerTask();
149 }
150 }
151 return null;
152 }
153
154 public static class ThumbnailGenerationTask extends AsyncTask<OCFile, Void, Bitmap> {
155 private final WeakReference<ImageView> mImageViewReference;
156 private OCFile mFile;
157 private FileDataStorageManager mStorageManager;
158
159 public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager storageManager) {
160 // Use a WeakReference to ensure the ImageView can be garbage collected
161 mImageViewReference = new WeakReference<ImageView>(imageView);
162 if (storageManager == null)
163 throw new IllegalArgumentException("storageManager must not be NULL");
164 mStorageManager = storageManager;
165 }
166
167 // Decode image in background.
168 @Override
169 protected Bitmap doInBackground(OCFile... params) {
170 Bitmap thumbnail = null;
171
172 try {
173 mFile = params[0];
174 final String imageKey = String.valueOf(mFile.getRemoteId());
175
176 // Check disk cache in background thread
177 thumbnail = getBitmapFromDiskCache(imageKey);
178
179 // Not found in disk cache
180 if (thumbnail == null || mFile.needsUpdateThumbnail()) {
181 // Converts dp to pixel
182 Resources r = MainApp.getAppContext().getResources();
183 int px = (int) Math.round(TypedValue.applyDimension(
184 TypedValue.COMPLEX_UNIT_DIP, 150, r.getDisplayMetrics()
185 ));
186
187 if (mFile.isDown()){
188 Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(
189 mFile.getStoragePath(), px, px);
190
191 if (bitmap != null) {
192 thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);
193
194 // Add thumbnail to cache
195 addBitmapToCache(imageKey, thumbnail);
196
197 mFile.setNeedsUpdateThumbnail(false);
198 mStorageManager.saveFile(mFile);
199 }
200
201 }
202 }
203
204 } catch (Throwable t) {
205 // the app should never break due to a problem with thumbnails
206 Log_OC.e(TAG, "Generation of thumbnail for " + mFile + " failed", t);
207 if (t instanceof OutOfMemoryError) {
208 System.gc();
209 }
210 }
211
212 return thumbnail;
213 }
214
215 protected void onPostExecute(Bitmap bitmap){
216 if (isCancelled()) {
217 bitmap = null;
218 }
219
220 if (mImageViewReference != null && bitmap != null) {
221 final ImageView imageView = mImageViewReference.get();
222 final ThumbnailGenerationTask bitmapWorkerTask =
223 getBitmapWorkerTask(imageView);
224 if (this == bitmapWorkerTask && imageView != null) {
225 if (imageView.getTag().equals(mFile.getFileId())) {
226 imageView.setImageBitmap(bitmap);
227 }
228 }
229 }
230 }
231 }
232
233
234 public static class AsyncDrawable extends BitmapDrawable {
235 private final WeakReference<ThumbnailGenerationTask> bitmapWorkerTaskReference;
236
237 public AsyncDrawable(
238 Resources res, Bitmap bitmap, ThumbnailGenerationTask bitmapWorkerTask
239 ) {
240
241 super(res, bitmap);
242 bitmapWorkerTaskReference =
243 new WeakReference<ThumbnailGenerationTask>(bitmapWorkerTask);
244 }
245
246 public ThumbnailGenerationTask getBitmapWorkerTask() {
247 return bitmapWorkerTaskReference.get();
248 }
249 }
250
251
252 /**
253 * Remove from cache the remoteId passed
254 * @param fileRemoteId: remote id of mFile passed
255 */
256 public static void removeFileFromCache(String fileRemoteId){
257 synchronized (mThumbnailsDiskCacheLock) {
258 if (mThumbnailCache != null) {
259 mThumbnailCache.removeKey(fileRemoteId);
260 }
261 mThumbnailsDiskCacheLock.notifyAll(); // Wake any waiting threads
262 }
263 }
264
265 }