From 395ad3248e486e0351d134342264f1a008336d1d Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Wed, 15 Oct 2014 11:25:29 +0200 Subject: [PATCH] Creation of ThumbnailsCacheManager class, grouping all the methods needed to access the thumbnails cache --- .../datamodel/ThumbnailsCacheManager.java | 262 ++++++++++++++++++ .../providers/FileContentProvider.java | 187 ++++++++----- .../android/ui/adapter/DiskLruImageCache.java | 20 +- .../ui/adapter/FileListListAdapter.java | 210 ++------------ 4 files changed, 397 insertions(+), 282 deletions(-) create mode 100644 src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java diff --git a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java new file mode 100644 index 0000000000..9bff1fff0f --- /dev/null +++ b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -0,0 +1,262 @@ +/* ownCloud Android client application + * Copyright (C) 2012-2014 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android.datamodel; + +import java.io.File; +import java.lang.ref.WeakReference; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap.CompressFormat; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.media.ThumbnailUtils; +import android.os.AsyncTask; +import android.util.TypedValue; +import android.widget.ImageView; + +import com.owncloud.android.MainApp; +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.ui.adapter.DiskLruImageCache; +import com.owncloud.android.utils.BitmapUtils; +import com.owncloud.android.utils.DisplayUtils; + +/** + * Manager for concurrent access to thumbnails cache. + * + * @author Tobias Kaminsky + * @author David A. Velasco + */ +public class ThumbnailsCacheManager { + + private static final String TAG = ThumbnailsCacheManager.class.getSimpleName(); + + private static final String CACHE_FOLDER = "thumbnailCache"; + + private static final Object mThumbnailsDiskCacheLock = new Object(); + private static DiskLruImageCache mThumbnailCache; + private static boolean mThumbnailCacheStarting = true; + + private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB + private static final CompressFormat mCompressFormat = CompressFormat.JPEG; + private static final int mCompressQuality = 70; + + public static Bitmap mDefaultImg = + BitmapFactory.decodeResource( + MainApp.getAppContext().getResources(), + DisplayUtils.getResourceId("image/png", "default.png") + ); + + + public static class InitDiskCacheTask extends AsyncTask { + @Override + protected Void doInBackground(File... params) { + synchronized (mThumbnailsDiskCacheLock) { + try { + // 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 = + MainApp.getAppContext().getExternalCacheDir().getPath() + + File.separator + CACHE_FOLDER; + Log_OC.d(TAG, "create dir: " + cachePath); + final File diskCacheDir = new File(cachePath); + mThumbnailCache = new DiskLruImageCache( + diskCacheDir, + DISK_CACHE_SIZE, + mCompressFormat, + mCompressQuality + ); + } catch (Exception e) { + Log_OC.d(TAG, "Thumbnail cache could not be opened ", e); + mThumbnailCache = null; + } + mThumbnailCacheStarting = false; // Finished initialization + mThumbnailsDiskCacheLock.notifyAll(); // Wake any waiting threads + } + return null; + } + } + + + public static void addBitmapToCache(String key, Bitmap bitmap) { + synchronized (mThumbnailsDiskCacheLock) { + if (mThumbnailCache != null) { + mThumbnailCache.put(key, bitmap); + } + } + } + + + public static Bitmap getBitmapFromDiskCache(String key) { + synchronized (mThumbnailsDiskCacheLock) { + // Wait while disk cache is started from background thread + while (mThumbnailCacheStarting) { + try { + mThumbnailsDiskCacheLock.wait(); + } catch (InterruptedException e) {} + } + if (mThumbnailCache != null) { + return (Bitmap) mThumbnailCache.getBitmap(key); + } + } + return null; + } + + + public static boolean cancelPotentialWork(OCFile file, ImageView imageView) { + final ThumbnailGenerationTask bitmapWorkerTask = getBitmapWorkerTask(imageView); + + if (bitmapWorkerTask != null) { + final OCFile bitmapData = bitmapWorkerTask.mFile; + // If bitmapData is not yet set or it differs from the new data + if (bitmapData == null || bitmapData != file) { + // Cancel previous task + bitmapWorkerTask.cancel(true); + } else { + // The same work is already in progress + return false; + } + } + // No task associated with the ImageView, or an existing task was cancelled + return true; + } + + public static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { + if (imageView != null) { + final Drawable drawable = imageView.getDrawable(); + if (drawable instanceof AsyncDrawable) { + final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; + return asyncDrawable.getBitmapWorkerTask(); + } + } + return null; + } + + public static class ThumbnailGenerationTask extends AsyncTask { + private final WeakReference mImageViewReference; + private OCFile mFile; + private FileDataStorageManager mStorageManager; + + public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager storageManager) { + // Use a WeakReference to ensure the ImageView can be garbage collected + mImageViewReference = new WeakReference(imageView); + if (storageManager == null) + throw new IllegalArgumentException("storageManager must not be NULL"); + mStorageManager = storageManager; + } + + // Decode image in background. + @Override + protected Bitmap doInBackground(OCFile... params) { + Bitmap thumbnail = null; + + try { + mFile = params[0]; + final String imageKey = String.valueOf(mFile.getRemoteId()); + + // Check disk cache in background thread + thumbnail = getBitmapFromDiskCache(imageKey); + + // Not found in disk cache + if (thumbnail == null || mFile.needsUpdateThumbnail()) { + // Converts dp to pixel + Resources r = MainApp.getAppContext().getResources(); + int px = (int) Math.round(TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, 150, r.getDisplayMetrics() + )); + + if (mFile.isDown()){ + Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile( + mFile.getStoragePath(), px, px); + + if (bitmap != null) { + thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px); + + // Add thumbnail to cache + addBitmapToCache(imageKey, thumbnail); + + mFile.setNeedsUpdateThumbnail(false); + mStorageManager.saveFile(mFile); + } + + } + } + + } catch (Throwable t) { + // the app should never break due to a problem with thumbnails + Log_OC.e(TAG, "Generation of thumbnail for " + mFile + " failed", t); + if (t instanceof OutOfMemoryError) { + System.gc(); + } + } + + return thumbnail; + } + + protected void onPostExecute(Bitmap bitmap){ + if (isCancelled()) { + bitmap = null; + } + + if (mImageViewReference != null && bitmap != null) { + final ImageView imageView = mImageViewReference.get(); + final ThumbnailGenerationTask bitmapWorkerTask = + getBitmapWorkerTask(imageView); + if (this == bitmapWorkerTask && imageView != null) { + if (imageView.getTag().equals(mFile.getFileId())) { + imageView.setImageBitmap(bitmap); + } + } + } + } + } + + + public static class AsyncDrawable extends BitmapDrawable { + private final WeakReference bitmapWorkerTaskReference; + + public AsyncDrawable( + Resources res, Bitmap bitmap, ThumbnailGenerationTask bitmapWorkerTask + ) { + + super(res, bitmap); + bitmapWorkerTaskReference = + new WeakReference(bitmapWorkerTask); + } + + public ThumbnailGenerationTask getBitmapWorkerTask() { + return bitmapWorkerTaskReference.get(); + } + } + + + /** + * Remove from cache the remoteId passed + * @param fileRemoteId: remote id of mFile passed + */ + public static void removeFileFromCache(String fileRemoteId){ + synchronized (mThumbnailsDiskCacheLock) { + if (mThumbnailCache != null) { + mThumbnailCache.removeKey(fileRemoteId); + } + mThumbnailsDiskCacheLock.notifyAll(); // Wake any waiting threads + } + } + +} diff --git a/src/com/owncloud/android/providers/FileContentProvider.java b/src/com/owncloud/android/providers/FileContentProvider.java index 36bade5293..22a47aaba5 100644 --- a/src/com/owncloud/android/providers/FileContentProvider.java +++ b/src/com/owncloud/android/providers/FileContentProvider.java @@ -22,13 +22,11 @@ import java.util.ArrayList; import java.util.HashMap; import com.owncloud.android.R; +import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.shares.ShareType; -import com.owncloud.android.ui.adapter.DiskLruImageCache; - - import android.content.ContentProvider; import android.content.ContentProviderOperation; @@ -43,7 +41,6 @@ import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; -import android.graphics.Bitmap.CompressFormat; import android.net.Uri; import android.text.TextUtils; @@ -57,12 +54,6 @@ import android.text.TextUtils; public class FileContentProvider extends ContentProvider { private DataBaseHelper mDbHelper; - private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB - private static final CompressFormat mCompressFormat = CompressFormat.JPEG; - private static final int mCompressQuality = 70; - - private final Object thumbnailDiskCacheLock = new Object(); - private DiskLruImageCache mThumbnailCache; // Projection for filelist table private static HashMap mFileProjectionMap; @@ -177,7 +168,7 @@ public class FileContentProvider extends ContentProvider { String remoteId = ""; if (c != null && c.moveToFirst()) { remoteId = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_REMOTE_ID)); - removeFileFromCache(remoteId); + ThumbnailsCacheManager.removeFileFromCache(remoteId); } Log_OC.d(TAG, "Removing FILE " + remoteId); @@ -210,12 +201,24 @@ public class FileContentProvider extends ContentProvider { //String remotePath; while (!children.isAfterLast()) { childId = children.getLong(children.getColumnIndex(ProviderTableMeta._ID)); - isDir = "DIR".equals(children.getString(children.getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE))); + isDir = "DIR".equals(children.getString( + children.getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE) + )); //remotePath = children.getString(children.getColumnIndex(ProviderTableMeta.FILE_PATH)); if (isDir) { - count += delete(db, ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_DIR, childId), null, null); + count += delete( + db, + ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_DIR, childId), + null, + null + ); } else { - count += delete(db, ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, childId), null, null); + count += delete( + db, + ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, childId), + null, + null + ); } children.moveToNext(); } @@ -250,29 +253,6 @@ public class FileContentProvider extends ContentProvider { return count; } - /** - * Remove from cache the remoteId passed - * @param fileRemoteId: remote id of file passed - */ - private void removeFileFromCache(String fileRemoteId){ - Context context = getContext(); - if (context != null) { - synchronized (thumbnailDiskCacheLock) { - try { - mThumbnailCache = new DiskLruImageCache(context, "thumbnailCache", - DISK_CACHE_SIZE, mCompressFormat, mCompressQuality); - - mThumbnailCache.removeKey(fileRemoteId); - - } catch (Exception e) { - Log_OC.d(TAG, "Thumbnail cache could not be opened ", e); - mThumbnailCache = null; - } - thumbnailDiskCacheLock.notifyAll(); // Wake any waiting threads - } - } - } - @Override public String getType(Uri uri) { switch (mUriMatcher.match(uri)) { @@ -288,7 +268,6 @@ public class FileContentProvider extends ContentProvider { @Override public Uri insert(Uri uri, ContentValues values) { - //Log_OC.d(TAG, "Inserting " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this); Uri newUri = null; SQLiteDatabase db = mDbHelper.getWritableDatabase(); db.beginTransaction(); @@ -308,23 +287,31 @@ public class FileContentProvider extends ContentProvider { case SINGLE_FILE: String remotePath = values.getAsString(ProviderTableMeta.FILE_PATH); String accountName = values.getAsString(ProviderTableMeta.FILE_ACCOUNT_OWNER); - String[] projection = new String[] {ProviderTableMeta._ID, ProviderTableMeta.FILE_PATH, ProviderTableMeta.FILE_ACCOUNT_OWNER }; - String where = ProviderTableMeta.FILE_PATH + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?"; + String[] projection = new String[] { + ProviderTableMeta._ID, ProviderTableMeta.FILE_PATH, + ProviderTableMeta.FILE_ACCOUNT_OWNER + }; + String where = ProviderTableMeta.FILE_PATH + "=? AND " + + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?"; String[] whereArgs = new String[] {remotePath, accountName}; Cursor doubleCheck = query(db, uri, projection, where, whereArgs, null); - if (doubleCheck == null || !doubleCheck.moveToFirst()) { // ugly patch; serious refactorization is needed to reduce work in FileDataStorageManager and bring it to FileContentProvider + // ugly patch; serious refactorization is needed to reduce work in + // FileDataStorageManager and bring it to FileContentProvider + if (doubleCheck == null || !doubleCheck.moveToFirst()) { long rowId = db.insert(ProviderTableMeta.FILE_TABLE_NAME, null, values); if (rowId > 0) { - Uri insertedFileUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, rowId); - //Log_OC.d(TAG, "Inserted " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this); + Uri insertedFileUri = + ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, rowId); return insertedFileUri; } else { - //Log_OC.d(TAG, "Error while inserting " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this); throw new SQLException("ERROR " + uri); } } else { // file is already inserted; race condition, let's avoid a duplicated entry - Uri insertedFileUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, doubleCheck.getLong(doubleCheck.getColumnIndex(ProviderTableMeta._ID))); + Uri insertedFileUri = ContentUris.withAppendedId( + ProviderTableMeta.CONTENT_URI_FILE, + doubleCheck.getLong(doubleCheck.getColumnIndex(ProviderTableMeta._ID)) + ); doubleCheck.close(); return insertedFileUri; @@ -333,22 +320,35 @@ public class FileContentProvider extends ContentProvider { case SHARES: String path = values.getAsString(ProviderTableMeta.OCSHARES_PATH); String accountNameShare= values.getAsString(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER); - String[] projectionShare = new String[] {ProviderTableMeta._ID, ProviderTableMeta.OCSHARES_PATH, ProviderTableMeta.OCSHARES_ACCOUNT_OWNER }; - String whereShare = ProviderTableMeta.OCSHARES_PATH + "=? AND " + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?"; + String[] projectionShare = new String[] { + ProviderTableMeta._ID, ProviderTableMeta.OCSHARES_PATH, + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + }; + String whereShare = ProviderTableMeta.OCSHARES_PATH + "=? AND " + + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?"; String[] whereArgsShare = new String[] {path, accountNameShare}; Uri insertedShareUri = null; - Cursor doubleCheckShare = query(db, uri, projectionShare, whereShare, whereArgsShare, null); - if (doubleCheckShare == null || !doubleCheckShare.moveToFirst()) { // ugly patch; serious refactorization is needed to reduce work in FileDataStorageManager and bring it to FileContentProvider + Cursor doubleCheckShare = + query(db, uri, projectionShare, whereShare, whereArgsShare, null); + // ugly patch; serious refactorization is needed to reduce work in + // FileDataStorageManager and bring it to FileContentProvider + if (doubleCheckShare == null || !doubleCheckShare.moveToFirst()) { long rowId = db.insert(ProviderTableMeta.OCSHARES_TABLE_NAME, null, values); if (rowId >0) { - insertedShareUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_SHARE, rowId); + insertedShareUri = + ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_SHARE, rowId); } else { throw new SQLException("ERROR " + uri); } } else { // file is already inserted; race condition, let's avoid a duplicated entry - insertedShareUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_SHARE, doubleCheckShare.getLong(doubleCheckShare.getColumnIndex(ProviderTableMeta._ID))); + insertedShareUri = ContentUris.withAppendedId( + ProviderTableMeta.CONTENT_URI_SHARE, + doubleCheckShare.getLong( + doubleCheckShare.getColumnIndex(ProviderTableMeta._ID) + ) + ); doubleCheckShare.close(); } updateFilesTableAccordingToShareInsertion(db, uri, values); @@ -361,11 +361,17 @@ public class FileContentProvider extends ContentProvider { } - private void updateFilesTableAccordingToShareInsertion(SQLiteDatabase db, Uri uri, ContentValues shareValues) { + private void updateFilesTableAccordingToShareInsertion( + SQLiteDatabase db, Uri uri, ContentValues shareValues + ) { ContentValues fileValues = new ContentValues(); - fileValues.put(ProviderTableMeta.FILE_SHARE_BY_LINK, - ShareType.PUBLIC_LINK.getValue() == shareValues.getAsInteger(ProviderTableMeta.OCSHARES_SHARE_TYPE)? 1 : 0); - String whereShare = ProviderTableMeta.FILE_PATH + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?"; + fileValues.put( + ProviderTableMeta.FILE_SHARE_BY_LINK, + ShareType.PUBLIC_LINK.getValue() == + shareValues.getAsInteger(ProviderTableMeta.OCSHARES_SHARE_TYPE)? 1 : 0 + ); + String whereShare = ProviderTableMeta.FILE_PATH + "=? AND " + + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?"; String[] whereArgsShare = new String[] { shareValues.getAsString(ProviderTableMeta.OCSHARES_PATH), shareValues.getAsString(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER) @@ -393,7 +399,14 @@ public class FileContentProvider extends ContentProvider { @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + public Cursor query( + Uri uri, + String[] projection, + String selection, + String[] selectionArgs, + String sortOrder + ) { + Cursor result = null; SQLiteDatabase db = mDbHelper.getReadableDatabase(); db.beginTransaction(); @@ -406,7 +419,15 @@ public class FileContentProvider extends ContentProvider { return result; } - private Cursor query(SQLiteDatabase db, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + private Cursor query( + SQLiteDatabase db, + Uri uri, + String[] projection, + String selection, + String[] selectionArgs, + String sortOrder + ) { + SQLiteQueryBuilder sqlQuery = new SQLiteQueryBuilder(); sqlQuery.setTables(ProviderTableMeta.FILE_TABLE_NAME); @@ -460,7 +481,6 @@ public class FileContentProvider extends ContentProvider { @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - //Log_OC.d(TAG, "Updating " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this); int count = 0; SQLiteDatabase db = mDbHelper.getWritableDatabase(); db.beginTransaction(); @@ -476,14 +496,24 @@ public class FileContentProvider extends ContentProvider { - private int update(SQLiteDatabase db, Uri uri, ContentValues values, String selection, String[] selectionArgs) { + private int update( + SQLiteDatabase db, + Uri uri, + ContentValues values, + String selection, + String[] selectionArgs + ) { switch (mUriMatcher.match(uri)) { case DIRECTORY: return 0; //updateFolderSize(db, selectionArgs[0]); case SHARES: - return db.update(ProviderTableMeta.OCSHARES_TABLE_NAME, values, selection, selectionArgs); + return db.update( + ProviderTableMeta.OCSHARES_TABLE_NAME, values, selection, selectionArgs + ); default: - return db.update(ProviderTableMeta.FILE_TABLE_NAME, values, selection, selectionArgs); + return db.update( + ProviderTableMeta.FILE_TABLE_NAME, values, selection, selectionArgs + ); } } @@ -541,8 +571,10 @@ public class FileContentProvider extends ContentProvider { */ @Override - public ContentProviderResult[] applyBatch (ArrayList operations) throws OperationApplicationException { - Log_OC.d("FileContentProvider", "applying batch in provider " + this + " (temporary: " + isTemporary() + ")" ); + public ContentProviderResult[] applyBatch (ArrayList operations) + throws OperationApplicationException { + Log_OC.d("FileContentProvider", "applying batch in provider " + this + + " (temporary: " + isTemporary() + ")" ); ContentProviderResult[] results = new ContentProviderResult[operations.size()]; int i=0; @@ -631,12 +663,13 @@ public class FileContentProvider extends ContentProvider { db.beginTransaction(); try { db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME + - " ADD COLUMN " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER " + - " DEFAULT 0"); + " ADD COLUMN " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + + " INTEGER " + " DEFAULT 0"); // assume there are not local changes pending to upload db.execSQL("UPDATE " + ProviderTableMeta.FILE_TABLE_NAME + - " SET " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " = " + System.currentTimeMillis() + + " SET " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " = " + + System.currentTimeMillis() + " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL"); upgraded = true; @@ -650,11 +683,12 @@ public class FileContentProvider extends ContentProvider { db.beginTransaction(); try { db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME + - " ADD COLUMN " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER " + - " DEFAULT 0"); + " ADD COLUMN " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + + " INTEGER " + " DEFAULT 0"); db.execSQL("UPDATE " + ProviderTableMeta.FILE_TABLE_NAME + - " SET " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " = " + ProviderTableMeta.FILE_MODIFIED + + " SET " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " = " + + ProviderTableMeta.FILE_MODIFIED + " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL"); upgraded = true; @@ -664,7 +698,8 @@ public class FileContentProvider extends ContentProvider { } } if (!upgraded) - Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion); + Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + + ", newVersion == " + newVersion); if (oldVersion < 5 && newVersion >= 5) { Log_OC.i("SQL", "Entering in the #4 ADD in onUpgrade"); @@ -681,7 +716,8 @@ public class FileContentProvider extends ContentProvider { } } if (!upgraded) - Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion); + Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + + ", newVersion == " + newVersion); if (oldVersion < 6 && newVersion >= 6) { Log_OC.i("SQL", "Entering in the #5 ADD in onUpgrade"); @@ -720,7 +756,8 @@ public class FileContentProvider extends ContentProvider { } } if (!upgraded) - Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion); + Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + + ", newVersion == " + newVersion); if (oldVersion < 7 && newVersion >= 7) { Log_OC.i("SQL", "Entering in the #7 ADD in onUpgrade"); @@ -741,7 +778,8 @@ public class FileContentProvider extends ContentProvider { } } if (!upgraded) - Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion); + Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + + ", newVersion == " + newVersion); if (oldVersion < 8 && newVersion >= 8) { Log_OC.i("SQL", "Entering in the #8 ADD in onUpgrade"); @@ -758,7 +796,8 @@ public class FileContentProvider extends ContentProvider { } } if (!upgraded) - Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion); + Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + + ", newVersion == " + newVersion); } } diff --git a/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java b/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java index 1983ff0483..93efdf1cd3 100644 --- a/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java +++ b/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java @@ -25,7 +25,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; @@ -45,9 +44,11 @@ public class DiskLruImageCache { private static final String TAG = DiskLruImageCache.class.getSimpleName(); - public DiskLruImageCache( Context context,String uniqueName, int diskCacheSize, - CompressFormat compressFormat, int quality ) throws IOException { - final File diskCacheDir = getDiskCacheDir(context, uniqueName ); + //public DiskLruImageCache( Context context,String uniqueName, int diskCacheSize, + public DiskLruImageCache( + File diskCacheDir, int diskCacheSize, CompressFormat compressFormat, int quality + ) throws IOException { + mDiskCache = DiskLruCache.open( diskCacheDir, CACHE_VERSION, VALUE_COUNT, diskCacheSize ); @@ -68,17 +69,6 @@ public class DiskLruImageCache { } } - private File getDiskCacheDir(Context context, String uniqueName) { - - // 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 = context.getExternalCacheDir().getPath(); - - Log_OC.d(TAG, "create dir: " + cachePath + File.separator + uniqueName); - - return new File(cachePath + File.separator + uniqueName); - } - public void put( String key, Bitmap data ) { DiskLruCache.Editor editor = null; diff --git a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java index c6614554cd..4050113353 100644 --- a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java @@ -17,21 +17,11 @@ */ package com.owncloud.android.ui.adapter; -import java.io.File; -import java.lang.ref.WeakReference; import java.util.Vector; import android.accounts.Account; import android.content.Context; -import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.Bitmap.CompressFormat; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.media.ThumbnailUtils; -import android.os.AsyncTask; -import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -45,11 +35,11 @@ import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.ThumbnailsCacheManager; +import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncDrawable; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; -import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.activity.ComponentsGetter; -import com.owncloud.android.utils.BitmapUtils; import com.owncloud.android.utils.DisplayUtils; @@ -64,8 +54,6 @@ import com.owncloud.android.utils.DisplayUtils; public class FileListListAdapter extends BaseAdapter implements ListAdapter { private final static String PERMISSION_SHARED_WITH_ME = "S"; - private static final String TAG = FileListListAdapter.class.getSimpleName(); - private Context mContext; private OCFile mFile = null; private Vector mFiles = null; @@ -75,14 +63,6 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter { private Account mAccount; private ComponentsGetter mTransferServiceGetter; - private final Object thumbnailDiskCacheLock = new Object(); - private DiskLruImageCache mThumbnailCache; - private boolean mThumbnailCacheStarting = true; - private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB - private static final CompressFormat mCompressFormat = CompressFormat.JPEG; - private static final int mCompressQuality = 70; - private Bitmap defaultImg; - public FileListListAdapter( boolean justFolders, Context context, @@ -93,145 +73,11 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter { mContext = context; mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext); mTransferServiceGetter = transferServiceGetter; - defaultImg = BitmapFactory.decodeResource(mContext.getResources(), - DisplayUtils.getResourceId("image/png", "default.png")); - // Initialise disk cache on background thread - new InitDiskCacheTask().execute(); + // initialise thumbnails cache on background thread + new ThumbnailsCacheManager.InitDiskCacheTask().execute(); } - class InitDiskCacheTask extends AsyncTask { - @Override - protected Void doInBackground(File... params) { - synchronized (thumbnailDiskCacheLock) { - try { - mThumbnailCache = new DiskLruImageCache(mContext, "thumbnailCache", - DISK_CACHE_SIZE, mCompressFormat, mCompressQuality); - } catch (Exception e) { - Log_OC.d(TAG, "Thumbnail cache could not be opened ", e); - mThumbnailCache = null; - } - mThumbnailCacheStarting = false; // Finished initialization - thumbnailDiskCacheLock.notifyAll(); // Wake any waiting threads - } - return null; - } - } - - static class AsyncDrawable extends BitmapDrawable { - private final WeakReference bitmapWorkerTaskReference; - - public AsyncDrawable(Resources res, Bitmap bitmap, - ThumbnailGenerationTask bitmapWorkerTask) { - super(res, bitmap); - bitmapWorkerTaskReference = - new WeakReference(bitmapWorkerTask); - } - - public ThumbnailGenerationTask getBitmapWorkerTask() { - return bitmapWorkerTaskReference.get(); - } - } - - class ThumbnailGenerationTask extends AsyncTask { - private final WeakReference imageViewReference; - private OCFile file; - - - public ThumbnailGenerationTask(ImageView imageView) { - // Use a WeakReference to ensure the ImageView can be garbage collected - imageViewReference = new WeakReference(imageView); - } - - // Decode image in background. - @Override - protected Bitmap doInBackground(OCFile... params) { - Bitmap thumbnail = null; - - try { - file = params[0]; - final String imageKey = String.valueOf(file.getRemoteId()); - - // Check disk cache in background thread - thumbnail = getBitmapFromDiskCache(imageKey); - - // Not found in disk cache - if (thumbnail == null || file.needsUpdateThumbnail()) { - // Converts dp to pixel - Resources r = mContext.getResources(); - int px = (int) Math.round(TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, 150, r.getDisplayMetrics() - )); - - if (file.isDown()){ - Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile( - file.getStoragePath(), px, px); - - if (bitmap != null) { - thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px); - - // Add thumbnail to cache - addBitmapToCache(imageKey, thumbnail); - - file.setNeedsUpdateThumbnail(false); - mStorageManager.saveFile(file); - } - - } - } - - } catch (Throwable t) { - // the app should never break due to a problem with thumbnails - Log_OC.e(TAG, "Generation of thumbnail for " + file + " failed", t); - if (t instanceof OutOfMemoryError) { - System.gc(); - } - } - - return thumbnail; - } - - protected void onPostExecute(Bitmap bitmap){ - if (isCancelled()) { - bitmap = null; - } - - if (imageViewReference != null && bitmap != null) { - final ImageView imageView = imageViewReference.get(); - final ThumbnailGenerationTask bitmapWorkerTask = - getBitmapWorkerTask(imageView); - if (this == bitmapWorkerTask && imageView != null) { - if (imageView.getTag().equals(file.getFileId())) { - imageView.setImageBitmap(bitmap); - } - } - } - } - } - - public void addBitmapToCache(String key, Bitmap bitmap) { - synchronized (thumbnailDiskCacheLock) { - if (mThumbnailCache != null) { - mThumbnailCache.put(key, bitmap); - } - } - } - - public Bitmap getBitmapFromDiskCache(String key) { - synchronized (thumbnailDiskCacheLock) { - // Wait while disk cache is started from background thread - while (mThumbnailCacheStarting) { - try { - thumbnailDiskCacheLock.wait(); - } catch (InterruptedException e) {} - } - if (mThumbnailCache != null) { - return (Bitmap) mThumbnailCache.getBitmap(key); - } - } - return null; - } - @Override public boolean areAllItemsEnabled() { return true; @@ -339,16 +185,23 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter { // get Thumbnail if file is image if (file.isImage()){ // Thumbnail in Cache? - Bitmap thumbnail = getBitmapFromDiskCache(String.valueOf(file.getRemoteId())); + Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( + String.valueOf(file.getRemoteId()) + ); if (thumbnail != null && !file.needsUpdateThumbnail()){ fileIcon.setImageBitmap(thumbnail); } else { // generate new Thumbnail - if (cancelPotentialWork(file, fileIcon)) { - final ThumbnailGenerationTask task = - new ThumbnailGenerationTask(fileIcon); - final AsyncDrawable asyncDrawable = - new AsyncDrawable(mContext.getResources(), defaultImg, task); + if (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon)) { + final ThumbnailsCacheManager.ThumbnailGenerationTask task = + new ThumbnailsCacheManager.ThumbnailGenerationTask( + fileIcon, mStorageManager + ); + final AsyncDrawable asyncDrawable = new AsyncDrawable( + mContext.getResources(), + ThumbnailsCacheManager.mDefaultImg, + task + ); fileIcon.setImageDrawable(asyncDrawable); task.execute(file); } @@ -399,35 +252,6 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter { return view; } - public static boolean cancelPotentialWork(OCFile file, ImageView imageView) { - final ThumbnailGenerationTask bitmapWorkerTask = getBitmapWorkerTask(imageView); - - if (bitmapWorkerTask != null) { - final OCFile bitmapData = bitmapWorkerTask.file; - // If bitmapData is not yet set or it differs from the new data - if (bitmapData == null || bitmapData != file) { - // Cancel previous task - bitmapWorkerTask.cancel(true); - } else { - // The same work is already in progress - return false; - } - } - // No task associated with the ImageView, or an existing task was cancelled - return true; - } - - private static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncDrawable) { - final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } - @Override public int getViewTypeCount() { return 1;