From 0a649f01bba1eb32c384093b4582408721eaa1a3 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Tue, 24 Oct 2017 11:03:28 +0200 Subject: [PATCH] Resized images --- src/main/AndroidManifest.xml | 6 + .../datamodel/ThumbnailsCacheManager.java | 417 ++++++++++++++---- .../android/files/services/FileUploader.java | 7 +- .../operations/RemoveFileOperation.java | 4 + .../operations/UploadFileOperation.java | 2 +- .../DiskLruImageCacheFileProvider.java | 143 ++++++ .../adapter/ExpandableUploadListAdapter.java | 5 +- .../ui/adapter/FileListListAdapter.java | 7 +- .../ui/adapter/LocalFileListAdapter.java | 4 +- .../android/ui/adapter/UploaderAdapter.java | 2 +- .../ui/fragment/FileDetailFragment.java | 12 +- .../ui/fragment/OCFileListFragment.java | 19 +- .../ui/helpers/FileOperationsHelper.java | 16 + .../android/ui/preview/ImageViewCustom.java | 4 + .../ui/preview/PreviewImageActivity.java | 12 +- .../ui/preview/PreviewImageFragment.java | 135 +++++- .../ui/preview/PreviewImagePagerAdapter.java | 23 +- .../michaelOrtiz/TouchImageViewCustom.java | 24 + src/main/res/values/strings.xml | 4 + 19 files changed, 688 insertions(+), 158 deletions(-) create mode 100644 src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index fda5644c8a..8a367d158b 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -185,6 +185,12 @@ android:resource="@xml/exposed_filepaths" /> + + + { + public static class ResizedImageGenerationTask extends AsyncTask { + private PreviewImageFragment previewImageFragment; + private FileDataStorageManager storageManager; + private Account account; + private WeakReference 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 { private final WeakReference mImageViewReference; private static Account mAccount; private ArrayList mAsyncTasks = null; @@ -232,7 +430,7 @@ public class ThumbnailsCacheManager { @SuppressFBWarnings("Dm") @Override - protected Bitmap doInBackground(Object... params) { + protected Bitmap doInBackground(ThumbnailGenerationTaskObject... params) { Bitmap thumbnail = null; try { @@ -245,13 +443,12 @@ public class ThumbnailsCacheManager { 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) { - thumbnail = doOCFileInBackground(); + thumbnail = doThumbnailFromOCFileInBackground(); if (MimeTypeUtil.isVideo((OCFile) mFile) && thumbnail != null) { 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 - Bitmap thumbnail = getBitmapFromDiskCache(imageKey); + thumbnail = getBitmapFromDiskCache(imageKey); // Not found in disk cache if (thumbnail == null || file.needsUpdateThumbnail()) { - - int px = getThumbnailDimension(); + int pxW; + int pxH; + pxW = pxH = getThumbnailDimension(); 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) { // Handle 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); mStorageManager.saveFile(file); @@ -348,10 +534,12 @@ public class ThumbnailsCacheManager { if (serverOCVersion.supportsRemoteThumbnails()) { getMethod = null; try { + // thumbnail String uri = mClient.getBaseUri() + "" + "/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.setRequestHeader("Cookie", "nc_sameSiteCookielax=true;nc_sameSiteCookiestrict=true"); @@ -363,20 +551,22 @@ public class ThumbnailsCacheManager { if (status == HttpStatus.SC_OK) { InputStream inputStream = getMethod.getResponseBodyAsStream(); 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 { 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 { @@ -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() { File file = (File)mFile; @@ -405,19 +607,22 @@ public class ThumbnailsCacheManager { imageKey = String.valueOf(file.hashCode()); } + // local file should always generate a thumbnail + mImageKey = PREFIX_THUMBNAIL + mImageKey; + // Check disk cache in background thread Bitmap thumbnail = getBitmapFromDiskCache(imageKey); // Not found in disk cache 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) { - thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px); + thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), pxW, pxH); } } return thumbnail; @@ -514,7 +719,7 @@ public class ThumbnailsCacheManager { Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getAbsolutePath(), px, px); if (bitmap != null) { - thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px); + thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px, px); } } else if (Type.VIDEO.equals(type)) { MediaMetadataRetriever retriever = new MediaMetadataRetriever(); @@ -541,7 +746,7 @@ public class ThumbnailsCacheManager { int max = Math.max(width, height); if (max > px) { 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 * @return int @@ -774,6 +958,17 @@ public class ThumbnailsCacheManager { 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){ Bitmap playButton = BitmapFactory.decodeResource(MainApp.getAppContext().getResources(), R.drawable.view_play); @@ -852,22 +1047,30 @@ public class ThumbnailsCacheManager { } } - public static class AsyncMediaThumbnailDrawable extends BitmapDrawable { - private final WeakReference bitmapWorkerTaskReference; - - public AsyncMediaThumbnailDrawable( - Resources res, Bitmap bitmap, MediaThumbnailGenerationTask bitmapWorkerTask - ) { + public static class AsyncResizedImageDrawable extends BitmapDrawable { + private final WeakReference bitmapWorkerTaskReference; + public AsyncResizedImageDrawable(Resources res, Bitmap bitmap, ResizedImageGenerationTask bitmapWorkerTask) { super(res, bitmap); bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); } - public MediaThumbnailGenerationTask getBitmapWorkerTask() { + private ResizedImageGenerationTask getBitmapWorkerTask() { return bitmapWorkerTaskReference.get(); } } + public static class AsyncMediaThumbnailDrawable extends BitmapDrawable { + private final WeakReference bitmapWorkerTaskReference; + + public AsyncMediaThumbnailDrawable(Resources res, Bitmap bitmap, + MediaThumbnailGenerationTask bitmapWorkerTask) { + + super(res, bitmap); + bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); + } + } + public static class AsyncAvatarDrawable extends BitmapDrawable { private final WeakReference avatarWorkerTaskReference; @@ -897,4 +1100,22 @@ public class ThumbnailsCacheManager { 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); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/owncloud/android/files/services/FileUploader.java b/src/main/java/com/owncloud/android/files/services/FileUploader.java index 9b9dd0cd10..7464b5a60f 100644 --- a/src/main/java/com/owncloud/android/files/services/FileUploader.java +++ b/src/main/java/com/owncloud/android/files/services/FileUploader.java @@ -1071,11 +1071,10 @@ public class FileUploader extends Service final ThumbnailsCacheManager.ThumbnailGenerationTask task = 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)); } } diff --git a/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java b/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java index 8f7067df27..a07113f916 100644 --- a/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java +++ b/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java @@ -22,6 +22,7 @@ package com.owncloud.android.operations; 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.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; @@ -75,6 +76,9 @@ public class RemoveFileOperation extends SyncOperation { mFileToRemove = getStorageManager().getFileByPath(mRemotePath); + // store resized image + ThumbnailsCacheManager.generateResizedImage(mFileToRemove); + boolean localRemovalFailed = false; if (!mOnlyLocalCopy) { RemoveRemoteFileOperation operation = new RemoveRemoteFileOperation(mRemotePath); diff --git a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 14faa073cf..ab051a41d8 100644 --- a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -918,7 +918,7 @@ public class UploadFileOperation extends SyncOperation { // generate new Thumbnail final ThumbnailsCacheManager.ThumbnailGenerationTask task = new ThumbnailsCacheManager.ThumbnailGenerationTask(getStorageManager(), mAccount); - task.execute(file); + task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, file.getRemoteId())); } private void updateOCFile(OCFile file, RemoteFile remoteFile) { diff --git a/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java b/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java new file mode 100644 index 0000000000..c04e334625 --- /dev/null +++ b/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 . + */ + +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; + } +} diff --git a/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java index 2beb7fbced..095e024628 100755 --- a/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java @@ -449,7 +449,8 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple task ); fileIcon.setImageDrawable(asyncDrawable); - task.execute(fakeFileToCheatThumbnailsCacheManagerInterface); + task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject( + fakeFileToCheatThumbnailsCacheManagerInterface, null)); } } @@ -485,7 +486,7 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple task ); fileIcon.setImageDrawable(asyncDrawable); - task.execute(file); + task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, null)); Log_OC.v(TAG, "Executing task to generate a new thumbnail"); } } diff --git a/src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java index ccb0f089e4..f753baa0a1 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java @@ -343,7 +343,9 @@ public class FileListListAdapter extends BaseAdapter { if (!file.isFolder()) { if ((MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file)) && file.getRemoteId() != null) { // 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 (MimeTypeUtil.isVideo(file)) { @@ -375,7 +377,8 @@ public class FileListListAdapter extends BaseAdapter { ); fileIcon.setImageDrawable(asyncDrawable); asyncTasks.add(task); - task.execute(file); + task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, + file.getRemoteId())); } catch (IllegalArgumentException e) { Log_OC.d(TAG, "ThumbnailGenerationTask : " + e.getMessage()); } diff --git a/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java index beaa9f2b77..79494346fb 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java +++ b/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)){ // Thumbnail in Cache? Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(file.hashCode()) + ThumbnailsCacheManager.PREFIX_THUMBNAIL + String.valueOf(file.hashCode()) ); if (thumbnail != null){ fileIcon.setImageBitmap(thumbnail); @@ -226,7 +226,7 @@ public class LocalFileListAdapter extends BaseAdapter implements FilterableListA task ); fileIcon.setImageDrawable(asyncDrawable); - task.execute(file); + task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, null)); Log_OC.v(TAG, "Executing task to generate a new thumbnail"); } // else, already being generated, don't restart it diff --git a/src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java index ebedb42c71..3346d08d22 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java @@ -122,7 +122,7 @@ public class UploaderAdapter extends SimpleAdapter { task ); fileIcon.setImageDrawable(asyncDrawable); - task.execute(file); + task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, file.getRemoteId())); } } } else { diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index 97a652f015..08e0e36b5d 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -339,14 +339,10 @@ public class FileDetailFragment extends FileFragment implements OnClickListener, return true; } 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()); + } else { + mContainerActivity.getFileOperationsHelper().sendCachedImage(getFile()); } return true; } @@ -508,7 +504,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener, task ); iv.setImageDrawable(asyncDrawable); - task.execute(file); + task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, file.getRemoteId())); } } } else { diff --git a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index adc0aba918..cf4952bf75 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -970,14 +970,21 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi return true; } 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 { - 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: { if (singleFile.isDown()) { diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 1e97f073b2..18e9eb1524 100755 --- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/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.ShareType; 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.observer.FileObserverService; 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) { if (file != null) { if (file.isDown()) { diff --git a/src/main/java/com/owncloud/android/ui/preview/ImageViewCustom.java b/src/main/java/com/owncloud/android/ui/preview/ImageViewCustom.java index e502cfa770..467d1cd7dd 100644 --- a/src/main/java/com/owncloud/android/ui/preview/ImageViewCustom.java +++ b/src/main/java/com/owncloud/android/ui/preview/ImageViewCustom.java @@ -31,6 +31,7 @@ public class ImageViewCustom extends AppCompatImageView { private long mMovieDuration; private long mMovieRunDuration; private long mLastTick; + protected PreviewImageFragment previewImageFragment; public ImageViewCustom(Context context) { super(context); @@ -143,4 +144,7 @@ public class ImageViewCustom extends AppCompatImageView { } } + public void setPreviewImageFragment(PreviewImageFragment previewImageFragment) { + this.previewImageFragment = previewImageFragment; + } } diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java b/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java index bb22667cb0..b545230518 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java @@ -149,7 +149,7 @@ public class PreviewImageActivity extends FileActivity implements } mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), - parentFolder, getAccount(), getStorageManager(), MainApp.isOnlyOnDevice()); + parentFolder, getAccount(), getStorageManager(), MainApp.isOnlyOnDevice(), getBaseContext()); } mViewPager = (ExtendedViewPager) findViewById(R.id.fragmentPager); @@ -323,7 +323,7 @@ public class PreviewImageActivity extends FileActivity implements finish(); } - private void requestForDownload(OCFile file) { + public void requestForDownload(OCFile file) { if (mDownloaderBinder == null) { 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); getSupportActionBar().setTitle(currentFile.getFileName()); setDrawerIndicatorEnabled(false); - if (!currentFile.isDown() - && !mPreviewImagePagerAdapter.pendingErrorAt(position)) { - requestForDownload(currentFile); - } // Call to reset image zoom to initial state ((PreviewImagePagerAdapter) mViewPager.getAdapter()).resetZoom(); @@ -454,6 +450,10 @@ public class PreviewImageActivity extends FileActivity implements } } + public void switchToFullScreen() { + hideSystemUI(mFullScreenAnchorView); + } + @Override protected void onAccountSet(boolean stateWasRecovered) { super.onAccountSet(stateWasRecovered); diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java b/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java index 169f65a299..811652f7fb 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java +++ b/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.NonNull; import android.support.annotation.StringRes; +import android.support.design.widget.Snackbar; import android.support.v4.app.FragmentStatePagerAdapter; import android.util.DisplayMetrics; import android.view.LayoutInflater; @@ -53,8 +54,10 @@ import android.widget.TextView; import com.caverock.androidsvg.SVG; import com.caverock.androidsvg.SVGParseException; +import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.files.FileMenuFilter; import com.owncloud.android.lib.common.utils.Log_OC; 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_IGNORE_FIRST = "IGNORE_FIRST"; - + private static final String ARG_SHOW_RESIZED_IMAGE = "SHOW_RESIZED_IMAGE"; private static final String SCREEN_NAME = "Image Preview"; private TouchImageViewCustom mImageView; @@ -100,6 +103,8 @@ public class PreviewImageFragment extends FileFragment { protected ImageView mMultiListIcon; protected ProgressBar mMultiListProgress; + private Boolean mShowResizedImage = false; + public Bitmap mBitmap = null; private static final String TAG = PreviewImageFragment.class.getSimpleName(); @@ -122,11 +127,14 @@ public class PreviewImageFragment extends FileFragment { * {@link FragmentStatePagerAdapter} * ; 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(); + frag.mShowResizedImage = showResizedImage; Bundle args = new Bundle(); args.putParcelable(ARG_FILE, imageFile); args.putBoolean(ARG_IGNORE_FIRST, ignoreFirstSavedState); + args.putBoolean(ARG_SHOW_RESIZED_IMAGE, showResizedImage); frag.setArguments(args); return frag; } @@ -158,6 +166,7 @@ public class PreviewImageFragment extends FileFragment { // not right now mIgnoreFirstSavedState = args.getBoolean(ARG_IGNORE_FIRST); + mShowResizedImage = args.getBoolean(ARG_SHOW_RESIZED_IMAGE); setHasOptionsMenu(true); } @@ -171,6 +180,7 @@ public class PreviewImageFragment extends FileFragment { super.onCreateView(inflater, container, savedInstanceState); View view = inflater.inflate(R.layout.preview_image_fragment, container, false); mImageView = (TouchImageViewCustom) view.findViewById(R.id.image); + mImageView.setPreviewImageFragment(this); mImageView.setVisibility(View.GONE); view.setOnClickListener(new OnClickListener() { @@ -197,6 +207,14 @@ public class PreviewImageFragment extends FileFragment { return view; } + public void switchToFullScreen() { + ((PreviewImageActivity) getActivity()).switchToFullScreen(); + } + + public void downloadFile() { + ((PreviewImageActivity) getActivity()).requestForDownload(getFile()); + } + protected void setupMultiView(View view) { mMultiListContainer = (LinearLayout) view.findViewById(R.id.empty_list_view); mMultiListMessage = (TextView) view.findViewById(R.id.empty_list_view_text); @@ -233,11 +251,63 @@ public class PreviewImageFragment extends FileFragment { @Override public void 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 { - 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; 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: mContainerActivity.getFileOperationsHelper().syncFile(getFile()); 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} * to be previewed. @@ -691,19 +785,22 @@ public class PreviewImageFragment extends FileFragment { getFile().getMimetype().equalsIgnoreCase("image/svg+xml")) && getActivity() != null && getActivity() instanceof PreviewImageActivity && getResources() != null) { 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); + + mImageView.setImageDrawable(layerDrawable); + mImageView.invalidate(); } - - layerDrawable.setDrawableByLayerId(layerDrawable.getId(0), layerOne); - - mImageView.setImageDrawable(layerDrawable); - mImageView.invalidate(); } } diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java b/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java index 8c2118c840..a3ae7ab5fe 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java @@ -20,6 +20,7 @@ package com.owncloud.android.ui.preview; import android.accounts.Account; +import android.content.Context; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; @@ -50,7 +51,8 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { private Set mObsoletePositions; private Set mDownloadErrors; private FileDataStorageManager mStorageManager; - + private Context mContext; + private Map mCachedFragments; /** @@ -63,7 +65,7 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { */ public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFolder, Account account, FileDataStorageManager storageManager, - boolean onlyOnDevice) { + boolean onlyOnDevice, Context context) { super(fragmentManager); if (fragmentManager == null) { @@ -76,6 +78,7 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { throw new IllegalArgumentException("NULL storage manager"); } + mContext = context; mAccount = account; mStorageManager = storageManager; mImageFiles = mStorageManager.getFolderImages(parentFolder, onlyOnDevice); @@ -140,16 +143,18 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { OCFile file = mImageFiles.get(i); Fragment fragment; 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 { - 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); return fragment; } diff --git a/src/main/java/third_parties/michaelOrtiz/TouchImageViewCustom.java b/src/main/java/third_parties/michaelOrtiz/TouchImageViewCustom.java index 9b3edbe028..3baecf7570 100644 --- a/src/main/java/third_parties/michaelOrtiz/TouchImageViewCustom.java +++ b/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.Bundle; import android.os.Parcelable; +import android.support.design.widget.Snackbar; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; @@ -35,6 +36,7 @@ import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.OverScroller; import android.widget.Scroller; +import com.owncloud.android.R; import com.owncloud.android.ui.preview.ImageViewCustom; /** @@ -888,9 +890,13 @@ public class TouchImageViewCustom extends ImageViewCustom { * */ private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { + private boolean snackShown = false; + @Override public boolean onScaleBegin(ScaleGestureDetector detector) { setState(State.ZOOM); + + previewImageFragment.switchToFullScreen(); return true; } @@ -898,6 +904,10 @@ public class TouchImageViewCustom extends ImageViewCustom { public boolean onScale(ScaleGestureDetector detector) { scaleImage(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY(), true); + if (!snackShown && getCurrentZoom() > 2 && !previewImageFragment.getFile().isDown()) { + showDownloadSnackbar(); + } + // // OnTouchImageViewListener is set: TouchImageView pinch zoomed by user. // @@ -907,6 +917,20 @@ public class TouchImageViewCustom extends ImageViewCustom { 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 public void onScaleEnd(ScaleGestureDetector detector) { super.onScaleEnd(detector); diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index b8d4f64e74..1c7eefdb95 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -709,6 +709,10 @@ No push notifications due to outdated login session. Please consider re-adding your account. Push notifications currently not available. Unknown + + No resized image possible. Download full image? + Download full image? + The Nextcloud Android app allows you to access all your files on your Nextcloud 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 & 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 & 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