Creation of ThumbnailsCacheManager class, grouping all the methods needed to access...
authorDavid A. Velasco <dvelasco@solidgear.es>
Wed, 15 Oct 2014 09:25:29 +0000 (11:25 +0200)
committerDavid A. Velasco <dvelasco@solidgear.es>
Wed, 15 Oct 2014 09:25:29 +0000 (11:25 +0200)
src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java [new file with mode: 0644]
src/com/owncloud/android/providers/FileContentProvider.java
src/com/owncloud/android/ui/adapter/DiskLruImageCache.java
src/com/owncloud/android/ui/adapter/FileListListAdapter.java

diff --git a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java
new file mode 100644 (file)
index 0000000..9bff1ff
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+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<File, Void, Void> {
+        @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<OCFile, Void, Bitmap> {
+        private final WeakReference<ImageView> 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>(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<ThumbnailGenerationTask> bitmapWorkerTaskReference;
+
+        public AsyncDrawable(
+                Resources res, Bitmap bitmap, ThumbnailGenerationTask bitmapWorkerTask
+            ) {
+            
+            super(res, bitmap);
+            bitmapWorkerTaskReference =
+                new WeakReference<ThumbnailGenerationTask>(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
+        }
+    }
+
+}
index 36bade5..22a47aa 100644 (file)
@@ -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<String, String> 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<ContentProviderOperation> operations) throws OperationApplicationException {
-        Log_OC.d("FileContentProvider", "applying batch in provider " + this + " (temporary: " + isTemporary() + ")" );
+    public ContentProviderResult[] applyBatch (ArrayList<ContentProviderOperation> 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);
         }
     }
 
index 1983ff0..93efdf1 100644 (file)
@@ -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;
index c661455..4050113 100644 (file)
  */\r
 package com.owncloud.android.ui.adapter;\r
 \r
-import java.io.File;\r
-import java.lang.ref.WeakReference;\r
 import java.util.Vector;\r
 \r
 import android.accounts.Account;\r
 import android.content.Context;\r
-import android.content.res.Resources;\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
 import android.view.LayoutInflater;\r
 import android.view.View;\r
 import android.view.ViewGroup;\r
@@ -45,11 +35,11 @@ import com.owncloud.android.R;
 import com.owncloud.android.authentication.AccountUtils;\r
 import com.owncloud.android.datamodel.FileDataStorageManager;\r
 import com.owncloud.android.datamodel.OCFile;\r
+import com.owncloud.android.datamodel.ThumbnailsCacheManager;\r
+import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncDrawable;\r
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
-import com.owncloud.android.lib.common.utils.Log_OC;\r
 import com.owncloud.android.ui.activity.ComponentsGetter;\r
-import com.owncloud.android.utils.BitmapUtils;\r
 import com.owncloud.android.utils.DisplayUtils;\r
 \r
 \r
@@ -64,8 +54,6 @@ import com.owncloud.android.utils.DisplayUtils;
 public class FileListListAdapter extends BaseAdapter implements ListAdapter {\r
     private final static String PERMISSION_SHARED_WITH_ME = "S";\r
     \r
-    private static final String TAG = FileListListAdapter.class.getSimpleName();\r
-\r
     private Context mContext;\r
     private OCFile mFile = null;\r
     private Vector<OCFile> mFiles = null;\r
@@ -75,14 +63,6 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
     private Account mAccount;
     private ComponentsGetter mTransferServiceGetter;\r
     \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 static final CompressFormat mCompressFormat = CompressFormat.JPEG;\r
-    private static final int mCompressQuality = 70;\r
-    private Bitmap defaultImg;\r
-        \r
     public FileListListAdapter(\r
             boolean justFolders, \r
             Context context, \r
@@ -93,145 +73,11 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
         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
-    }\r
-    \r
-    class InitDiskCacheTask extends AsyncTask<File, Void, Void> {\r
-        @Override\r
-        protected Void doInBackground(File... params) {\r
-            synchronized (thumbnailDiskCacheLock) {\r
-                try {\r
-                    mThumbnailCache = new DiskLruImageCache(mContext, "thumbnailCache", \r
-                                        DISK_CACHE_SIZE, mCompressFormat, mCompressQuality);\r
-                } catch (Exception e) {\r
-                    Log_OC.d(TAG, "Thumbnail cache could not be opened ", e);\r
-                    mThumbnailCache = null;\r
-                }\r
-                mThumbnailCacheStarting = false; // Finished initialization\r
-                thumbnailDiskCacheLock.notifyAll(); // Wake any waiting threads\r
-            }\r
-            return null;\r
-        }\r
+        // initialise thumbnails cache on background thread\r
+        new ThumbnailsCacheManager.InitDiskCacheTask().execute();\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
-        \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
-            Bitmap thumbnail = null;\r
-            \r
-            try {\r
-                file = params[0];\r
-                final String imageKey = String.valueOf(file.getRemoteId());\r
-    \r
-                // Check disk cache in background thread\r
-                thumbnail = getBitmapFromDiskCache(imageKey);\r
-    \r
-                // Not found in disk cache\r
-                if (thumbnail == null || file.needsUpdateThumbnail()) { \r
-                    // Converts dp to pixel\r
-                    Resources r = mContext.getResources();\r
-                    int px = (int) Math.round(TypedValue.applyDimension(\r
-                            TypedValue.COMPLEX_UNIT_DIP, 150, r.getDisplayMetrics()\r
-                    ));\r
-                    \r
-                    if (file.isDown()){\r
-                        Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(\r
-                                file.getStoragePath(), px, px);\r
-                        \r
-                        if (bitmap != null) {\r
-                            thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);\r
-    \r
-                            // Add thumbnail to cache\r
-                            addBitmapToCache(imageKey, thumbnail);\r
-\r
-                            file.setNeedsUpdateThumbnail(false);\r
-                            mStorageManager.saveFile(file);\r
-                        }\r
-    \r
-                    }\r
-                }\r
-                \r
-            } catch (Throwable t) {\r
-                // the app should never break due to a problem with thumbnails\r
-                Log_OC.e(TAG, "Generation of thumbnail for " + file + " failed", t);\r
-                if (t instanceof OutOfMemoryError) {\r
-                    System.gc();\r
-                }\r
-            }\r
-            \r
-            return thumbnail;\r
-        }\r
-        \r
-        protected void onPostExecute(Bitmap 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
-                    if (imageView.getTag().equals(file.getFileId())) {\r
-                        imageView.setImageBitmap(bitmap);\r
-                    }\r
-                }\r
-            }\r
-        }\r
-    }\r
-  \r
-    public void addBitmapToCache(String key, Bitmap bitmap) {\r
-        synchronized (thumbnailDiskCacheLock) {\r
-            if (mThumbnailCache != null) {\r
-                mThumbnailCache.put(key, bitmap);\r
-            }\r
-        }\r
-    }\r
-\r
-    public Bitmap getBitmapFromDiskCache(String key) {\r
-        synchronized (thumbnailDiskCacheLock) {\r
-            // Wait while disk cache is started from background thread\r
-            while (mThumbnailCacheStarting) {\r
-                try {\r
-                    thumbnailDiskCacheLock.wait();\r
-                } catch (InterruptedException e) {}\r
-            }\r
-            if (mThumbnailCache != null) {\r
-                return (Bitmap) mThumbnailCache.getBitmap(key);\r
-            }\r
-        }\r
-        return null;\r
-    }
-\r
     @Override\r
     public boolean areAllItemsEnabled() {\r
         return true;\r
@@ -339,16 +185,23 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
                 // get Thumbnail if file is image\r
                 if (file.isImage()){\r
                      // Thumbnail in Cache?\r
-                    Bitmap thumbnail = getBitmapFromDiskCache(String.valueOf(file.getRemoteId()));\r
+                    Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(\r
+                            String.valueOf(file.getRemoteId())\r
+                    );\r
                     if (thumbnail != null && !file.needsUpdateThumbnail()){\r
                         fileIcon.setImageBitmap(thumbnail);\r
                     } else {\r
                         // generate new Thumbnail\r
-                        if (cancelPotentialWork(file, fileIcon)) {\r
-                            final ThumbnailGenerationTask task = \r
-                                    new ThumbnailGenerationTask(fileIcon);\r
-                            final AsyncDrawable asyncDrawable =\r
-                                    new AsyncDrawable(mContext.getResources(), defaultImg, task);\r
+                        if (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon)) {\r
+                            final ThumbnailsCacheManager.ThumbnailGenerationTask task = \r
+                                    new ThumbnailsCacheManager.ThumbnailGenerationTask(\r
+                                            fileIcon, mStorageManager\r
+                                    );\r
+                            final AsyncDrawable asyncDrawable = new AsyncDrawable(\r
+                                    mContext.getResources(), \r
+                                    ThumbnailsCacheManager.mDefaultImg, \r
+                                    task\r
+                            );\r
                             fileIcon.setImageDrawable(asyncDrawable);\r
                             task.execute(file);\r
                         }\r
@@ -399,35 +252,6 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
         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
         return 1;\r