Resized images

This commit is contained in:
tobiasKaminsky 2017-10-24 11:03:28 +02:00 committed by AndyScherzinger
parent f838dd7b43
commit 0a649f01bb
No known key found for this signature in database
GPG key ID: 6CADC7E3523C308B
19 changed files with 688 additions and 158 deletions

View file

@ -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"

View file

@ -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;
@ -69,6 +76,9 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
*/ */
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 = File cacheDir = MainApp.getAppContext().getExternalCacheDir();
MainApp.getAppContext().getExternalCacheDir().getPath() +
File.separator + CACHE_FOLDER; if (cacheDir != null) {
String cachePath = cacheDir.getPath() + File.separator + CACHE_FOLDER;
Log_OC.d(TAG, "create dir: " + cachePath); Log_OC.d(TAG, "create dir: " + cachePath);
final File diskCacheDir = new File(cachePath); File diskCacheDir = new File(cachePath);
mThumbnailCache = new DiskLruImageCache( mThumbnailCache = new DiskLruImageCache(diskCacheDir, DISK_CACHE_SIZE, mCompressFormat,
diskCacheDir, mCompressQuality);
DISK_CACHE_SIZE, } else {
mCompressFormat, throw new FileNotFoundException("Thumbnail cache could not be opened");
mCompressQuality }
); } catch (java.io.IOException e) {
} catch (Exception e) { Log_OC.d(TAG, e.getMessage());
Log_OC.d(TAG, "Thumbnail cache could not be opened ", e);
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]; ThumbnailGenerationTaskObject object = params[0];
if (params.length == 2) { mFile = object.getFile();
mImageKey = (String) params[1]; 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 {
} }
} }
/** private Bitmap doThumbnailFromOCFileInBackground() {
* Converts size of file icon from dp to pixel Bitmap thumbnail;
* @return int OCFile file = (OCFile) mFile;
*/ String imageKey = PREFIX_THUMBNAIL + String.valueOf(file.getRemoteId());
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());
// 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 pxW;
int px = getThumbnailDimension(); int pxH;
pxW = pxH = getThumbnailDimension();
if (file.isDown()) { if (file.isDown()) {
Bitmap temp = BitmapUtils.decodeSampledBitmapFromFile( Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(
file.getStoragePath(), px, px); file.getStoragePath(), pxW, pxH);
Bitmap bitmap = ThumbnailUtils.extractThumbnail(temp, px, px);
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(), "/"); pxW + "/" + pxH + Uri.encode(file.getRemotePath(), "/");
Log_OC.d("Thumbnail", "URI: " + uri); 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); thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH);
} else {
mClient.exhaustResponse(getMethod.getResponseBodyAsStream());
}
// Handle PNG // Handle PNG
if (file.getMimetype().equalsIgnoreCase("image/png")) { if (file.getMimetype().equalsIgnoreCase("image/png")) {
thumbnail = handlePNG(thumbnail, px); thumbnail = handlePNG(thumbnail, pxW);
} }
// Add thumbnail to cache // Add thumbnail to cache
if (thumbnail != null) { if (thumbnail != null) {
Log_OC.d(TAG, "add thumbnail to cache: " + file.getFileName());
addBitmapToCache(imageKey, thumbnail); addBitmapToCache(imageKey, thumbnail);
} }
} else {
mClient.exhaustResponse(getMethod.getResponseBodyAsStream());
}
} 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(), pxW, pxH);
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(), 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 { public static class AsyncResizedImageDrawable extends BitmapDrawable {
private final WeakReference<MediaThumbnailGenerationTask> bitmapWorkerTaskReference; private final WeakReference<ResizedImageGenerationTask> bitmapWorkerTaskReference;
public AsyncMediaThumbnailDrawable(
Resources res, Bitmap bitmap, MediaThumbnailGenerationTask bitmapWorkerTask
) {
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);
}
}
} }

View file

@ -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]; File file = new File(mCurrentUpload.getOriginalStoragePath());
params[0] = new File(mCurrentUpload.getOriginalStoragePath()); String remoteId = mCurrentUpload.getFile().getRemoteId();
params[1] = mCurrentUpload.getFile().getRemoteId();
task.execute(params); task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, remoteId));
} }
} }

View file

@ -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);

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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");
} }
} }

View file

@ -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());
} }

View file

@ -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

View file

@ -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 {

View file

@ -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()) {
if (!getFile().isDown()) { // Download the file
Log_OC.d(TAG, getFile().getRemotePath() + " : File must be downloaded");
((FileDisplayActivity) mContainerActivity).startDownloadForSending(getFile(),
OCFileListFragment.DOWNLOAD_SEND);
}
else {
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 {

View file

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

View file

@ -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()) {

View file

@ -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;
}
} }

View file

@ -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);

View file

@ -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,12 +251,64 @@ public class PreviewImageFragment extends FileFragment {
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
if (getFile() == null || !getFile().isDown()) { if (getFile() != null) {
showErrorMessage(R.string.preview_image_error_no_local_file); 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 { } else {
mLoadBitmapTask = new LoadBitmapTask(mImageView); mLoadBitmapTask = new LoadBitmapTask(mImageView);
mLoadBitmapTask.execute(getFile()); mLoadBitmapTask.execute(getFile());
} }
} else {
showErrorMessage(R.string.preview_image_error_no_local_file);
}
} }
@Override @Override
@ -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:
if (MimeTypeUtil.isImage(getFile()) && !getFile().isDown()) {
mContainerActivity.getFileOperationsHelper().sendCachedImage(getFile());
return true;
} else {
mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile()); mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile());
return true; 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,6 +785,8 @@ 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();
if (mImageView.getDrawable() instanceof LayerDrawable) {
LayerDrawable layerDrawable = (LayerDrawable) mImageView.getDrawable(); LayerDrawable layerDrawable = (LayerDrawable) mImageView.getDrawable();
Drawable layerOne; Drawable layerOne;
@ -706,6 +802,7 @@ public class PreviewImageFragment extends FileFragment {
mImageView.invalidate(); mImageView.invalidate();
} }
} }
}
private static int convertDpToPixel(float dp, Context context) { private static int convertDpToPixel(float dp, Context context) {

View file

@ -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,6 +51,7 @@ 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)); fragment = PreviewImageFragment.newInstance(file, mObsoletePositions.contains(i), false);
} else if (mDownloadErrors.contains(i)) {
fragment = FileDownloadFragment.newInstance(file, mAccount, true);
((FileDownloadFragment)fragment).setError(true);
mDownloadErrors.remove(i);
} 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;
} }

View file

@ -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);

View file

@ -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>