Ver código fonte

Merge branch 'develop' of https://github.com/tobiasKaminsky/android into thumbnails_for_downloaded_images

David A. Velasco 10 anos atrás
pai
commit
aae90eb1aa

+ 2 - 2
.classpath

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="src" path="gen"/>
 	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
 	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
 	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="gen"/>
 	<classpathentry kind="output" path="bin/classes"/>
 </classpath>

BIN
libs/disklrucache-2.0.2.jar


+ 0 - 3
lint.xml

@@ -1,3 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<lint>
-</lint>

+ 170 - 0
src/com/owncloud/android/ui/adapter/DiskLruImageCache.java

@@ -0,0 +1,170 @@
+package com.owncloud.android.ui.adapter;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+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;
+import android.util.Log;
+
+import com.jakewharton.disklrucache.DiskLruCache;
+import com.owncloud.android.BuildConfig;
+import com.owncloud.android.utils.Log_OC;
+
+public class DiskLruImageCache {
+
+    private DiskLruCache mDiskCache;
+    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, CACHE_VERSION, VALUE_COUNT, diskCacheSize 
+                );
+                mCompressFormat = compressFormat;
+                mCompressQuality = quality;
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+    }
+
+    private boolean writeBitmapToFile( Bitmap bitmap, DiskLruCache.Editor editor )
+        throws IOException, FileNotFoundException {
+        OutputStream out = null;
+        try {
+            out = new BufferedOutputStream( editor.newOutputStream( 0 ), IO_BUFFER_SIZE );
+            return bitmap.compress( mCompressFormat, mCompressQuality, out );
+        } finally {
+            if ( out != null ) {
+                out.close();
+            }
+        }
+    }
+
+    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("DiskCache", "create dir: " + cachePath + File.separator + uniqueName);
+                    
+        return new File(cachePath + File.separator + uniqueName);
+    }
+
+    public void put( String key, Bitmap data ) {
+
+        DiskLruCache.Editor editor = null;
+        try {
+            editor = mDiskCache.edit( key );
+            if ( editor == null ) {
+                return;
+            }
+
+            if( writeBitmapToFile( data, editor ) ) {               
+                mDiskCache.flush();
+                editor.commit();
+                if ( BuildConfig.DEBUG ) {
+                   Log.d( "cache_test_DISK_", "image put on disk cache " + key );
+                }
+            } else {
+                editor.abort();
+                if ( BuildConfig.DEBUG ) {
+                    Log.d( "cache_test_DISK_", "ERROR on: image put on disk cache " + key );
+                }
+            }   
+        } catch (IOException e) {
+            if ( BuildConfig.DEBUG ) {
+                Log.d( "cache_test_DISK_", "ERROR on: image put on disk cache " + key );
+            }
+            try {
+                if ( editor != null ) {
+                    editor.abort();
+                }
+            } catch (IOException ignored) {
+            }           
+        }
+
+    }
+
+    public Bitmap getBitmap( String key ) {
+
+        Bitmap bitmap = null;
+        DiskLruCache.Snapshot snapshot = null;
+        try {
+
+            snapshot = mDiskCache.get( key );
+            if ( snapshot == null ) {
+                return null;
+            }
+            final InputStream in = snapshot.getInputStream( 0 );
+            if ( in != null ) {
+                final BufferedInputStream buffIn = 
+                new BufferedInputStream( in, IO_BUFFER_SIZE );
+                bitmap = BitmapFactory.decodeStream( buffIn );              
+            }   
+        } catch ( IOException e ) {
+            e.printStackTrace();
+        } finally {
+            if ( snapshot != null ) {
+                snapshot.close();
+            }
+        }
+
+        if ( BuildConfig.DEBUG ) {
+            Log.d("cache_test_DISK_", bitmap == null ? "not found" : "image read from disk " + key);
+        }
+
+        return bitmap;
+
+    }
+
+    public boolean containsKey( String key ) {
+
+        boolean contained = false;
+        DiskLruCache.Snapshot snapshot = null;
+        try {
+            snapshot = mDiskCache.get( key );
+            contained = snapshot != null;
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if ( snapshot != null ) {
+                snapshot.close();
+            }
+        }
+
+        return contained;
+
+    }
+
+    public void clearCache() {
+        if ( BuildConfig.DEBUG ) {
+            Log.d( "cache_test_DISK_", "disk cache CLEARED");
+        }
+        try {
+            mDiskCache.delete();
+        } catch ( IOException e ) {
+            e.printStackTrace();
+        }
+    }
+
+    public File getCacheFolder() {
+        return mDiskCache.getDirectory();
+    }
+
+}

+ 273 - 14
src/com/owncloud/android/ui/adapter/FileListListAdapter.java

@@ -17,10 +17,25 @@
  */
 package com.owncloud.android.ui.adapter;
 
+import java.io.File;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+//import java.net.URLEncoder;
 import java.util.Vector;
 
 import android.accounts.Account;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
 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;
@@ -36,15 +51,30 @@ import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.lib.common.OwnCloudAccount;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
+import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
 import com.owncloud.android.ui.activity.ComponentsGetter;
 import com.owncloud.android.utils.DisplayUtils;
 
+/*
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.util.EntityUtils;
+*/
+
 
 /**
  * This Adapter populates a ListView with all files and folders in an ownCloud
  * instance.
  * 
  * @author Bartek Przybylski
+ * @Author Tobias Kaminsky
  * 
  */
 public class FileListListAdapter extends BaseAdapter implements ListAdapter {
@@ -59,15 +89,184 @@ 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 OwnCloudClient mClient;
+    private Bitmap defaultImg;
+        
     public FileListListAdapter(
             boolean justFolders, 
             Context context, 
             ComponentsGetter transferServiceGetter
             ) {
+        
         mJustFolders = justFolders;
         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();
+    }
+    
+    class InitDiskCacheTask extends AsyncTask<File, Void, Void> {
+        @Override
+        protected Void doInBackground(File... params) {
+            synchronized (thumbnailDiskCacheLock) {
+                mThumbnailCache = new DiskLruImageCache(mContext, "thumbnailCache", 
+                                    DISK_CACHE_SIZE, mCompressFormat, mCompressQuality);
+                
+                try {
+                    OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext);
+                    mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+                            getClientFor(ocAccount, mContext);
+                } catch (AccountNotFoundException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                } catch (AuthenticatorException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                } catch (OperationCanceledException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                } catch (IOException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+
+                mThumbnailCacheStarting = false; // Finished initialization
+                thumbnailDiskCacheLock.notifyAll(); // Wake any waiting threads
+            }
+            return null;
+        }
+    }
+    
+    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();
+        }
+    }
+
+    class ThumbnailGenerationTask extends AsyncTask<OCFile, Void, Bitmap> {
+        private final WeakReference<ImageView> imageViewReference;
+        private OCFile file;
+
+        
+        public ThumbnailGenerationTask(ImageView imageView) {
+         // Use a WeakReference to ensure the ImageView can be garbage collected
+            imageViewReference = new WeakReference<ImageView>(imageView);
+        }
+
+        // Decode image in background.
+        @Override
+        protected Bitmap doInBackground(OCFile... params) {
+            file = params[0];
+            final String imageKey = String.valueOf(file.getRemoteId());
+
+            // Check disk cache in background thread
+            Bitmap thumbnail = getBitmapFromDiskCache(imageKey);
+
+            // Not found in disk cache
+            if (thumbnail == null) { 
+                // 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 = BitmapFactory.decodeFile(file.getStoragePath());
+                    thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);
+                    
+                    // Add thumbnail to cache
+                    addBitmapToCache(imageKey, thumbnail);
+
+                } else {
+                    // Download thumbnail from server
+                    // Commented out as maybe changes to client library are needed
+//                    DefaultHttpClient httpclient = new DefaultHttpClient();
+//                    try {
+//                        httpclient.getCredentialsProvider().setCredentials(
+//                                new AuthScope(mClient.getBaseUri().toString().replace("https://", ""), 443), 
+//                                new UsernamePasswordCredentials(mClient.getCredentials().getUsername(), mClient.getCredentials().getAuthToken()));
+//                        
+//
+//                        HttpGet httpget = new HttpGet(mClient.getBaseUri() + "/ocs/v1.php/thumbnail?x=50&y=50&path=" + URLEncoder.encode(file.getRemotePath(), "UTF-8"));
+//                        HttpResponse response = httpclient.execute(httpget);
+//                        HttpEntity entity = response.getEntity();
+//                        
+//                        if (entity != null) {
+//                            byte[] bytes = EntityUtils.toByteArray(entity);
+//                            Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
+//                            thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);
+//                            
+//                            // Add thumbnail to cache
+//                            if (thumbnail != null){
+//                                addBitmapToCache(imageKey, thumbnail);
+//                            }
+//                        }
+//                    } catch(Exception e){
+//                        e.printStackTrace();
+//                    }finally {
+//                        httpclient.getConnectionManager().shutdown();
+//                    }
+                } 
+            }
+            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) {
+                    imageView.setImageBitmap(bitmap);
+                }
+            }
+        }
+    }
+  
+    public void addBitmapToCache(String key, Bitmap bitmap) {
+        synchronized (thumbnailDiskCacheLock) {
+            if (mThumbnailCache != null && mThumbnailCache.getBitmap(key) == 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
@@ -112,10 +311,10 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
                     .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
             view = inflator.inflate(R.layout.list_item, null);
         }
-    
+         
         if (mFiles != null && mFiles.size() > position) {
             OCFile file = mFiles.get(position);
-            TextView fileName = (TextView) view.findViewById(R.id.Filename);
+            TextView fileName = (TextView) view.findViewById(R.id.Filename);           
             String name = file.getFileName();
 
             fileName.setText(name);
@@ -126,7 +325,8 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
 
             ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);
             localStateView.bringToFront();
-            FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();
+            FileDownloaderBinder downloaderBinder = 
+                    mTransferServiceGetter.getFileDownloaderBinder();
             FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();
             if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {
                 localStateView.setImageResource(R.drawable.downloading_file_indicator);
@@ -149,7 +349,9 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
                 fileSizeV.setVisibility(View.VISIBLE);
                 fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
                 lastModV.setVisibility(View.VISIBLE);
-                lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));
+                lastModV.setText(
+                        DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp())
+                );
                 // this if-else is needed even thoe fav icon is visible by default
                 // because android reuses views in listview
                 if (!file.keepInSync()) {
@@ -168,20 +370,42 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
                         checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);
                     }
                     checkBoxV.setVisibility(View.VISIBLE);
+                }               
+                
+                // get Thumbnail if file is image
+                if (file.isImage()){
+                     // Thumbnail in Cache?
+                    Bitmap thumbnail = getBitmapFromDiskCache(String.valueOf(file.getRemoteId()));
+                    if (thumbnail != null){
+                        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);
+                            fileIcon.setImageDrawable(asyncDrawable);
+                            task.execute(file);
+                        }
+                    }
+                } else {
+                    fileIcon.setImageResource(
+                            DisplayUtils.getResourceId(file.getMimetype(), file.getFileName())
+                    );
                 }
-
-                fileIcon.setImageResource(DisplayUtils.getResourceId(file.getMimetype(), file.getFileName()));
-
+
                 if (checkIfFileIsSharedWithMe(file)) {
                     sharedWithMeIconV.setVisibility(View.VISIBLE);
                 }
             } 
             else {
-                
                 fileSizeV.setVisibility(View.INVISIBLE);
                 //fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
                 lastModV.setVisibility(View.VISIBLE);
-                lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));
+                lastModV.setText(
+                        DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp())
+                );
                 checkBoxV.setVisibility(View.GONE);
                 view.findViewById(R.id.imageView3).setVisibility(View.GONE);
 
@@ -189,7 +413,9 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
                     fileIcon.setImageResource(R.drawable.shared_with_me_folder);
                     sharedWithMeIconV.setVisibility(View.VISIBLE);
                 } else {
-                    fileIcon.setImageResource(DisplayUtils.getResourceId(file.getMimetype(), file.getFileName()));
+                    fileIcon.setImageResource(
+                            DisplayUtils.getResourceId(file.getMimetype(), file.getFileName())
+                    );
                 }
 
                 // If folder is sharedByLink, icon folder must be changed to
@@ -208,6 +434,35 @@ 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() {
@@ -226,8 +481,10 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
 
     /**
      * Change the adapted directory for a new one
-     * @param directory                 New file to adapt. Can be NULL, meaning "no content to adapt".
-     * @param updatedStorageManager     Optional updated storage manager; used to replace mStorageManager if is different (and not NULL)
+     * @param directory                 New file to adapt. Can be NULL, meaning 
+     *                                  "no content to adapt".
+     * @param updatedStorageManager     Optional updated storage manager; used to replace 
+     *                                  mStorageManager if is different (and not NULL)
      */
     public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager) {
         mFile = directory;
@@ -273,7 +530,9 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
      * @return boolean: True if it is shared with me and false if it is not
      */
     private boolean checkIfFileIsSharedWithMe(OCFile file) {
-        return (mFile.getPermissions() != null && !mFile.getPermissions().contains(PERMISSION_SHARED_WITH_ME)
-                && file.getPermissions() != null && file.getPermissions().contains(PERMISSION_SHARED_WITH_ME));
+        return (mFile.getPermissions() != null 
+                && !mFile.getPermissions().contains(PERMISSION_SHARED_WITH_ME)
+                && file.getPermissions() != null 
+                && file.getPermissions().contains(PERMISSION_SHARED_WITH_ME));
     }
 }