public class DiskLruImageCache {
private DiskLruCache mDiskCache;
- private CompressFormat mCompressFormat = CompressFormat.JPEG;
- private int mCompressQuality = 70;
- private static final int APP_VERSION = 1;
+ private CompressFormat mCompressFormat;
+ private int mCompressQuality;
+ private static final int CACHE_VERSION = 1;
private static final int VALUE_COUNT = 1;
+ private static final int IO_BUFFER_SIZE = 8 * 1024;
private static final String TAG = "DiskLruImageCache";
public DiskLruImageCache( Context context,String uniqueName, int diskCacheSize,
CompressFormat compressFormat, int quality ) {
try {
final File diskCacheDir = getDiskCacheDir(context, uniqueName );
- mDiskCache = DiskLruCache.open( diskCacheDir, APP_VERSION, VALUE_COUNT, diskCacheSize );
+ mDiskCache = DiskLruCache.open( diskCacheDir, CACHE_VERSION, VALUE_COUNT, diskCacheSize );
mCompressFormat = compressFormat;
mCompressQuality = quality;
} catch (IOException e) {
throws IOException, FileNotFoundException {
OutputStream out = null;
try {
- out = new BufferedOutputStream( editor.newOutputStream( 0 ), Utils.IO_BUFFER_SIZE );
+ out = new BufferedOutputStream( editor.newOutputStream( 0 ), IO_BUFFER_SIZE );
return bitmap.compress( mCompressFormat, mCompressQuality, out );
} finally {
if ( out != null ) {
// Check if media is mounted or storage is built-in, if so, try and use external cache dir
// otherwise use internal cache dir
- final String cachePath =
- Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
- !Utils.isExternalStorageRemovable() ?
- Utils.getExternalCacheDir(context).getPath() :
- context.getCacheDir().getPath();
+ final String cachePath = context.getExternalCacheDir().getPath();
Log_OC.d("DiskCache", "create dir: " + cachePath + File.separator + uniqueName);
final InputStream in = snapshot.getInputStream( 0 );
if ( in != null ) {
final BufferedInputStream buffIn =
- new BufferedInputStream( in, Utils.IO_BUFFER_SIZE );
+ new BufferedInputStream( in, IO_BUFFER_SIZE );
bitmap = BitmapFactory.decodeStream( buffIn );
}
} catch ( IOException e ) {
\r
import java.io.File;\r
import java.io.IOException;\r
+import java.lang.ref.WeakReference;\r
import java.net.URLEncoder;\r
import java.util.Vector;\r
\r
import android.graphics.Bitmap;\r
import android.graphics.Bitmap.CompressFormat;\r
import android.graphics.BitmapFactory;\r
+import android.graphics.drawable.BitmapDrawable;\r
+import android.graphics.drawable.Drawable;\r
import android.media.ThumbnailUtils;\r
import android.os.AsyncTask;\r
import android.util.TypedValue;\r
* instance.\r
* \r
* @author Bartek Przybylski\r
+ * @Author Tobias Kaminsky\r
* \r
*/\r
public class FileListListAdapter extends BaseAdapter implements ListAdapter {\r
private FileDataStorageManager mStorageManager;
private Account mAccount;
private ComponentsGetter mTransferServiceGetter;\r
- private final Object mDiskCacheLock = new Object();\r
- private DiskLruImageCache mDiskLruCache;\r
- private boolean mDiskCacheStarting = true;\r
+ private final Object thumbnailDiskCacheLock = new Object();\r
+ private DiskLruImageCache mThumbnailCache;\r
+ private boolean mThumbnailCacheStarting = true;\r
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB\r
- private CompressFormat mCompressFormat = CompressFormat.JPEG;\r
- private int mCompressQuality = 70;\r
+ private static final CompressFormat mCompressFormat = CompressFormat.JPEG;\r
+ private static final int mCompressQuality = 70;\r
private OwnCloudClient mClient;\r
+ private Bitmap defaultImg;\r
\r
public FileListListAdapter(Context context, ComponentsGetter transferServiceGetter) {\r
mContext = context;\r
mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
mTransferServiceGetter = transferServiceGetter;\r
+ defaultImg = BitmapFactory.decodeResource(mContext.getResources(), \r
+ DisplayUtils.getResourceId("image/png", "default.png"));\r
\r
// Initialise disk cache on background thread\r
new InitDiskCacheTask().execute();\r
class InitDiskCacheTask extends AsyncTask<File, Void, Void> {\r
@Override\r
protected Void doInBackground(File... params) {\r
- synchronized (mDiskCacheLock) {\r
- mDiskLruCache = new DiskLruImageCache(mContext, "thumbnailCache", DISK_CACHE_SIZE, mCompressFormat,mCompressQuality);\r
+ synchronized (thumbnailDiskCacheLock) {\r
+ mThumbnailCache = new DiskLruImageCache(mContext, "thumbnailCache", \r
+ DISK_CACHE_SIZE, mCompressFormat, mCompressQuality);\r
\r
try {\r
OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext);\r
e.printStackTrace();\r
}\r
\r
- mDiskCacheStarting = false; // Finished initialization\r
- mDiskCacheLock.notifyAll(); // Wake any waiting threads\r
+ mThumbnailCacheStarting = false; // Finished initialization\r
+ thumbnailDiskCacheLock.notifyAll(); // Wake any waiting threads\r
}\r
return null;\r
}\r
}\r
+ \r
+ static class AsyncDrawable extends BitmapDrawable {\r
+ private final WeakReference<ThumbnailGenerationTask> bitmapWorkerTaskReference;\r
+\r
+ public AsyncDrawable(Resources res, Bitmap bitmap,\r
+ ThumbnailGenerationTask bitmapWorkerTask) {\r
+ super(res, bitmap);\r
+ bitmapWorkerTaskReference =\r
+ new WeakReference<ThumbnailGenerationTask>(bitmapWorkerTask);\r
+ }\r
+\r
+ public ThumbnailGenerationTask getBitmapWorkerTask() {\r
+ return bitmapWorkerTaskReference.get();\r
+ }\r
+ }\r
+\r
+ class ThumbnailGenerationTask extends AsyncTask<OCFile, Void, Bitmap> {\r
+ private final WeakReference<ImageView> imageViewReference;\r
+ private OCFile file;\r
\r
- class BitmapWorkerTask extends AsyncTask<OCFile, Void, Bitmap> {\r
- private final ImageView fileIcon;\r
\r
- public BitmapWorkerTask(ImageView fileIcon) {\r
- this.fileIcon = fileIcon;\r
+ public ThumbnailGenerationTask(ImageView imageView) {\r
+ // Use a WeakReference to ensure the ImageView can be garbage collected\r
+ imageViewReference = new WeakReference<ImageView>(imageView);\r
}\r
\r
// Decode image in background.\r
@Override\r
protected Bitmap doInBackground(OCFile... params) {\r
- OCFile file = params[0];\r
+ file = params[0];\r
final String imageKey = String.valueOf(file.getRemoteId());\r
\r
// Check disk cache in background thread\r
\r
} else {\r
// Download thumbnail from server\r
- DefaultHttpClient httpclient = new DefaultHttpClient();\r
- try {\r
- httpclient.getCredentialsProvider().setCredentials(\r
- new AuthScope(mClient.getBaseUri().toString().replace("https://", ""), 443), \r
- new UsernamePasswordCredentials(mClient.getCredentials().getUsername(), mClient.getCredentials().getAuthToken()));\r
- \r
-\r
- // TODO change to user preview.png\r
- //HttpGet httpget = new HttpGet(mClient.getBaseUri() + "/index.php/core/preview.png?file="+URLEncoder.encode(file.getRemotePath(), "UTF-8")+"&x=36&y=36&forceIcon=1");\r
- HttpGet httpget = new HttpGet(mClient.getBaseUri() + "/ocs/v1.php/thumbnail?x=50&y=50&path=" + URLEncoder.encode(file.getRemotePath(), "UTF-8"));\r
- HttpResponse response = httpclient.execute(httpget);\r
- HttpEntity entity = response.getEntity();\r
- \r
- if (entity != null) {\r
- byte[] bytes = EntityUtils.toByteArray(entity);\r
- Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);\r
- thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);\r
- \r
- // Add thumbnail to cache\r
- if (thumbnail != null){\r
- addBitmapToCache(imageKey, thumbnail);\r
- }\r
- }\r
- } catch(Exception e){\r
- e.printStackTrace();\r
- }finally {\r
- httpclient.getConnectionManager().shutdown();\r
- }\r
+ // Commented out as maybe changes to client library are needed\r
+// DefaultHttpClient httpclient = new DefaultHttpClient();\r
+// try {\r
+// httpclient.getCredentialsProvider().setCredentials(\r
+// new AuthScope(mClient.getBaseUri().toString().replace("https://", ""), 443), \r
+// new UsernamePasswordCredentials(mClient.getCredentials().getUsername(), mClient.getCredentials().getAuthToken()));\r
+// \r
+//\r
+// HttpGet httpget = new HttpGet(mClient.getBaseUri() + "/ocs/v1.php/thumbnail?x=50&y=50&path=" + URLEncoder.encode(file.getRemotePath(), "UTF-8"));\r
+// HttpResponse response = httpclient.execute(httpget);\r
+// HttpEntity entity = response.getEntity();\r
+// \r
+// if (entity != null) {\r
+// byte[] bytes = EntityUtils.toByteArray(entity);\r
+// Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);\r
+// thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);\r
+// \r
+// // Add thumbnail to cache\r
+// if (thumbnail != null){\r
+// addBitmapToCache(imageKey, thumbnail);\r
+// }\r
+// }\r
+// } catch(Exception e){\r
+// e.printStackTrace();\r
+// }finally {\r
+// httpclient.getConnectionManager().shutdown();\r
+// }\r
} \r
}\r
return thumbnail;\r
}\r
\r
protected void onPostExecute(Bitmap bitmap){\r
- if (bitmap != null){\r
- fileIcon.setImageBitmap(bitmap);\r
+ if (isCancelled()) {\r
+ bitmap = null;\r
+ }\r
+\r
+ if (imageViewReference != null && bitmap != null) {\r
+ final ImageView imageView = imageViewReference.get();\r
+ final ThumbnailGenerationTask bitmapWorkerTask =\r
+ getBitmapWorkerTask(imageView);\r
+ if (this == bitmapWorkerTask && imageView != null) {\r
+ imageView.setImageBitmap(bitmap);\r
+ }\r
}\r
}\r
}\r
\r
public void addBitmapToCache(String key, Bitmap bitmap) {\r
- synchronized (mDiskCacheLock) {\r
- if (mDiskLruCache != null && mDiskLruCache.getBitmap(key) == null) {\r
- mDiskLruCache.put(key, bitmap);\r
+ synchronized (thumbnailDiskCacheLock) {\r
+ if (mThumbnailCache != null && mThumbnailCache.getBitmap(key) == null) {\r
+ mThumbnailCache.put(key, bitmap);\r
}\r
}\r
}\r
\r
public Bitmap getBitmapFromDiskCache(String key) {\r
- synchronized (mDiskCacheLock) {\r
+ synchronized (thumbnailDiskCacheLock) {\r
// Wait while disk cache is started from background thread\r
- while (mDiskCacheStarting) {\r
+ while (mThumbnailCacheStarting) {\r
try {\r
- mDiskCacheLock.wait();\r
+ thumbnailDiskCacheLock.wait();\r
} catch (InterruptedException e) {}\r
}\r
- if (mDiskLruCache != null) {\r
- return (Bitmap) mDiskLruCache.getBitmap(key);\r
+ if (mThumbnailCache != null) {\r
+ return (Bitmap) mThumbnailCache.getBitmap(key);\r
}\r
}\r
return null;\r
checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);\r
}\r
checkBoxV.setVisibility(View.VISIBLE);\r
- }\r
- \r
- // first set thumbnail according to Mimetype, prevents empty thumbnails\r
- fileIcon.setImageResource(DisplayUtils.getResourceId(file.getMimetype(), file.getFileName()));\r
+ } \r
\r
// get Thumbnail if file is image\r
if (file.isImage()){\r
- // Thumbnail in Cache?\r
+ // Thumbnail in Cache?\r
Bitmap thumbnail = getBitmapFromDiskCache(String.valueOf(file.getRemoteId()));\r
if (thumbnail != null){\r
fileIcon.setImageBitmap(thumbnail);\r
} else {\r
// generate new Thumbnail\r
- new BitmapWorkerTask(fileIcon).execute(file);\r
+ if (cancelPotentialWork(file, fileIcon)) {\r
+ final ThumbnailGenerationTask task = new ThumbnailGenerationTask(fileIcon);\r
+ final AsyncDrawable asyncDrawable =\r
+ new AsyncDrawable(mContext.getResources(), defaultImg, task);\r
+ fileIcon.setImageDrawable(asyncDrawable);\r
+ task.execute(file);\r
+ }\r
}\r
}\r
\r
return view;\r
}\r
+ \r
+ public static boolean cancelPotentialWork(OCFile file, ImageView imageView) {\r
+ final ThumbnailGenerationTask bitmapWorkerTask = getBitmapWorkerTask(imageView);\r
+\r
+ if (bitmapWorkerTask != null) {\r
+ final OCFile bitmapData = bitmapWorkerTask.file;\r
+ // If bitmapData is not yet set or it differs from the new data\r
+ if (bitmapData == null || bitmapData != file) {\r
+ // Cancel previous task\r
+ bitmapWorkerTask.cancel(true);\r
+ } else {\r
+ // The same work is already in progress\r
+ return false;\r
+ }\r
+ }\r
+ // No task associated with the ImageView, or an existing task was cancelled\r
+ return true;\r
+ }\r
+ \r
+ private static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) {\r
+ if (imageView != null) {\r
+ final Drawable drawable = imageView.getDrawable();\r
+ if (drawable instanceof AsyncDrawable) {\r
+ final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;\r
+ return asyncDrawable.getBitmapWorkerTask();\r
+ }\r
+ }\r
+ return null;\r
+ }\r
\r
@Override\r
public int getViewTypeCount() {\r