Browse Source

Resized images

tobiasKaminsky 7 years ago
parent
commit
0a649f01bb
19 changed files with 687 additions and 157 deletions
  1. 6 0
      src/main/AndroidManifest.xml
  2. 319 98
      src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java
  3. 3 4
      src/main/java/com/owncloud/android/files/services/FileUploader.java
  4. 4 0
      src/main/java/com/owncloud/android/operations/RemoveFileOperation.java
  5. 1 1
      src/main/java/com/owncloud/android/operations/UploadFileOperation.java
  6. 143 0
      src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java
  7. 3 2
      src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java
  8. 5 2
      src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java
  9. 2 2
      src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java
  10. 1 1
      src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java
  11. 4 8
      src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java
  12. 13 6
      src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
  13. 16 0
      src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
  14. 4 0
      src/main/java/com/owncloud/android/ui/preview/ImageViewCustom.java
  15. 6 6
      src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java
  16. 115 18
      src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java
  17. 14 9
      src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java
  18. 24 0
      src/main/java/third_parties/michaelOrtiz/TouchImageViewCustom.java
  19. 4 0
      src/main/res/values/strings.xml

+ 6 - 0
src/main/AndroidManifest.xml

@@ -185,6 +185,12 @@
                 android:resource="@xml/exposed_filepaths" />
                 android:resource="@xml/exposed_filepaths" />
         </provider>
         </provider>
 
 
+        <provider
+            android:name=".providers.DiskLruImageCacheFileProvider"
+            android:authorities="org.nextcloud.imageCache.provider"
+            android:exported="true">
+        </provider>
+
         <activity
         <activity
             android:name=".authentication.AuthenticatorActivity"
             android:name=".authentication.AuthenticatorActivity"
             android:exported="true"
             android:exported="true"

+ 319 - 98
src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java

@@ -1,4 +1,4 @@
-/**
+/*
  *   ownCloud Android client application
  *   ownCloud Android client application
  *
  *
  *   @author Tobias Kaminsky
  *   @author Tobias Kaminsky
@@ -23,12 +23,14 @@ package com.owncloud.android.datamodel;
 
 
 import android.accounts.Account;
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.AccountManager;
+import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.CompressFormat;
 import android.graphics.Bitmap.CompressFormat;
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Paint;
+import android.graphics.Point;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Drawable;
 import android.media.MediaMetadataRetriever;
 import android.media.MediaMetadataRetriever;
@@ -36,7 +38,9 @@ import android.media.ThumbnailUtils;
 import android.net.Uri;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.AsyncTask;
 import android.text.TextUtils;
 import android.text.TextUtils;
+import android.view.Display;
 import android.view.MenuItem;
 import android.view.MenuItem;
+import android.view.WindowManager;
 import android.widget.ImageView;
 import android.widget.ImageView;
 
 
 import com.owncloud.android.MainApp;
 import com.owncloud.android.MainApp;
@@ -49,7 +53,9 @@ import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 import com.owncloud.android.ui.adapter.DiskLruImageCache;
 import com.owncloud.android.ui.adapter.DiskLruImageCache;
+import com.owncloud.android.ui.preview.PreviewImageFragment;
 import com.owncloud.android.utils.BitmapUtils;
 import com.owncloud.android.utils.BitmapUtils;
+import com.owncloud.android.utils.ConnectivityUtils;
 import com.owncloud.android.utils.DisplayUtils.AvatarGenerationListener;
 import com.owncloud.android.utils.DisplayUtils.AvatarGenerationListener;
 import com.owncloud.android.utils.FileStorageUtils;
 import com.owncloud.android.utils.FileStorageUtils;
 import com.owncloud.android.utils.MimeTypeUtil;
 import com.owncloud.android.utils.MimeTypeUtil;
@@ -58,6 +64,7 @@ import org.apache.commons.httpclient.HttpStatus;
 import org.apache.commons.httpclient.methods.GetMethod;
 import org.apache.commons.httpclient.methods.GetMethod;
 
 
 import java.io.File;
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -68,7 +75,10 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
  * Manager for concurrent access to thumbnails cache.
  * Manager for concurrent access to thumbnails cache.
  */
  */
 public class ThumbnailsCacheManager {
 public class ThumbnailsCacheManager {
-    
+
+    public static final String PREFIX_RESIZED_IMAGE = "r";
+    public static final String PREFIX_THUMBNAIL = "t";
+
     private static final String TAG = ThumbnailsCacheManager.class.getSimpleName();
     private static final String TAG = ThumbnailsCacheManager.class.getSimpleName();
     
     
     private static final String CACHE_FOLDER = "thumbnailCache";
     private static final String CACHE_FOLDER = "thumbnailCache";
@@ -77,7 +87,7 @@ public class ThumbnailsCacheManager {
     private static DiskLruImageCache mThumbnailCache = null;
     private static DiskLruImageCache mThumbnailCache = null;
     private static boolean mThumbnailCacheStarting = true;
     private static boolean mThumbnailCacheStarting = true;
     
     
-    private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
+    private static final int DISK_CACHE_SIZE = 1024 * 1024 * 200; // 200MB
     private static final CompressFormat mCompressFormat = CompressFormat.JPEG;
     private static final CompressFormat mCompressFormat = CompressFormat.JPEG;
     private static final int mCompressQuality = 70;
     private static final int mCompressQuality = 70;
     private static OwnCloudClient mClient = null;
     private static OwnCloudClient mClient = null;
@@ -106,19 +116,19 @@ public class ThumbnailsCacheManager {
                     try {
                     try {
                         // Check if media is mounted or storage is built-in, if so, 
                         // Check if media is mounted or storage is built-in, if so, 
                         // try and use external cache dir; otherwise use internal cache dir
                         // 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);
+                        File cacheDir = MainApp.getAppContext().getExternalCacheDir();
+
+                        if (cacheDir != null) {
+                            String cachePath = cacheDir.getPath() + File.separator + CACHE_FOLDER;
+                            Log_OC.d(TAG, "create dir: " + cachePath);
+                            File diskCacheDir = new File(cachePath);
+                            mThumbnailCache = new DiskLruImageCache(diskCacheDir, DISK_CACHE_SIZE, mCompressFormat,
+                                    mCompressQuality);
+                        } else {
+                            throw new FileNotFoundException("Thumbnail cache could not be opened");
+                        }
+                    } catch (java.io.IOException e) {
+                        Log_OC.d(TAG, e.getMessage());
                         mThumbnailCache = null;
                         mThumbnailCache = null;
                     }
                     }
                 }
                 }
@@ -139,17 +149,32 @@ public class ThumbnailsCacheManager {
         return Math.round(r.getDimension(R.dimen.file_icon_size_grid));
         return Math.round(r.getDimension(R.dimen.file_icon_size_grid));
     }
     }
 
 
+    /**
+     * Converts dimension of screen as point
+     *
+     * @return Point
+     */
+    private static Point getScreenDimension() {
+        WindowManager wm = (WindowManager) MainApp.getAppContext().
+                getSystemService(Context.WINDOW_SERVICE);
+        Display display = wm.getDefaultDisplay();
+        Point point = new Point();
+        display.getSize(point);
+        return point;
+    }
+
     /**
     /**
      * Add thumbnail to cache
      * Add thumbnail to cache
      * @param imageKey: thumb key
      * @param imageKey: thumb key
      * @param bitmap:   image for extracting thumbnail
      * @param bitmap:   image for extracting thumbnail
      * @param path:     image path
      * @param path:     image path
-     * @param px:       thumbnail dp
+     * @param pxW:      thumbnail width in pixel
+     * @param pxH:      thumbnail height in pixel
      * @return Bitmap
      * @return Bitmap
      */
      */
-    private static Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int px){
+    private static Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int pxW, int pxH){
 
 
-        Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);
+        Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH);
 
 
         // Rotate image, obeying exif tag
         // Rotate image, obeying exif tag
         thumbnail = BitmapUtils.rotateImage(thumbnail,path);
         thumbnail = BitmapUtils.rotateImage(thumbnail,path);
@@ -185,7 +210,180 @@ public class ThumbnailsCacheManager {
         return null;
         return null;
     }
     }
 
 
-    public static class ThumbnailGenerationTask extends AsyncTask<Object, Void, Bitmap> {
+    public static class ResizedImageGenerationTask extends AsyncTask<Object, Void, Bitmap> {
+        private PreviewImageFragment previewImageFragment;
+        private FileDataStorageManager storageManager;
+        private Account account;
+        private WeakReference<ImageView> imageViewReference;
+        private OCFile file;
+
+
+        public ResizedImageGenerationTask(PreviewImageFragment previewImageFragment, ImageView imageView,
+                                          FileDataStorageManager storageManager, Account account)
+                throws IllegalArgumentException {
+            this.previewImageFragment = previewImageFragment;
+            imageViewReference = new WeakReference<>(imageView);
+            this.storageManager = storageManager;
+            this.account = account;
+        }
+
+        @Override
+        protected Bitmap doInBackground(Object... params) {
+            Bitmap thumbnail = null;
+
+            file = (OCFile) params[0];
+
+            try {
+                if (account != null) {
+                    OwnCloudAccount ocAccount = new OwnCloudAccount(account, MainApp.getAppContext());
+                    mClient = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount,
+                            MainApp.getAppContext());
+                }
+
+                thumbnail = doResizedImageInBackground();
+
+                if (MimeTypeUtil.isVideo(file) && thumbnail != null) {
+                    thumbnail = addVideoOverlay(thumbnail);
+                }
+
+            } catch (OutOfMemoryError oome) {
+                System.gc();
+            } 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);
+            }
+
+            return thumbnail;
+        }
+
+        private Bitmap doResizedImageInBackground() {
+            Bitmap thumbnail;
+
+            String imageKey = PREFIX_RESIZED_IMAGE + String.valueOf(file.getRemoteId());
+
+            // Check disk cache in background thread
+            thumbnail = getBitmapFromDiskCache(imageKey);
+
+            // Not found in disk cache
+            if (thumbnail == null || file.needsUpdateThumbnail()) {
+                Point p = getScreenDimension();
+                int pxW = p.x;
+                int pxH = p.y;
+
+                if (file.isDown()) {
+                    Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(
+                            file.getStoragePath(), pxW, pxH);
+
+                    if (bitmap != null) {
+                        // Handle PNG
+                        if (file.getMimetype().equalsIgnoreCase("image/png")) {
+                            bitmap = handlePNG(bitmap, pxW);
+                        }
+
+                        thumbnail = addThumbnailToCache(imageKey, bitmap, file.getStoragePath(), pxW, pxH);
+
+                        file.setNeedsUpdateThumbnail(false);
+                        storageManager.saveFile(file);
+                    }
+
+                } else {
+                    // Download thumbnail from server
+                    OwnCloudVersion serverOCVersion = AccountUtils.getServerVersion(account);
+                    if (mClient != null && serverOCVersion != null) {
+                        if (serverOCVersion.supportsRemoteThumbnails()) {
+                            GetMethod getMethod = null;
+                            try {
+                                // resized image via gallery app
+                                String uri = mClient.getBaseUri() + "" +
+                                        "/index.php/apps/gallery/api/preview/" +
+                                        Integer.parseInt(file.getRemoteId().substring(0, 8)) +
+                                        "/" + pxW + "/" + pxH;
+                                Log_OC.d(TAG, "generate resizedImage: " + file.getFileName() +
+                                        " URI: " + uri);
+                                getMethod = new GetMethod(uri);
+                                getMethod.setRequestHeader("Cookie",
+                                        "nc_sameSiteCookielax=true;nc_sameSiteCookiestrict=true");
+                                int status = mClient.executeMethod(getMethod);
+                                if (status == HttpStatus.SC_OK) {
+                                    InputStream inputStream = getMethod.getResponseBodyAsStream();
+                                    thumbnail = BitmapFactory.decodeStream(inputStream);
+                                } else {
+                                    mClient.exhaustResponse(getMethod.getResponseBodyAsStream());
+                                }
+
+                                // Handle PNG
+                                if (file.getMimetype().equalsIgnoreCase("image/png")) {
+                                    thumbnail = handlePNG(thumbnail, pxW);
+                                }
+
+                                // Add thumbnail to cache
+                                if (thumbnail != null) {
+                                    Log_OC.d(TAG, "add thumbnail to cache: " + file.getFileName());
+                                    addBitmapToCache(imageKey, thumbnail);
+                                }
+
+                            } catch (Exception e) {
+                                Log_OC.d(TAG, e.getMessage(), e);
+                            } finally {
+                                if (getMethod != null) {
+                                    getMethod.releaseConnection();
+                                }
+                            }
+                        } else {
+                            Log_OC.d(TAG, "Server too old");
+                        }
+                    }
+                }
+            }
+
+            return thumbnail;
+
+        }
+
+        protected void onPostExecute(Bitmap bitmap) {
+            if (imageViewReference != null) {
+                final ImageView imageView = imageViewReference.get();
+
+                if (bitmap != null) {
+                    final ResizedImageGenerationTask bitmapWorkerTask = getResizedImageGenerationWorkerTask(imageView);
+
+                    if (this == bitmapWorkerTask) {
+                        String tagId = String.valueOf(file.getFileId());
+
+                        if (String.valueOf(imageView.getTag()).equals(tagId)) {
+                            imageView.setImageBitmap(bitmap);
+                        }
+                    }
+                } else {
+                    if (ConnectivityUtils.isAppConnected(MainApp.getAppContext())) {
+                        previewImageFragment.setErrorPreviewMessage();
+                    } else {
+                        previewImageFragment.setNoConnectionErrorMessage();
+                    }
+                }
+            }
+        }
+    }
+
+    public static class ThumbnailGenerationTaskObject {
+        private Object file;
+        private String imageKey;
+
+        public ThumbnailGenerationTaskObject(Object file, String imageKey) {
+            this.file = file;
+            this.imageKey = imageKey;
+        }
+
+        private Object getFile() {
+            return file;
+        }
+
+        private String getImageKey() {
+            return imageKey;
+        }
+    }
+
+    public static class ThumbnailGenerationTask extends AsyncTask<ThumbnailGenerationTaskObject, Void, Bitmap> {
         private final WeakReference<ImageView> mImageViewReference;
         private final WeakReference<ImageView> mImageViewReference;
         private static Account mAccount;
         private static Account mAccount;
         private ArrayList<ThumbnailGenerationTask> mAsyncTasks = null;
         private ArrayList<ThumbnailGenerationTask> mAsyncTasks = null;
@@ -232,7 +430,7 @@ public class ThumbnailsCacheManager {
 
 
         @SuppressFBWarnings("Dm")
         @SuppressFBWarnings("Dm")
         @Override
         @Override
-        protected Bitmap doInBackground(Object... params) {
+        protected Bitmap doInBackground(ThumbnailGenerationTaskObject... params) {
             Bitmap thumbnail = null;
             Bitmap thumbnail = null;
 
 
             try {
             try {
@@ -245,13 +443,12 @@ public class ThumbnailsCacheManager {
                             getClientFor(ocAccount, MainApp.getAppContext());
                             getClientFor(ocAccount, MainApp.getAppContext());
                 }
                 }
 
 
-                mFile = params[0];
-                if (params.length == 2) {
-                    mImageKey = (String) params[1];
-                }
+                ThumbnailGenerationTaskObject object = params[0];
+                mFile = object.getFile();
+                mImageKey = object.getImageKey();
 
 
                 if (mFile instanceof OCFile) {
                 if (mFile instanceof OCFile) {
-                    thumbnail = doOCFileInBackground();
+                    thumbnail = doThumbnailFromOCFileInBackground();
 
 
                     if (MimeTypeUtil.isVideo((OCFile) mFile) && thumbnail != null) {
                     if (MimeTypeUtil.isVideo((OCFile) mFile) && thumbnail != null) {
                         thumbnail = addVideoOverlay(thumbnail);
                         thumbnail = addVideoOverlay(thumbnail);
@@ -300,42 +497,31 @@ public class ThumbnailsCacheManager {
             }
             }
         }
         }
 
 
-        /**
-         * Converts size of file icon from dp to pixel
-         * @return int
-         */
-        private int getThumbnailDimension(){
-            // Converts dp to pixel
-            Resources r = MainApp.getAppContext().getResources();
-            Double d = Math.pow(2,Math.floor(Math.log(r.getDimension(R.dimen.file_icon_size_grid))/Math.log(2)));
-            return d.intValue();
-        }
-
-        private Bitmap doOCFileInBackground() {
-            OCFile file = (OCFile)mFile;
-
-            final String imageKey = String.valueOf(file.getRemoteId());
+        private Bitmap doThumbnailFromOCFileInBackground() {
+            Bitmap thumbnail;
+            OCFile file = (OCFile) mFile;
+            String imageKey = PREFIX_THUMBNAIL + String.valueOf(file.getRemoteId());
 
 
             // Check disk cache in background thread
             // Check disk cache in background thread
-            Bitmap thumbnail = getBitmapFromDiskCache(imageKey);
+            thumbnail = getBitmapFromDiskCache(imageKey);
 
 
             // Not found in disk cache
             // Not found in disk cache
             if (thumbnail == null || file.needsUpdateThumbnail()) {
             if (thumbnail == null || file.needsUpdateThumbnail()) {
-
-                int px = getThumbnailDimension();
+                int pxW;
+                int pxH;
+                pxW = pxH = getThumbnailDimension();
 
 
                 if (file.isDown()) {
                 if (file.isDown()) {
-                    Bitmap temp = BitmapUtils.decodeSampledBitmapFromFile(
-                            file.getStoragePath(), px, px);
-                    Bitmap bitmap = ThumbnailUtils.extractThumbnail(temp, px, px);
+                    Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(
+                            file.getStoragePath(), pxW, pxH);
 
 
                     if (bitmap != null) {
                     if (bitmap != null) {
                         // Handle PNG
                         // Handle PNG
                         if (file.getMimetype().equalsIgnoreCase("image/png")) {
                         if (file.getMimetype().equalsIgnoreCase("image/png")) {
-                            bitmap = handlePNG(bitmap, px);
+                            bitmap = handlePNG(bitmap, pxW);
                         }
                         }
 
 
-                        thumbnail = addThumbnailToCache(imageKey, bitmap, file.getStoragePath(), px);
+                        thumbnail = addThumbnailToCache(imageKey, bitmap, file.getStoragePath(), pxW, pxH);
 
 
                         file.setNeedsUpdateThumbnail(false);
                         file.setNeedsUpdateThumbnail(false);
                         mStorageManager.saveFile(file);
                         mStorageManager.saveFile(file);
@@ -348,10 +534,12 @@ public class ThumbnailsCacheManager {
                         if (serverOCVersion.supportsRemoteThumbnails()) {
                         if (serverOCVersion.supportsRemoteThumbnails()) {
                             getMethod = null;
                             getMethod = null;
                             try {
                             try {
+                                // thumbnail
                                 String uri = mClient.getBaseUri() + "" +
                                 String uri = mClient.getBaseUri() + "" +
                                         "/index.php/apps/files/api/v1/thumbnail/" +
                                         "/index.php/apps/files/api/v1/thumbnail/" +
-                                        px + "/" + px + Uri.encode(file.getRemotePath(), "/");
-                                Log_OC.d("Thumbnail", "URI: " + uri);
+                                        pxW + "/" + pxH + Uri.encode(file.getRemotePath(), "/");
+                                Log_OC.d(TAG, "generate thumbnail: " + file.getFileName() +
+                                        " URI: " + uri);
                                 getMethod = new GetMethod(uri);
                                 getMethod = new GetMethod(uri);
                                 getMethod.setRequestHeader("Cookie",
                                 getMethod.setRequestHeader("Cookie",
                                         "nc_sameSiteCookielax=true;nc_sameSiteCookiestrict=true");
                                         "nc_sameSiteCookielax=true;nc_sameSiteCookiestrict=true");
@@ -363,20 +551,22 @@ public class ThumbnailsCacheManager {
                                 if (status == HttpStatus.SC_OK) {
                                 if (status == HttpStatus.SC_OK) {
                                     InputStream inputStream = getMethod.getResponseBodyAsStream();
                                     InputStream inputStream = getMethod.getResponseBodyAsStream();
                                     Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                                     Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
-                                    thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);
-
-                                    // Handle PNG
-                                    if (file.getMimetype().equalsIgnoreCase("image/png")) {
-                                        thumbnail = handlePNG(thumbnail, px);
-                                    }
-
-                                    // Add thumbnail to cache
-                                    if (thumbnail != null) {
-                                        addBitmapToCache(imageKey, thumbnail);
-                                    }
+                                    thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH);
                                 } else {
                                 } else {
                                     mClient.exhaustResponse(getMethod.getResponseBodyAsStream());
                                     mClient.exhaustResponse(getMethod.getResponseBodyAsStream());
                                 }
                                 }
+
+                                // Handle PNG
+                                if (file.getMimetype().equalsIgnoreCase("image/png")) {
+                                    thumbnail = handlePNG(thumbnail, pxW);
+                                }
+
+                                // Add thumbnail to cache
+                                if (thumbnail != null) {
+                                    Log_OC.d(TAG, "add thumbnail to cache: " + file.getFileName());
+                                    addBitmapToCache(imageKey, thumbnail);
+                                }
+
                             } catch (Exception e) {
                             } catch (Exception e) {
                                 Log_OC.d(TAG, e.getMessage(), e);
                                 Log_OC.d(TAG, e.getMessage(), e);
                             } finally {
                             } finally {
@@ -395,6 +585,18 @@ public class ThumbnailsCacheManager {
 
 
         }
         }
 
 
+        /**
+         * Converts size of file icon from dp to pixel
+         *
+         * @return int
+         */
+        private int getThumbnailDimension() {
+            // Converts dp to pixel
+            Resources r = MainApp.getAppContext().getResources();
+            Double d = Math.pow(2, Math.floor(Math.log(r.getDimension(R.dimen.file_icon_size_grid)) / Math.log(2)));
+            return d.intValue();
+        }
+
         private Bitmap doFileInBackground() {
         private Bitmap doFileInBackground() {
             File file = (File)mFile;
             File file = (File)mFile;
 
 
@@ -405,19 +607,22 @@ public class ThumbnailsCacheManager {
                 imageKey = String.valueOf(file.hashCode());
                 imageKey = String.valueOf(file.hashCode());
             }
             }
 
 
+            // local file should always generate a thumbnail
+            mImageKey = PREFIX_THUMBNAIL + mImageKey;
+
             // Check disk cache in background thread
             // Check disk cache in background thread
             Bitmap thumbnail = getBitmapFromDiskCache(imageKey);
             Bitmap thumbnail = getBitmapFromDiskCache(imageKey);
 
 
             // Not found in disk cache
             // Not found in disk cache
             if (thumbnail == null) {
             if (thumbnail == null) {
+                int pxW;
+                int pxH;
+                pxW = pxH = getThumbnailDimension();
 
 
-                int px = getThumbnailDimension();
-
-                Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(
-                        file.getAbsolutePath(), px, px);
+                Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getAbsolutePath(), pxW, pxH);
 
 
                 if (bitmap != null) {
                 if (bitmap != null) {
-                    thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px);
+                    thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), pxW, pxH);
                 }
                 }
             }
             }
             return thumbnail;
             return thumbnail;
@@ -514,7 +719,7 @@ public class ThumbnailsCacheManager {
                     Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getAbsolutePath(), px, px);
                     Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getAbsolutePath(), px, px);
 
 
                     if (bitmap != null) {
                     if (bitmap != null) {
-                        thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px);
+                        thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px, px);
                     }
                     }
                 } else if (Type.VIDEO.equals(type)) {
                 } else if (Type.VIDEO.equals(type)) {
                     MediaMetadataRetriever retriever = new MediaMetadataRetriever();
                     MediaMetadataRetriever retriever = new MediaMetadataRetriever();
@@ -541,7 +746,7 @@ public class ThumbnailsCacheManager {
                         int max = Math.max(width, height);
                         int max = Math.max(width, height);
                         if (max > px) {
                         if (max > px) {
                             thumbnail = BitmapUtils.scaleBitmap(thumbnail, px, width, height, max);
                             thumbnail = BitmapUtils.scaleBitmap(thumbnail, px, width, height, max);
-                            thumbnail = addThumbnailToCache(imageKey, thumbnail, file.getPath(), px);
+                            thumbnail = addThumbnailToCache(imageKey, thumbnail, file.getPath(), px, px);
                         }
                         }
                     }
                     }
                 }
                 }
@@ -605,27 +810,6 @@ public class ThumbnailsCacheManager {
             }
             }
         }
         }
 
 
-        /**
-         * Add thumbnail to cache
-         * @param imageKey: thumb key
-         * @param bitmap:   image for extracting thumbnail
-         * @param path:     image path
-         * @param px:       thumbnail dp
-         * @return Bitmap
-         */
-        private Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int px){
-
-            Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);
-
-            // Rotate image, obeying exif tag
-            thumbnail = BitmapUtils.rotateImage(thumbnail,path);
-
-            // Add thumbnail to cache
-            addBitmapToCache(imageKey, thumbnail);
-
-            return thumbnail;
-        }
-
         /**
         /**
          * Converts size of file icon from dp to pixel
          * Converts size of file icon from dp to pixel
          * @return int
          * @return int
@@ -774,6 +958,17 @@ public class ThumbnailsCacheManager {
         return null;
         return null;
     }
     }
 
 
+    private static ResizedImageGenerationTask getResizedImageGenerationWorkerTask(ImageView imageView) {
+        if (imageView != null) {
+            final Drawable drawable = imageView.getDrawable();
+            if (drawable instanceof AsyncResizedImageDrawable) {
+                final AsyncResizedImageDrawable asyncDrawable = (AsyncResizedImageDrawable) drawable;
+                return asyncDrawable.getBitmapWorkerTask();
+            }
+        }
+        return null;
+    }
+
     public static Bitmap addVideoOverlay(Bitmap thumbnail){
     public static Bitmap addVideoOverlay(Bitmap thumbnail){
         Bitmap playButton = BitmapFactory.decodeResource(MainApp.getAppContext().getResources(),
         Bitmap playButton = BitmapFactory.decodeResource(MainApp.getAppContext().getResources(),
                 R.drawable.view_play);
                 R.drawable.view_play);
@@ -852,22 +1047,30 @@ public class ThumbnailsCacheManager {
         }
         }
     }
     }
 
 
-    public static class AsyncMediaThumbnailDrawable extends BitmapDrawable {
-        private final WeakReference<MediaThumbnailGenerationTask> bitmapWorkerTaskReference;
-
-        public AsyncMediaThumbnailDrawable(
-                Resources res, Bitmap bitmap, MediaThumbnailGenerationTask bitmapWorkerTask
-        ) {
+    public static class AsyncResizedImageDrawable extends BitmapDrawable {
+        private final WeakReference<ResizedImageGenerationTask> bitmapWorkerTaskReference;
 
 
+        public AsyncResizedImageDrawable(Resources res, Bitmap bitmap, ResizedImageGenerationTask bitmapWorkerTask) {
             super(res, bitmap);
             super(res, bitmap);
             bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
             bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
         }
         }
 
 
-        public MediaThumbnailGenerationTask getBitmapWorkerTask() {
+        private ResizedImageGenerationTask getBitmapWorkerTask() {
             return bitmapWorkerTaskReference.get();
             return bitmapWorkerTaskReference.get();
         }
         }
     }
     }
 
 
+    public static class AsyncMediaThumbnailDrawable extends BitmapDrawable {
+        private final WeakReference<MediaThumbnailGenerationTask> bitmapWorkerTaskReference;
+
+        public AsyncMediaThumbnailDrawable(Resources res, Bitmap bitmap,
+                                           MediaThumbnailGenerationTask bitmapWorkerTask) {
+
+            super(res, bitmap);
+            bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
+        }
+    }
+
     public static class AsyncAvatarDrawable extends BitmapDrawable {
     public static class AsyncAvatarDrawable extends BitmapDrawable {
         private final WeakReference<AvatarGenerationTask> avatarWorkerTaskReference;
         private final WeakReference<AvatarGenerationTask> avatarWorkerTaskReference;
 
 
@@ -897,4 +1100,22 @@ public class ThumbnailsCacheManager {
 
 
         return resultBitmap;
         return resultBitmap;
     }
     }
-}
+
+    public static void generateResizedImage(OCFile file) {
+        Point p = getScreenDimension();
+        int pxW = p.x;
+        int pxH = p.y;
+        String imageKey = PREFIX_RESIZED_IMAGE + String.valueOf(file.getRemoteId());
+
+        Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getStoragePath(), pxW, pxH);
+
+        if (bitmap != null) {
+            // Handle PNG
+            if (file.getMimetype().equalsIgnoreCase("image/png")) {
+                bitmap = handlePNG(bitmap, pxW);
+            }
+
+            addThumbnailToCache(imageKey, bitmap, file.getStoragePath(), pxW, pxH);
+        }
+    }
+}

+ 3 - 4
src/main/java/com/owncloud/android/files/services/FileUploader.java

@@ -1071,11 +1071,10 @@ public class FileUploader extends Service
             final ThumbnailsCacheManager.ThumbnailGenerationTask task =
             final ThumbnailsCacheManager.ThumbnailGenerationTask task =
                     new ThumbnailsCacheManager.ThumbnailGenerationTask(mStorageManager, mCurrentAccount);
                     new ThumbnailsCacheManager.ThumbnailGenerationTask(mStorageManager, mCurrentAccount);
 
 
-            Object[] params = new Object[2];
-            params[0] = new File(mCurrentUpload.getOriginalStoragePath());
-            params[1] = mCurrentUpload.getFile().getRemoteId();
+            File file = new File(mCurrentUpload.getOriginalStoragePath());
+            String remoteId = mCurrentUpload.getFile().getRemoteId();
 
 
-            task.execute(params);
+            task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, remoteId));
         }
         }
     }
     }
 
 

+ 4 - 0
src/main/java/com/owncloud/android/operations/RemoveFileOperation.java

@@ -22,6 +22,7 @@
 package com.owncloud.android.operations;
 package com.owncloud.android.operations;
 
 
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.ThumbnailsCacheManager;
 import com.owncloud.android.lib.common.OwnCloudClient;
 import com.owncloud.android.lib.common.OwnCloudClient;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
@@ -75,6 +76,9 @@ public class RemoveFileOperation extends SyncOperation {
         
         
         mFileToRemove = getStorageManager().getFileByPath(mRemotePath);
         mFileToRemove = getStorageManager().getFileByPath(mRemotePath);
 
 
+        // store resized image
+        ThumbnailsCacheManager.generateResizedImage(mFileToRemove);
+
         boolean localRemovalFailed = false;
         boolean localRemovalFailed = false;
         if (!mOnlyLocalCopy) {
         if (!mOnlyLocalCopy) {
             RemoveRemoteFileOperation operation = new RemoveRemoteFileOperation(mRemotePath);
             RemoveRemoteFileOperation operation = new RemoveRemoteFileOperation(mRemotePath);

+ 1 - 1
src/main/java/com/owncloud/android/operations/UploadFileOperation.java

@@ -918,7 +918,7 @@ public class UploadFileOperation extends SyncOperation {
         // generate new Thumbnail
         // generate new Thumbnail
         final ThumbnailsCacheManager.ThumbnailGenerationTask task =
         final ThumbnailsCacheManager.ThumbnailGenerationTask task =
                 new ThumbnailsCacheManager.ThumbnailGenerationTask(getStorageManager(), mAccount);
                 new ThumbnailsCacheManager.ThumbnailGenerationTask(getStorageManager(), mAccount);
-        task.execute(file);
+        task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, file.getRemoteId()));
     }
     }
 
 
     private void updateOCFile(OCFile file, RemoteFile remoteFile) {
     private void updateOCFile(OCFile file, RemoteFile remoteFile) {

+ 143 - 0
src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java

@@ -0,0 +1,143 @@
+/*
+ *   ownCloud Android client application
+ *
+ *   Copyright (C) 2016 Tobias Kaminsky
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ *   License as published by the Free Software Foundation; either
+ *   version 3 of the License, or any later version.
+ *
+ *   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 AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ *   You should have received a copy of the GNU Affero General Public
+ *   License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.providers;
+
+import android.accounts.Account;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.OpenableColumns;
+
+import com.owncloud.android.MainApp;
+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.lib.common.utils.Log_OC;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+
+public class DiskLruImageCacheFileProvider extends ContentProvider {
+    public static final String AUTHORITY = "org.nextcloud.imageCache.provider";
+    public static final String TAG = DiskLruImageCacheFileProvider.class.getSimpleName();
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    private OCFile getFile(Uri uri) {
+        Account account = AccountUtils.getCurrentOwnCloudAccount(MainApp.getAppContext());
+        FileDataStorageManager fileDataStorageManager = new FileDataStorageManager(account,
+                MainApp.getAppContext().getContentResolver());
+
+        return fileDataStorageManager.getFileByPath(uri.getPath());
+    }
+
+    @Override
+    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+        OCFile ocFile = getFile(uri);
+
+        Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
+                String.valueOf(ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + ocFile.getRemoteId()));
+
+        // fallback to thumbnail
+        if (thumbnail == null) {
+            thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
+                    String.valueOf(ThumbnailsCacheManager.PREFIX_THUMBNAIL + ocFile.getRemoteId()));
+        }
+
+        // fallback to default image
+        if (thumbnail == null) {
+            thumbnail = ThumbnailsCacheManager.mDefaultImg;
+        }
+
+        // create a file to write bitmap data
+        File f = new File(MainApp.getAppContext().getCacheDir(), ocFile.getFileName());
+        try {
+            f.createNewFile();
+
+            //Convert bitmap to byte array
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            thumbnail.compress(Bitmap.CompressFormat.JPEG, 90, bos);
+            byte[] bitmapData = bos.toByteArray();
+
+            //write the bytes in file
+            FileOutputStream fos = null;
+            try {
+                fos = new FileOutputStream(f);
+            } catch (FileNotFoundException e) {
+                e.printStackTrace();
+            }
+            fos.write(bitmapData);
+            fos.flush();
+            fos.close();
+
+        } catch (Exception e) {
+            Log_OC.e(TAG, "Error opening file: " + e.getMessage());
+        }
+
+        return ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        OCFile ocFile = getFile(uri);
+        return ocFile.getMimetype();
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] arg1, String arg2, String[] arg3, String arg4) {
+        MatrixCursor cursor = null;
+
+        OCFile ocFile = getFile(uri);
+        File file = new File(MainApp.getAppContext().getCacheDir(), ocFile.getFileName());
+        if (file.exists()) {
+            cursor = new MatrixCursor(new String[] {
+                    OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE });
+            cursor.addRow(new Object[] { uri.getLastPathSegment(),
+                    file.length() });
+        }
+
+        return cursor;
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        return 0;
+    }
+}

+ 3 - 2
src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java

@@ -449,7 +449,8 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
                                         task
                                         task
                                 );
                                 );
                         fileIcon.setImageDrawable(asyncDrawable);
                         fileIcon.setImageDrawable(asyncDrawable);
-                        task.execute(fakeFileToCheatThumbnailsCacheManagerInterface);
+                        task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(
+                                fakeFileToCheatThumbnailsCacheManagerInterface, null));
                     }
                     }
                 }
                 }
 
 
@@ -485,7 +486,7 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
                                         task
                                         task
                                 );
                                 );
                         fileIcon.setImageDrawable(asyncDrawable);
                         fileIcon.setImageDrawable(asyncDrawable);
-                        task.execute(file);
+                        task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, null));
                         Log_OC.v(TAG, "Executing task to generate a new thumbnail");
                         Log_OC.v(TAG, "Executing task to generate a new thumbnail");
                     }
                     }
                 }
                 }

+ 5 - 2
src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java

@@ -343,7 +343,9 @@ public class FileListListAdapter extends BaseAdapter {
             if (!file.isFolder()) {
             if (!file.isFolder()) {
                 if ((MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file)) && file.getRemoteId() != null) {
                 if ((MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file)) && file.getRemoteId() != null) {
                     // Thumbnail in Cache?
                     // Thumbnail in Cache?
-                    Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(file.getRemoteId());
+                    Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
+                            ThumbnailsCacheManager.PREFIX_THUMBNAIL + String.valueOf(file.getRemoteId())
+                    );
                     if (thumbnail != null && !file.needsUpdateThumbnail()) {
                     if (thumbnail != null && !file.needsUpdateThumbnail()) {
 
 
                         if (MimeTypeUtil.isVideo(file)) {
                         if (MimeTypeUtil.isVideo(file)) {
@@ -375,7 +377,8 @@ public class FileListListAdapter extends BaseAdapter {
                                         );
                                         );
                                 fileIcon.setImageDrawable(asyncDrawable);
                                 fileIcon.setImageDrawable(asyncDrawable);
                                 asyncTasks.add(task);
                                 asyncTasks.add(task);
-                                task.execute(file);
+                                task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file,
+                                        file.getRemoteId()));
                             } catch (IllegalArgumentException e) {
                             } catch (IllegalArgumentException e) {
                                 Log_OC.d(TAG, "ThumbnailGenerationTask : " + e.getMessage());
                                 Log_OC.d(TAG, "ThumbnailGenerationTask : " + e.getMessage());
                             }
                             }

+ 2 - 2
src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java

@@ -204,7 +204,7 @@ public class LocalFileListAdapter extends BaseAdapter implements FilterableListA
                 if (MimeTypeUtil.isImage(file)){
                 if (MimeTypeUtil.isImage(file)){
                 // Thumbnail in Cache?
                 // Thumbnail in Cache?
                     Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
                     Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
-                            String.valueOf(file.hashCode())
+                            ThumbnailsCacheManager.PREFIX_THUMBNAIL + String.valueOf(file.hashCode())
                     );
                     );
                     if (thumbnail != null){
                     if (thumbnail != null){
                         fileIcon.setImageBitmap(thumbnail);
                         fileIcon.setImageBitmap(thumbnail);
@@ -226,7 +226,7 @@ public class LocalFileListAdapter extends BaseAdapter implements FilterableListA
                                             task
                                             task
                                     );
                                     );
                             fileIcon.setImageDrawable(asyncDrawable);
                             fileIcon.setImageDrawable(asyncDrawable);
-                            task.execute(file);
+                            task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, null));
                             Log_OC.v(TAG, "Executing task to generate a new thumbnail");
                             Log_OC.v(TAG, "Executing task to generate a new thumbnail");
 
 
                         } // else, already being generated, don't restart it
                         } // else, already being generated, don't restart it

+ 1 - 1
src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java

@@ -122,7 +122,7 @@ public class UploaderAdapter extends SimpleAdapter {
                                 task
                                 task
                         );
                         );
                         fileIcon.setImageDrawable(asyncDrawable);
                         fileIcon.setImageDrawable(asyncDrawable);
-                        task.execute(file);
+                        task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, file.getRemoteId()));
                     }
                     }
                 }
                 }
             } else {
             } else {

+ 4 - 8
src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java

@@ -339,14 +339,10 @@ public class FileDetailFragment extends FileFragment implements OnClickListener,
                 return true;
                 return true;
             }
             }
             case R.id.action_send_file: {
             case R.id.action_send_file: {
-                // Obtain the file
-                if (!getFile().isDown()) {  // Download the file                    
-                    Log_OC.d(TAG, getFile().getRemotePath() + " : File must be downloaded");
-                    ((FileDisplayActivity) mContainerActivity).startDownloadForSending(getFile(),
-                            OCFileListFragment.DOWNLOAD_SEND);
-                }
-                else {
+                if (getFile().isDown()) {
                     mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile());
                     mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile());
+                } else {
+                    mContainerActivity.getFileOperationsHelper().sendCachedImage(getFile());
                 }
                 }
                 return true;
                 return true;
             }
             }
@@ -508,7 +504,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener,
                                         task
                                         task
                                 );
                                 );
                         iv.setImageDrawable(asyncDrawable);
                         iv.setImageDrawable(asyncDrawable);
-                        task.execute(file);
+                        task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, file.getRemoteId()));
                     }
                     }
                 }
                 }
             } else {
             } else {

+ 13 - 6
src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -970,14 +970,21 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
                     return true;
                     return true;
                 }
                 }
                 case R.id.action_send_file: {
                 case R.id.action_send_file: {
-                    // Obtain the file
-                    if (!singleFile.isDown()) {  // Download the file
-                        Log_OC.d(TAG, singleFile.getRemotePath() + " : File must be downloaded");
-                        ((FileDisplayActivity) mContainerActivity).startDownloadForSending(singleFile, DOWNLOAD_SEND);
+                    if (MimeTypeUtil.isImage(singleFile) && !singleFile.isDown()) {
+                        mContainerActivity.getFileOperationsHelper().sendCachedImage(singleFile);
+                        return true;
                     } else {
                     } else {
-                        mContainerActivity.getFileOperationsHelper().sendDownloadedFile(singleFile);
+                        // Obtain the file
+                        if (!singleFile.isDown()) {  // Download the file
+                            Log_OC.d(TAG, singleFile.getRemotePath() + " : File must be downloaded");
+                            ((FileDisplayActivity) mContainerActivity).startDownloadForSending(singleFile,
+                                    DOWNLOAD_SEND);
+
+                        } else {
+                            mContainerActivity.getFileOperationsHelper().sendDownloadedFile(singleFile);
+                        }
+                        return true;
                     }
                     }
-                    return true;
                 }
                 }
                 case R.id.action_set_as_wallpaper: {
                 case R.id.action_set_as_wallpaper: {
                     if (singleFile.isDown()) {
                     if (singleFile.isDown()) {

+ 16 - 0
src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java

@@ -44,6 +44,7 @@ import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
+import com.owncloud.android.providers.DiskLruImageCacheFileProvider;
 import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.services.observer.FileObserverService;
 import com.owncloud.android.services.observer.FileObserverService;
 import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.ui.activity.FileActivity;
@@ -495,6 +496,21 @@ public class FileOperationsHelper {
         }
         }
     }
     }
 
 
+    public void sendCachedImage(OCFile file) {
+        if (file != null) {
+            Intent sendIntent = new Intent(Intent.ACTION_SEND);
+            // set MimeType
+            sendIntent.setType(file.getMimetype());
+            sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + DiskLruImageCacheFileProvider.AUTHORITY +
+                    file.getRemotePath()));
+            sendIntent.putExtra(Intent.ACTION_SEND, true);      // Send Action
+
+            mFileActivity.startActivity(Intent.createChooser(sendIntent, "Send"));
+        } else {
+            Log_OC.wtf(TAG, "Trying to send a NULL OCFile");
+        }
+    }
+
     public void setPictureAs(OCFile file) {
     public void setPictureAs(OCFile file) {
         if (file != null) {
         if (file != null) {
             if (file.isDown()) {
             if (file.isDown()) {

+ 4 - 0
src/main/java/com/owncloud/android/ui/preview/ImageViewCustom.java

@@ -31,6 +31,7 @@ public class ImageViewCustom extends AppCompatImageView {
     private long mMovieDuration;
     private long mMovieDuration;
     private long mMovieRunDuration;
     private long mMovieRunDuration;
     private long mLastTick;
     private long mLastTick;
+    protected PreviewImageFragment previewImageFragment;
 
 
     public ImageViewCustom(Context context) {
     public ImageViewCustom(Context context) {
         super(context);
         super(context);
@@ -143,4 +144,7 @@ public class ImageViewCustom extends AppCompatImageView {
         }
         }
     }
     }
 
 
+    public void setPreviewImageFragment(PreviewImageFragment previewImageFragment) {
+        this.previewImageFragment = previewImageFragment;
+    }
 }
 }

+ 6 - 6
src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java

@@ -149,7 +149,7 @@ public class PreviewImageActivity extends FileActivity implements
             }
             }
 
 
             mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(),
             mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(),
-                    parentFolder, getAccount(), getStorageManager(), MainApp.isOnlyOnDevice());
+                    parentFolder, getAccount(), getStorageManager(), MainApp.isOnlyOnDevice(), getBaseContext());
         }
         }
 
 
         mViewPager = (ExtendedViewPager) findViewById(R.id.fragmentPager);
         mViewPager = (ExtendedViewPager) findViewById(R.id.fragmentPager);
@@ -323,7 +323,7 @@ public class PreviewImageActivity extends FileActivity implements
         finish();
         finish();
     }
     }
 
 
-    private void requestForDownload(OCFile file) {
+    public void requestForDownload(OCFile file) {
         if (mDownloaderBinder == null) {
         if (mDownloaderBinder == null) {
             Log_OC.d(TAG, "requestForDownload called without binder to download service");
             Log_OC.d(TAG, "requestForDownload called without binder to download service");
             
             
@@ -352,10 +352,6 @@ public class PreviewImageActivity extends FileActivity implements
             OCFile currentFile = mPreviewImagePagerAdapter.getFileAt(position); 
             OCFile currentFile = mPreviewImagePagerAdapter.getFileAt(position); 
             getSupportActionBar().setTitle(currentFile.getFileName());
             getSupportActionBar().setTitle(currentFile.getFileName());
             setDrawerIndicatorEnabled(false);
             setDrawerIndicatorEnabled(false);
-            if (!currentFile.isDown()
-                    && !mPreviewImagePagerAdapter.pendingErrorAt(position)) {
-                requestForDownload(currentFile);
-            }
 
 
             // Call to reset image zoom to initial state
             // Call to reset image zoom to initial state
             ((PreviewImagePagerAdapter) mViewPager.getAdapter()).resetZoom();
             ((PreviewImagePagerAdapter) mViewPager.getAdapter()).resetZoom();
@@ -454,6 +450,10 @@ public class PreviewImageActivity extends FileActivity implements
         }
         }
     }
     }
 
 
+    public void switchToFullScreen() {
+        hideSystemUI(mFullScreenAnchorView);
+    }
+
     @Override
     @Override
     protected void onAccountSet(boolean stateWasRecovered) {
     protected void onAccountSet(boolean stateWasRecovered) {
         super.onAccountSet(stateWasRecovered);
         super.onAccountSet(stateWasRecovered);

+ 115 - 18
src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java

@@ -36,6 +36,7 @@ import android.os.Process;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.NonNull;
 import android.support.annotation.StringRes;
 import android.support.annotation.StringRes;
+import android.support.design.widget.Snackbar;
 import android.support.v4.app.FragmentStatePagerAdapter;
 import android.support.v4.app.FragmentStatePagerAdapter;
 import android.util.DisplayMetrics;
 import android.util.DisplayMetrics;
 import android.view.LayoutInflater;
 import android.view.LayoutInflater;
@@ -53,8 +54,10 @@ import android.widget.TextView;
 
 
 import com.caverock.androidsvg.SVG;
 import com.caverock.androidsvg.SVG;
 import com.caverock.androidsvg.SVGParseException;
 import com.caverock.androidsvg.SVGParseException;
+import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.ThumbnailsCacheManager;
 import com.owncloud.android.files.FileMenuFilter;
 import com.owncloud.android.files.FileMenuFilter;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
@@ -88,7 +91,7 @@ public class PreviewImageFragment extends FileFragment {
 
 
     private static final String ARG_FILE = "FILE";
     private static final String ARG_FILE = "FILE";
     private static final String ARG_IGNORE_FIRST = "IGNORE_FIRST";
     private static final String ARG_IGNORE_FIRST = "IGNORE_FIRST";
-
+    private static final String ARG_SHOW_RESIZED_IMAGE = "SHOW_RESIZED_IMAGE";
     private static final String SCREEN_NAME = "Image Preview";
     private static final String SCREEN_NAME = "Image Preview";
 
 
     private TouchImageViewCustom mImageView;
     private TouchImageViewCustom mImageView;
@@ -100,6 +103,8 @@ public class PreviewImageFragment extends FileFragment {
     protected ImageView mMultiListIcon;
     protected ImageView mMultiListIcon;
     protected ProgressBar mMultiListProgress;
     protected ProgressBar mMultiListProgress;
 
 
+    private Boolean mShowResizedImage = false;
+
     public Bitmap mBitmap = null;
     public Bitmap mBitmap = null;
 
 
     private static final String TAG = PreviewImageFragment.class.getSimpleName();
     private static final String TAG = PreviewImageFragment.class.getSimpleName();
@@ -122,11 +127,14 @@ public class PreviewImageFragment extends FileFragment {
      *                              {@link FragmentStatePagerAdapter}
      *                              {@link FragmentStatePagerAdapter}
      *                              ; TODO better solution
      *                              ; TODO better solution
      */
      */
-    public static PreviewImageFragment newInstance(@NonNull OCFile imageFile, boolean ignoreFirstSavedState) {
+    public static PreviewImageFragment newInstance(@NonNull OCFile imageFile, boolean ignoreFirstSavedState,
+                                                   boolean showResizedImage) {
         PreviewImageFragment frag = new PreviewImageFragment();
         PreviewImageFragment frag = new PreviewImageFragment();
+        frag.mShowResizedImage = showResizedImage;
         Bundle args = new Bundle();
         Bundle args = new Bundle();
         args.putParcelable(ARG_FILE, imageFile);
         args.putParcelable(ARG_FILE, imageFile);
         args.putBoolean(ARG_IGNORE_FIRST, ignoreFirstSavedState);
         args.putBoolean(ARG_IGNORE_FIRST, ignoreFirstSavedState);
+        args.putBoolean(ARG_SHOW_RESIZED_IMAGE, showResizedImage);
         frag.setArguments(args);
         frag.setArguments(args);
         return frag;
         return frag;
     }
     }
@@ -158,6 +166,7 @@ public class PreviewImageFragment extends FileFragment {
         // not right now
         // not right now
 
 
         mIgnoreFirstSavedState = args.getBoolean(ARG_IGNORE_FIRST);
         mIgnoreFirstSavedState = args.getBoolean(ARG_IGNORE_FIRST);
+        mShowResizedImage = args.getBoolean(ARG_SHOW_RESIZED_IMAGE);
         setHasOptionsMenu(true);
         setHasOptionsMenu(true);
     }
     }
 
 
@@ -171,6 +180,7 @@ public class PreviewImageFragment extends FileFragment {
         super.onCreateView(inflater, container, savedInstanceState);
         super.onCreateView(inflater, container, savedInstanceState);
         View view = inflater.inflate(R.layout.preview_image_fragment, container, false);
         View view = inflater.inflate(R.layout.preview_image_fragment, container, false);
         mImageView = (TouchImageViewCustom) view.findViewById(R.id.image);
         mImageView = (TouchImageViewCustom) view.findViewById(R.id.image);
+        mImageView.setPreviewImageFragment(this);
         mImageView.setVisibility(View.GONE);
         mImageView.setVisibility(View.GONE);
 
 
         view.setOnClickListener(new OnClickListener() {
         view.setOnClickListener(new OnClickListener() {
@@ -197,6 +207,14 @@ public class PreviewImageFragment extends FileFragment {
         return view;
         return view;
     }
     }
 
 
+    public void switchToFullScreen() {
+        ((PreviewImageActivity) getActivity()).switchToFullScreen();
+    }
+
+    public void downloadFile() {
+        ((PreviewImageActivity) getActivity()).requestForDownload(getFile());
+    }
+
     protected void setupMultiView(View view) {
     protected void setupMultiView(View view) {
         mMultiListContainer = (LinearLayout) view.findViewById(R.id.empty_list_view);
         mMultiListContainer = (LinearLayout) view.findViewById(R.id.empty_list_view);
         mMultiListMessage = (TextView) view.findViewById(R.id.empty_list_view_text);
         mMultiListMessage = (TextView) view.findViewById(R.id.empty_list_view_text);
@@ -233,11 +251,63 @@ public class PreviewImageFragment extends FileFragment {
     @Override
     @Override
     public void onStart() {
     public void onStart() {
         super.onStart();
         super.onStart();
-        if (getFile() == null || !getFile().isDown()) {
-            showErrorMessage(R.string.preview_image_error_no_local_file);
+        if (getFile() != null) {
+            mImageView.setTag(getFile().getFileId());
+
+            if (mShowResizedImage) {
+                Bitmap resizedImage = ThumbnailsCacheManager.getBitmapFromDiskCache(
+                        String.valueOf(ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + getFile().getRemoteId()));
+
+                if (resizedImage != null && !getFile().needsUpdateThumbnail()) {
+                    mImageView.setImageBitmap(resizedImage);
+                    mImageView.setVisibility(View.VISIBLE);
+                    mBitmap = resizedImage;
+                } else {
+                    // show thumbnail while loading resized image
+                    Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
+                            String.valueOf(ThumbnailsCacheManager.PREFIX_THUMBNAIL + getFile().getRemoteId()));
+
+                    if (thumbnail != null) {
+                        mImageView.setImageBitmap(thumbnail);
+                        mImageView.setVisibility(View.VISIBLE);
+                        mBitmap = thumbnail;
+                    } else {
+                        thumbnail = ThumbnailsCacheManager.mDefaultImg;
+                    }
+
+                    // generate new resized image
+                    if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(getFile(), mImageView) &&
+                            mContainerActivity.getStorageManager() != null) {
+                        final ThumbnailsCacheManager.ResizedImageGenerationTask task =
+                                new ThumbnailsCacheManager.ResizedImageGenerationTask(PreviewImageFragment.this,
+                                        mImageView,
+                                        mContainerActivity.getStorageManager(),
+                                        mContainerActivity.getStorageManager().getAccount());
+                        if (resizedImage == null) {
+                            resizedImage = thumbnail;
+                        }
+                        final ThumbnailsCacheManager.AsyncResizedImageDrawable asyncDrawable =
+                                new ThumbnailsCacheManager.AsyncResizedImageDrawable(
+                                        MainApp.getAppContext().getResources(),
+                                        resizedImage,
+                                        task
+                                );
+                        mImageView.setImageDrawable(asyncDrawable);
+                        task.execute(getFile());
+                    }
+                }
+                mMultiView.setVisibility(View.GONE);
+                if (getResources() != null) {
+                    mImageView.setBackgroundColor(getResources().getColor(R.color.black));
+                }
+                mImageView.setVisibility(View.VISIBLE);
+
+            } else {
+                mLoadBitmapTask = new LoadBitmapTask(mImageView);
+                mLoadBitmapTask.execute(getFile());
+            }
         } else {
         } else {
-            mLoadBitmapTask = new LoadBitmapTask(mImageView);
-            mLoadBitmapTask.execute(getFile());
+            showErrorMessage(R.string.preview_image_error_no_local_file);
         }
         }
     }
     }
 
 
@@ -368,9 +438,15 @@ public class PreviewImageFragment extends FileFragment {
                 return true;
                 return true;
 
 
             case R.id.action_send_file:
             case R.id.action_send_file:
-                mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile());
-                return true;
+                if (MimeTypeUtil.isImage(getFile()) && !getFile().isDown()) {
+                    mContainerActivity.getFileOperationsHelper().sendCachedImage(getFile());
+                    return true;
+                } else {
+                    mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile());
+                    return true;
+                }
 
 
+            case R.id.action_download_file:
             case R.id.action_sync_file:
             case R.id.action_sync_file:
                 mContainerActivity.getFileOperationsHelper().syncFile(getFile());
                 mContainerActivity.getFileOperationsHelper().syncFile(getFile());
                 return true;
                 return true;
@@ -665,6 +741,24 @@ public class PreviewImageFragment extends FileFragment {
         }
         }
     }
     }
 
 
+    public void setErrorPreviewMessage() {
+        Snackbar.make(mMultiView, R.string.resized_image_not_possible, Snackbar.LENGTH_INDEFINITE)
+                .setAction(R.string.common_yes, new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        downloadFile();
+                    }
+                }).show();
+    }
+
+    public void setNoConnectionErrorMessage() {
+        try {
+            Snackbar.make(getView(), R.string.auth_no_net_conn_title, Snackbar.LENGTH_LONG).show();
+        } catch (NullPointerException npe) {
+
+        }
+    }
+
     /**
     /**
      * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewImageFragment}
      * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewImageFragment}
      * to be previewed.
      * to be previewed.
@@ -691,19 +785,22 @@ public class PreviewImageFragment extends FileFragment {
                 getFile().getMimetype().equalsIgnoreCase("image/svg+xml")) && getActivity() != null
                 getFile().getMimetype().equalsIgnoreCase("image/svg+xml")) && getActivity() != null
                 && getActivity() instanceof PreviewImageActivity && getResources() != null) {
                 && getActivity() instanceof PreviewImageActivity && getResources() != null) {
             PreviewImageActivity previewImageActivity = (PreviewImageActivity) getActivity();
             PreviewImageActivity previewImageActivity = (PreviewImageActivity) getActivity();
-            LayerDrawable layerDrawable = (LayerDrawable) mImageView.getDrawable();
-            Drawable layerOne;
 
 
-            if (previewImageActivity.getSystemUIVisible()) {
-                layerOne = getResources().getDrawable(R.color.white);
-            } else {
-                layerOne = getResources().getDrawable(R.drawable.backrepeat);
-            }
+            if (mImageView.getDrawable() instanceof LayerDrawable) {
+                LayerDrawable layerDrawable = (LayerDrawable) mImageView.getDrawable();
+                Drawable layerOne;
+
+                if (previewImageActivity.getSystemUIVisible()) {
+                    layerOne = getResources().getDrawable(R.color.white);
+                } else {
+                    layerOne = getResources().getDrawable(R.drawable.backrepeat);
+                }
 
 
-            layerDrawable.setDrawableByLayerId(layerDrawable.getId(0), layerOne);
+                layerDrawable.setDrawableByLayerId(layerDrawable.getId(0), layerOne);
 
 
-            mImageView.setImageDrawable(layerDrawable);
-            mImageView.invalidate();
+                mImageView.setImageDrawable(layerDrawable);
+                mImageView.invalidate();
+            }
         }
         }
     }
     }
 
 

+ 14 - 9
src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java

@@ -20,6 +20,7 @@
 package com.owncloud.android.ui.preview;
 package com.owncloud.android.ui.preview;
 
 
 import android.accounts.Account;
 import android.accounts.Account;
+import android.content.Context;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentStatePagerAdapter;
 import android.support.v4.app.FragmentStatePagerAdapter;
@@ -50,7 +51,8 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
     private Set<Integer> mObsoletePositions;
     private Set<Integer> mObsoletePositions;
     private Set<Integer> mDownloadErrors;
     private Set<Integer> mDownloadErrors;
     private FileDataStorageManager mStorageManager;
     private FileDataStorageManager mStorageManager;
-    
+    private Context mContext;
+
     private Map<Integer, FileFragment> mCachedFragments;
     private Map<Integer, FileFragment> mCachedFragments;
 
 
     /**
     /**
@@ -63,7 +65,7 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
      */
      */
     public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFolder,
     public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFolder,
                                     Account account, FileDataStorageManager storageManager,
                                     Account account, FileDataStorageManager storageManager,
-                                    boolean onlyOnDevice) {
+                                    boolean onlyOnDevice, Context context) {
         super(fragmentManager);
         super(fragmentManager);
         
         
         if (fragmentManager == null) {
         if (fragmentManager == null) {
@@ -76,6 +78,7 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
             throw new IllegalArgumentException("NULL storage manager");
             throw new IllegalArgumentException("NULL storage manager");
         }
         }
 
 
+        mContext = context;
         mAccount = account;
         mAccount = account;
         mStorageManager = storageManager;
         mStorageManager = storageManager;
         mImageFiles = mStorageManager.getFolderImages(parentFolder, onlyOnDevice);
         mImageFiles = mStorageManager.getFolderImages(parentFolder, onlyOnDevice);
@@ -140,16 +143,18 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
         OCFile file = mImageFiles.get(i);
         OCFile file = mImageFiles.get(i);
         Fragment fragment;
         Fragment fragment;
         if (file.isDown()) {
         if (file.isDown()) {
-            fragment = PreviewImageFragment.newInstance(file, mObsoletePositions.contains(i));
-            
-        } else if (mDownloadErrors.contains(i)) {
-            fragment = FileDownloadFragment.newInstance(file, mAccount, true);
-            ((FileDownloadFragment)fragment).setError(true);
-            mDownloadErrors.remove(i);
+            fragment = PreviewImageFragment.newInstance(file, mObsoletePositions.contains(i), false);
             
             
         } else {
         } else {
-            fragment = FileDownloadFragment.newInstance(file, mAccount, mObsoletePositions.contains(i));
+            if (mDownloadErrors.contains(i)) {
+                fragment = FileDownloadFragment.newInstance(file, mAccount, true);
+                ((FileDownloadFragment) fragment).setError(true);
+                mDownloadErrors.remove(i);
+            } else {
+                fragment = PreviewImageFragment.newInstance(file, mObsoletePositions.contains(i), true);
+            }
         }
         }
+
         mObsoletePositions.remove(i);
         mObsoletePositions.remove(i);
         return fragment;
         return fragment;
     }
     }

+ 24 - 0
src/main/java/third_parties/michaelOrtiz/TouchImageViewCustom.java

@@ -25,6 +25,7 @@ import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.Parcelable;
+import android.support.design.widget.Snackbar;
 import android.util.AttributeSet;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Log;
 import android.view.GestureDetector;
 import android.view.GestureDetector;
@@ -35,6 +36,7 @@ import android.view.animation.AccelerateDecelerateInterpolator;
 import android.widget.OverScroller;
 import android.widget.OverScroller;
 import android.widget.Scroller;
 import android.widget.Scroller;
 
 
+import com.owncloud.android.R;
 import com.owncloud.android.ui.preview.ImageViewCustom;
 import com.owncloud.android.ui.preview.ImageViewCustom;
 
 
 /**
 /**
@@ -888,9 +890,13 @@ public class TouchImageViewCustom extends ImageViewCustom {
      *
      *
      */
      */
     private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
     private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+        private boolean snackShown = false;
+
         @Override
         @Override
         public boolean onScaleBegin(ScaleGestureDetector detector) {
         public boolean onScaleBegin(ScaleGestureDetector detector) {
             setState(State.ZOOM);
             setState(State.ZOOM);
+
+            previewImageFragment.switchToFullScreen();
             return true;
             return true;
         }
         }
 
 
@@ -898,6 +904,10 @@ public class TouchImageViewCustom extends ImageViewCustom {
         public boolean onScale(ScaleGestureDetector detector) {
         public boolean onScale(ScaleGestureDetector detector) {
         	scaleImage(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY(), true);
         	scaleImage(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY(), true);
         	
         	
+            if (!snackShown && getCurrentZoom() > 2 && !previewImageFragment.getFile().isDown()) {
+                showDownloadSnackbar();
+            }
+
         	//
         	//
         	// OnTouchImageViewListener is set: TouchImageView pinch zoomed by user.
         	// OnTouchImageViewListener is set: TouchImageView pinch zoomed by user.
         	//
         	//
@@ -907,6 +917,20 @@ public class TouchImageViewCustom extends ImageViewCustom {
             return true;
             return true;
         }
         }
         
         
+        private void showDownloadSnackbar() {
+            snackShown = true;
+
+            Snackbar.make(getRootView(), R.string.resized_images_download_full_image, Snackbar.LENGTH_LONG)
+                    .setCallback(new Snackbar.Callback() {
+                        @Override
+                        public void onDismissed(Snackbar snackbar, int event) {
+                            super.onDismissed(snackbar, event);
+                            snackShown = false;
+                        }
+                    })
+                    .setAction(R.string.common_yes, v -> previewImageFragment.downloadFile()).show();
+        }
+
         @Override
         @Override
         public void onScaleEnd(ScaleGestureDetector detector) {
         public void onScaleEnd(ScaleGestureDetector detector) {
         	super.onScaleEnd(detector);
         	super.onScaleEnd(detector);

+ 4 - 0
src/main/res/values/strings.xml

@@ -709,6 +709,10 @@
     <string name="push_notifications_old_login">No push notifications due to outdated login session. Please consider re-adding your account.</string>
     <string name="push_notifications_old_login">No push notifications due to outdated login session. Please consider re-adding your account.</string>
     <string name="push_notifications_temp_error">Push notifications currently not available.</string>
     <string name="push_notifications_temp_error">Push notifications currently not available.</string>
     <string name="date_unknown">Unknown</string>
     <string name="date_unknown">Unknown</string>
+
+    <string name="resized_image_not_possible">No resized image possible. Download full image?</string>
+    <string name="resized_images_download_full_image">Download full image?</string>
+
     <string name="store_short_desc">The Nextcloud Android app allows you to access all your files on your Nextcloud</string>
     <string name="store_short_desc">The Nextcloud Android app allows you to access all your files on your Nextcloud</string>
     <string name="store_full_desc">The Open Source Nextcloud Android app allows you to access all your files on your Nextcloud.\n\nFeatures:\n* Easy, modern interface, fully themed in alignment with your server\'s theming\n* Upload your files to your Nextcloud server\n* Share your files with others\n* Keep your favorite files and folders synced\n* Search across all folders on your server\n* Auto Upload for photos and videos taken by your device\n* Keep up to date with notifications\n* Multi-account support\n* Secure access to your data with fingerprint or pin\n* Integration with DAVdroid for easy setup of Calendar &amp; Contacts synchronization\n\nPlease report all issues at https://github.com/nextcloud/android/issues and discuss this app at https://help.nextcloud.com/c/clients/android\n\nNew to Nextcloud? Nextcloud is a private file sync &amp; share and communication server. It is fully open source and you can host it yourself or pay a company to do it for you. That way, you are in control of your photos, your calendar and contact data, your documents and everything else.\n\nCheck out Nextcloud at https://nextcloud.com</string>
     <string name="store_full_desc">The Open Source Nextcloud Android app allows you to access all your files on your Nextcloud.\n\nFeatures:\n* Easy, modern interface, fully themed in alignment with your server\'s theming\n* Upload your files to your Nextcloud server\n* Share your files with others\n* Keep your favorite files and folders synced\n* Search across all folders on your server\n* Auto Upload for photos and videos taken by your device\n* Keep up to date with notifications\n* Multi-account support\n* Secure access to your data with fingerprint or pin\n* Integration with DAVdroid for easy setup of Calendar &amp; Contacts synchronization\n\nPlease report all issues at https://github.com/nextcloud/android/issues and discuss this app at https://help.nextcloud.com/c/clients/android\n\nNew to Nextcloud? Nextcloud is a private file sync &amp; share and communication server. It is fully open source and you can host it yourself or pay a company to do it for you. That way, you are in control of your photos, your calendar and contact data, your documents and everything else.\n\nCheck out Nextcloud at https://nextcloud.com</string>
 </resources>
 </resources>