From c30ab40fdd5efc013a237f2670400bd3e67ae367 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Sat, 9 Aug 2014 10:19:02 +0200 Subject: [PATCH] Remote fetch of thumbnail --- res/layout/image_fragment.xml | 49 ++++++ res/layout/image_item.xml | 119 ++++++++++++++ .../android/ui/adapter/DiskLruImageCache.java | 172 +++++++++++++++++++++ src/com/owncloud/android/ui/adapter/Utils.java | 35 +++++ third_party/disklrucache-2.0.2.jar | Bin 0 -> 21700 bytes 5 files changed, 375 insertions(+) create mode 100644 res/layout/image_fragment.xml create mode 100644 res/layout/image_item.xml create mode 100644 src/com/owncloud/android/ui/adapter/DiskLruImageCache.java create mode 100644 src/com/owncloud/android/ui/adapter/Utils.java create mode 100644 third_party/disklrucache-2.0.2.jar diff --git a/res/layout/image_fragment.xml b/res/layout/image_fragment.xml new file mode 100644 index 00000000..340ee03b --- /dev/null +++ b/res/layout/image_fragment.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + diff --git a/res/layout/image_item.xml b/res/layout/image_item.xml new file mode 100644 index 00000000..bd9f8d71 --- /dev/null +++ b/res/layout/image_item.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java b/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java new file mode 100644 index 00000000..5b401c28 --- /dev/null +++ b/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java @@ -0,0 +1,172 @@ +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.os.Environment; +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 = CompressFormat.JPEG; + private int mCompressQuality = 70; + private static final int APP_VERSION = 1; + private static final int VALUE_COUNT = 1; + private static final String TAG = "DiskLruImageCache"; + + public DiskLruImageCache( Context context,String uniqueName, int diskCacheSize, + CompressFormat compressFormat, int quality ) { + try { + final File diskCacheDir = getDiskCacheDir(context, uniqueName ); + mDiskCache = DiskLruCache.open( diskCacheDir, APP_VERSION, VALUE_COUNT, diskCacheSize ); + 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 ), Utils.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 = + Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || + !Utils.isExternalStorageRemovable() ? + Utils.getExternalCacheDir(context).getPath() : + context.getCacheDir().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, Utils.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(); + } + +} \ No newline at end of file diff --git a/src/com/owncloud/android/ui/adapter/Utils.java b/src/com/owncloud/android/ui/adapter/Utils.java new file mode 100644 index 00000000..181cc925 --- /dev/null +++ b/src/com/owncloud/android/ui/adapter/Utils.java @@ -0,0 +1,35 @@ +package com.owncloud.android.ui.adapter; + +import java.io.File; + +import android.content.Context; +import android.os.Build; +import android.os.Environment; + +public class Utils { + public static final int IO_BUFFER_SIZE = 8 * 1024; + + private Utils() {}; + + public static boolean isExternalStorageRemovable() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { + return Environment.isExternalStorageRemovable(); + } + return true; + } + + public static File getExternalCacheDir(Context context) { + if (hasExternalCacheDir()) { + return context.getExternalCacheDir(); + } + + // Before Froyo we need to construct the external cache dir ourselves + final String cacheDir = "/Android/data/" + context.getPackageName() + "/cache/"; + return new File(Environment.getExternalStorageDirectory().getPath() + cacheDir); + } + + public static boolean hasExternalCacheDir() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO; + } + +} diff --git a/third_party/disklrucache-2.0.2.jar b/third_party/disklrucache-2.0.2.jar new file mode 100644 index 0000000000000000000000000000000000000000..ca7907d0e220a8a508743570a075188aa4629eb6 GIT binary patch literal 21700 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 literal 0 HcmV?d00001 -- 2.11.0