diff --git a/scripts/lint/lint-results.txt b/scripts/lint/lint-results.txt index eaf517cb66cb..d551712f07d4 100644 --- a/scripts/lint/lint-results.txt +++ b/scripts/lint/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 1 error and 520 warnings + Lint Report: 1 error and 515 warnings diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 5066ca09d69f..94c28dcd2cec 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -84,8 +84,9 @@ - + + + + - + diff --git a/src/main/java/com/owncloud/android/authentication/AccountUtils.java b/src/main/java/com/owncloud/android/authentication/AccountUtils.java index 2dd094be02d9..ab0b1a41632e 100644 --- a/src/main/java/com/owncloud/android/authentication/AccountUtils.java +++ b/src/main/java/com/owncloud/android/authentication/AccountUtils.java @@ -88,7 +88,7 @@ public static Account getCurrentOwnCloudAccount(Context context) { if (defaultAccount == null && ocAccounts.length > 0) { // take first which is not pending for removal account as fallback - for (Account account: ocAccounts) { + for (Account account : ocAccounts) { boolean pendingForRemoval = arbitraryDataProvider.getBooleanValue(account, ManageAccountsActivity.PENDING_FOR_REMOVAL); diff --git a/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java b/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java index 8ece8d30eef4..f1056e266846 100644 --- a/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -372,7 +372,7 @@ private void initWebViewLogin(String baseURL) { mLoginWebView.getSettings().setSaveFormData(false); mLoginWebView.getSettings().setSavePassword(false); - if (baseURL != null && !baseURL.isEmpty()){ + if (baseURL != null && !baseURL.isEmpty()) { Map headers = new HashMap<>(); headers.put(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE); mLoginWebView.loadUrl(baseURL + WEB_LOGIN, headers); diff --git a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index 507e10f56b5d..32f59158d51a 100644 --- a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -1,4 +1,4 @@ -/** +/* * ownCloud Android client application * * @author Tobias Kaminsky @@ -22,19 +22,23 @@ package com.owncloud.android.datamodel; import android.accounts.Account; +import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Point; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.media.MediaMetadataRetriever; import android.media.ThumbnailUtils; import android.net.Uri; import android.os.AsyncTask; +import android.view.Display; import android.view.MenuItem; +import android.view.WindowManager; import android.widget.ImageView; import com.owncloud.android.MainApp; @@ -47,7 +51,9 @@ import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.status.OwnCloudVersion; 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.ConnectivityUtils; import com.owncloud.android.utils.DisplayUtils.AvatarGenerationListener; import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.MimeTypeUtil; @@ -56,6 +62,7 @@ import org.apache.commons.httpclient.methods.GetMethod; import java.io.File; +import java.io.FileNotFoundException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -66,16 +73,16 @@ * Manager for concurrent access to thumbnails cache. */ public class ThumbnailsCacheManager { - + private static final String TAG = ThumbnailsCacheManager.class.getSimpleName(); - + private static final String CACHE_FOLDER = "thumbnailCache"; private static final Object mThumbnailsDiskCacheLock = new Object(); private static DiskLruImageCache mThumbnailCache = null; 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 int mCompressQuality = 70; private static OwnCloudClient mClient = null; @@ -92,7 +99,7 @@ public class ThumbnailsCacheManager { R.drawable.file_movie ); - + public static class InitDiskCacheTask extends AsyncTask { @Override @@ -104,19 +111,19 @@ protected Void doInBackground(File... params) { try { // Check if media is mounted or storage is built-in, if so, // try and use external cache dir; otherwise use internal cache dir - final String cachePath = - MainApp.getAppContext().getExternalCacheDir().getPath() + - File.separator + CACHE_FOLDER; - Log_OC.d(TAG, "create dir: " + cachePath); - final File diskCacheDir = new File(cachePath); - mThumbnailCache = new DiskLruImageCache( - diskCacheDir, - DISK_CACHE_SIZE, - mCompressFormat, - mCompressQuality - ); - } catch (Exception e) { - Log_OC.d(TAG, "Thumbnail cache could not be opened ", e); + File cacheDir = MainApp.getAppContext().getExternalCacheDir(); + + if (cacheDir != null) { + String cachePath = cacheDir.getPath() + File.separator + CACHE_FOLDER; + Log_OC.d(TAG, "create dir: " + cachePath); + File diskCacheDir = new File(cachePath); + mThumbnailCache = new DiskLruImageCache(diskCacheDir, DISK_CACHE_SIZE, mCompressFormat, + mCompressQuality); + } else { + throw new FileNotFoundException("Thumbnail cache could not be opened"); + } + } catch (java.io.IOException e) { + Log_OC.d(TAG, e.getMessage()); mThumbnailCache = null; } } @@ -129,36 +136,53 @@ protected Void doInBackground(File... params) { /** * Converts size of file icon from dp to pixel + * * @return int */ - private static int getThumbnailDimension(){ + private static int getThumbnailDimension() { // Converts dp to pixel Resources r = MainApp.getAppContext().getResources(); 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 + * * @param imageKey: thumb key * @param bitmap: image for extracting thumbnail * @param path: image path - * @param px: thumbnail dp + * @param pxW: thumbnail width in pixel + * @param pxH: thumbnail height in pixel * @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 - thumbnail = BitmapUtils.rotateImage(thumbnail,path); + thumbnail = BitmapUtils.rotateImage(thumbnail, path); // Add thumbnail to cache addBitmapToCache(imageKey, thumbnail); return thumbnail; } - - public static void addBitmapToCache(String key, Bitmap bitmap) { + + private static void addBitmapToCache(String key, Bitmap bitmap) { synchronized (mThumbnailsDiskCacheLock) { if (mThumbnailCache != null) { mThumbnailCache.put(key, bitmap); @@ -183,7 +207,180 @@ public static Bitmap getBitmapFromDiskCache(String key) { return null; } - public static class ThumbnailGenerationTask extends AsyncTask { + 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 = "r" + 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; @@ -201,7 +398,7 @@ public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager stora Account account, ArrayList asyncTasks) throws IllegalArgumentException { // Use a WeakReference to ensure the ImageView can be garbage collected - mImageViewReference = new WeakReference(imageView); + mImageViewReference = new WeakReference<>(imageView); if (storageManager == null) { throw new IllegalArgumentException("storageManager must not be NULL"); } @@ -214,7 +411,7 @@ public GetMethod getGetMethod() { return getMethod; } - public ThumbnailGenerationTask(FileDataStorageManager storageManager, Account account){ + public ThumbnailGenerationTask(FileDataStorageManager storageManager, Account account) { if (storageManager == null) { throw new IllegalArgumentException("storageManager must not be NULL"); } @@ -225,12 +422,12 @@ public ThumbnailGenerationTask(FileDataStorageManager storageManager, Account ac public ThumbnailGenerationTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can be garbage collected - mImageViewReference = new WeakReference(imageView); + mImageViewReference = new WeakReference<>(imageView); } @SuppressFBWarnings("Dm") @Override - protected Bitmap doInBackground(Object... params) { + protected Bitmap doInBackground(ThumbnailGenerationTaskObject... params) { Bitmap thumbnail = null; try { @@ -243,13 +440,12 @@ protected Bitmap doInBackground(Object... params) { 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); @@ -266,7 +462,7 @@ protected Bitmap doInBackground(Object... params) { //} else { do nothing } - } catch(OutOfMemoryError oome) { + } catch (OutOfMemoryError oome) { System.gc(); } catch (Throwable t) { // the app should never break due to a problem with thumbnails @@ -276,15 +472,15 @@ protected Bitmap doInBackground(Object... params) { return thumbnail; } - protected void onPostExecute(Bitmap bitmap){ + protected void onPostExecute(Bitmap bitmap) { if (bitmap != null && mImageViewReference != null) { final ImageView imageView = mImageViewReference.get(); final ThumbnailGenerationTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (this == bitmapWorkerTask) { String tagId = ""; - if (mFile instanceof OCFile){ - tagId = String.valueOf(((OCFile)mFile).getFileId()); - } else if (mFile instanceof File){ + if (mFile instanceof OCFile) { + tagId = String.valueOf(((OCFile) mFile).getFileId()); + } else if (mFile instanceof File) { tagId = String.valueOf(mFile.hashCode()); } if (String.valueOf(imageView.getTag()).equals(tagId)) { @@ -298,42 +494,31 @@ protected void onPostExecute(Bitmap bitmap){ } } - /** - * 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 = "t" + 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); @@ -346,10 +531,12 @@ private Bitmap doOCFileInBackground() { 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"); @@ -361,20 +548,22 @@ private Bitmap doOCFileInBackground() { 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 { @@ -393,8 +582,20 @@ private Bitmap doOCFileInBackground() { } + /** + * 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; + File file = (File) mFile; final String imageKey; if (mImageKey != null) { @@ -403,19 +604,22 @@ private Bitmap doFileInBackground() { imageKey = String.valueOf(file.hashCode()); } + // local file should always generate a thumbnail + mImageKey = "t" + 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; @@ -512,7 +716,7 @@ private Bitmap doFileInBackground(File file, Type type) { 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(); @@ -539,7 +743,7 @@ private Bitmap doFileInBackground(File file, Type type) { 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); } } } @@ -582,9 +786,9 @@ protected Bitmap doInBackground(String... params) { mUsername = params[0]; thumbnail = doAvatarInBackground(); - } catch(OutOfMemoryError oome) { + } catch (OutOfMemoryError oome) { System.gc(); // todo, does this really make sense? - } catch(Throwable t){ + } catch (Throwable t) { // the app should never break due to a problem with avatars Log_OC.e(TAG, "Generation of avatar for " + mUsername + " failed", t); } @@ -598,37 +802,17 @@ protected void onPostExecute(Bitmap bitmap) { AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(mCallContext); if (this == avatarWorkerTask && listener.shouldCallGeneratedCallback(mUsername, mCallContext)) { - listener.avatarGenerated(new BitmapDrawable(bitmap), mCallContext); - } + listener.avatarGenerated(new BitmapDrawable(bitmap), mCallContext); + } } } - /** - * 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 */ - private int getAvatarDimension(){ + private int getAvatarDimension() { // Converts dp to pixel Resources r = MainApp.getAppContext().getResources(); return Math.round(r.getDimension(R.dimen.file_avatar_size)); @@ -710,13 +894,13 @@ public static boolean cancelPotentialAvatarWork(Object file, Object callContext) if (callContext instanceof ImageView) { return cancelPotentialAvatarWork(file, (ImageView) callContext); } else if (callContext instanceof MenuItem) { - return cancelPotentialAvatarWork(file, (MenuItem)callContext); + return cancelPotentialAvatarWork(file, (MenuItem) callContext); } return false; } - public static boolean cancelPotentialAvatarWork(Object file, ImageView imageView) { + private static boolean cancelPotentialAvatarWork(Object file, ImageView imageView) { final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(imageView); if (avatarWorkerTask != null) { @@ -735,7 +919,7 @@ public static boolean cancelPotentialAvatarWork(Object file, ImageView imageView return true; } - public static boolean cancelPotentialAvatarWork(Object file, MenuItem menuItem) { + private static boolean cancelPotentialAvatarWork(Object file, MenuItem menuItem) { final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(menuItem); if (avatarWorkerTask != null) { @@ -754,7 +938,7 @@ public static boolean cancelPotentialAvatarWork(Object file, MenuItem menuItem) return true; } - public static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { + private static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); if (drawable instanceof AsyncThumbnailDrawable) { @@ -765,7 +949,18 @@ public static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { return null; } - public static Bitmap addVideoOverlay(Bitmap thumbnail){ + 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); @@ -787,15 +982,15 @@ public static Bitmap addVideoOverlay(Bitmap thumbnail){ int x3 = 0; int y3 = 0; - double ym = ( ((Math.pow(x3,2) - Math.pow(x1,2) + Math.pow(y3,2) - Math.pow(y1,2)) * - (x2 - x1)) - (Math.pow(x2,2) - Math.pow(x1,2) + Math.pow(y2,2) - - Math.pow(y1,2)) * (x3 - x1) ) / (2 * ( ((y3 - y1) * (x2 - x1)) - - ((y2 - y1) * (x3 - x1)) )); - double xm = ( (Math.pow(x2,2) - Math.pow(x1,2)) + (Math.pow(y2,2) - Math.pow(y1,2)) - - (2*ym*(y2 - y1)) ) / (2*(x2 - x1)); + double ym = (((Math.pow(x3, 2) - Math.pow(x1, 2) + Math.pow(y3, 2) - Math.pow(y1, 2)) * + (x2 - x1)) - (Math.pow(x2, 2) - Math.pow(x1, 2) + Math.pow(y2, 2) - + Math.pow(y1, 2)) * (x3 - x1)) / (2 * (((y3 - y1) * (x2 - x1)) - + ((y2 - y1) * (x3 - x1)))); + double xm = ((Math.pow(x2, 2) - Math.pow(x1, 2)) + (Math.pow(y2, 2) - Math.pow(y1, 2)) - + (2 * ym * (y2 - y1))) / (2 * (x2 - x1)); // offset to top left - double ox = - xm; + double ox = -xm; c.drawBitmap(thumbnail, 0, 0, null); @@ -809,11 +1004,11 @@ public static Bitmap addVideoOverlay(Bitmap thumbnail){ return resultBitmap; } - public static AvatarGenerationTask getAvatarWorkerTask(Object callContext) { + private static AvatarGenerationTask getAvatarWorkerTask(Object callContext) { if (callContext instanceof ImageView) { - return getAvatarWorkerTask(((ImageView)callContext).getDrawable()); + return getAvatarWorkerTask(((ImageView) callContext).getDrawable()); } else if (callContext instanceof MenuItem) { - return getAvatarWorkerTask(((MenuItem)callContext).getIcon()); + return getAvatarWorkerTask(((MenuItem) callContext).getIcon()); } return null; @@ -838,27 +1033,35 @@ public AsyncThumbnailDrawable( bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); } - public ThumbnailGenerationTask getBitmapWorkerTask() { + private ThumbnailGenerationTask getBitmapWorkerTask() { return bitmapWorkerTaskReference.get(); } } - 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; @@ -867,16 +1070,15 @@ public AsyncAvatarDrawable( ) { super(res, bitmap); - avatarWorkerTaskReference = - new WeakReference(avatarWorkerTask); + avatarWorkerTaskReference = new WeakReference<>(avatarWorkerTask); } - public AvatarGenerationTask getAvatarWorkerTask() { + private AvatarGenerationTask getAvatarWorkerTask() { return avatarWorkerTaskReference.get(); } } - private static Bitmap handlePNG(Bitmap bitmap, int px){ + private static Bitmap handlePNG(Bitmap bitmap, int px) { Bitmap resultBitmap = Bitmap.createBitmap(px, px, Bitmap.Config.ARGB_8888); @@ -888,4 +1090,22 @@ private static Bitmap handlePNG(Bitmap bitmap, int px){ return resultBitmap; } -} + + public static void generateResizedImage(OCFile file) { + Point p = getScreenDimension(); + int pxW = p.x; + int pxH = p.y; + String imageKey = "r" + 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/db/PreferenceManager.java b/src/main/java/com/owncloud/android/db/PreferenceManager.java index dc7c6195f639..0344d0ad3c42 100644 --- a/src/main/java/com/owncloud/android/db/PreferenceManager.java +++ b/src/main/java/com/owncloud/android/db/PreferenceManager.java @@ -1,18 +1,18 @@ -/** +/* * ownCloud Android client application * * @author David A. Velasco * Copyright (C) 2016 ownCloud Inc. - *

+ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. - *

+ * * 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 General Public License for more details. - *

+ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ 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 7e1d4de507a9..b08c7b9fd8ef 100644 --- a/src/main/java/com/owncloud/android/files/services/FileUploader.java +++ b/src/main/java/com/owncloud/android/files/services/FileUploader.java @@ -1059,11 +1059,10 @@ public void uploadFile(String uploadKey) { 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/jobs/AccountRemovalJob.java b/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java index 97024961f500..6f6a5b9e5077 100644 --- a/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java +++ b/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java @@ -59,7 +59,7 @@ protected Result onRunJob(Params params) { PersistableBundleCompat bundle = params.getExtras(); Account account = AccountUtils.getOwnCloudAccountByName(context, bundle.getString(ACCOUNT, "")); - if (account != null ) { + if (account != null) { AccountManager am = (AccountManager) context.getSystemService(ACCOUNT_SERVICE); am.removeAccount(account, this, null); diff --git a/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java b/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java index 8f7067df2781..a07113f91662 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 @@ protected RemoteOperationResult run(OwnCloudClient client) { 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 0e4d343aa496..7f145f76f61d 100644 --- a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -888,7 +888,7 @@ private void saveUploadedFile(OwnCloudClient client) { // 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 000000000000..f3904330549e --- /dev/null +++ b/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java @@ -0,0 +1,141 @@ +/* + * 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("r" + ocFile.getRemoteId())); + + // fallback to thumbnail + if (thumbnail == null) { + thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(String.valueOf("t" + 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/activity/ActivitiesListActivity.java b/src/main/java/com/owncloud/android/ui/activity/ActivitiesListActivity.java index 8e93e01eb40c..961e6daedcf0 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ActivitiesListActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ActivitiesListActivity.java @@ -26,8 +26,8 @@ import android.accounts.OperationCanceledException; import android.content.Context; import android.content.Intent; -import android.os.AsyncTask; import android.graphics.PorterDuff; +import android.os.AsyncTask; import android.os.Bundle; import android.support.design.widget.BottomNavigationView; import android.support.v4.widget.SwipeRefreshLayout; diff --git a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 4042fb0eb0c3..4bfc72efe4c4 100644 --- a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -618,7 +618,7 @@ public void updateAccountList() { ArrayList persistingAccounts = new ArrayList<>(); - for (Account acc: accounts) { + for (Account acc : accounts) { boolean pendingForRemoval = arbitraryDataProvider.getBooleanValue(acc, ManageAccountsActivity.PENDING_FOR_REMOVAL); @@ -669,7 +669,7 @@ private void repopulateAccountList(ArrayList accounts) { mNavigationView.getMenu().removeGroup(R.id.drawer_menu_accounts); // add all accounts to list - for (Account account: accounts) { + for (Account account : accounts) { try { // show all accounts except the currently active one and those pending for removal @@ -1207,7 +1207,7 @@ private void populateDrawerOwnCloudAccounts() { ArrayList persistingAccounts = new ArrayList<>(); - for (Account acc: accountsAll) { + for (Account acc : accountsAll) { boolean pendingForRemoval = arbitraryDataProvider.getBooleanValue(acc, ManageAccountsActivity.PENDING_FOR_REMOVAL); diff --git a/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 5f1b673420b5..d9ea5eacfa2d 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -432,7 +432,7 @@ private void performAccountRemoval(Account account) { Account[] accounts = AccountManager.get(this).getAccountsByType(MainApp.getAccountType()); String newAccountName = ""; - for (Account acc: accounts) { + for (Account acc : accounts) { if (!account.name.equalsIgnoreCase(acc.name)) { newAccountName = acc.name; break; diff --git a/src/main/java/com/owncloud/android/ui/activity/Preferences.java b/src/main/java/com/owncloud/android/ui/activity/Preferences.java index b26d11d8ddd5..b00efa9bdb2c 100644 --- a/src/main/java/com/owncloud/android/ui/activity/Preferences.java +++ b/src/main/java/com/owncloud/android/ui/activity/Preferences.java @@ -1,4 +1,4 @@ -/** +/* * ownCloud Android client application * * @author Bartek Przybylski diff --git a/src/main/java/com/owncloud/android/ui/adapter/DiskLruImageCache.java b/src/main/java/com/owncloud/android/ui/adapter/DiskLruImageCache.java index e4ab19e0b46f..3042d83ceaa0 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/DiskLruImageCache.java +++ b/src/main/java/com/owncloud/android/ui/adapter/DiskLruImageCache.java @@ -121,10 +121,10 @@ public Bitmap getBitmap( String key ) { } final InputStream in = snapshot.getInputStream( 0 ); if ( in != null ) { - final BufferedInputStream buffIn = + final BufferedInputStream buffIn = new BufferedInputStream( in, IO_BUFFER_SIZE ); - bitmap = BitmapFactory.decodeStream( buffIn ); - } + bitmap = BitmapFactory.decodeStream( buffIn ); + } } catch ( IOException e ) { Log_OC.d(TAG, e.getMessage(), e); } finally { 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 ce42214e3509..8e706be8cc28 100755 --- a/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java @@ -416,7 +416,8 @@ public void onClick(View v) { task ); fileIcon.setImageDrawable(asyncDrawable); - task.execute(fakeFileToCheatThumbnailsCacheManagerInterface); + task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject( + fakeFileToCheatThumbnailsCacheManagerInterface, null)); } } @@ -452,7 +453,7 @@ public void onClick(View v) { 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 24b220acad80..d8fb44c15302 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java @@ -369,7 +369,9 @@ public void onClick(View view) { 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( + "t" + String.valueOf(file.getRemoteId()) + ); if (thumbnail != null && !file.needsUpdateThumbnail()) { if (MimeTypeUtil.isVideo(file)) { @@ -385,7 +387,6 @@ public void onClick(View view) { final ThumbnailsCacheManager.ThumbnailGenerationTask task = new ThumbnailsCacheManager.ThumbnailGenerationTask( fileIcon, mStorageManager, mAccount, asyncTasks); - if (thumbnail == null) { if (MimeTypeUtil.isVideo(file)) { thumbnail = ThumbnailsCacheManager.mDefaultVideo; @@ -401,7 +402,8 @@ public void onClick(View view) { ); 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 01a683642318..eb6a72ecc4dc 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java @@ -203,7 +203,7 @@ public View getView(int position, View convertView, ViewGroup parent) { if (MimeTypeUtil.isImage(file)){ // Thumbnail in Cache? Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(file.hashCode()) + "t" + String.valueOf(file.hashCode()) ); if (thumbnail != null){ fileIcon.setImageBitmap(thumbnail); @@ -225,7 +225,7 @@ public View getView(int position, View convertView, ViewGroup parent) { 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 ebedb42c718e..3346d08d22ea 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 View getView(int position, View convertView, ViewGroup parent) { 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 48ac205dbc7e..22c907fb4611 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -329,17 +329,16 @@ public boolean onOptionsItemSelected(MenuItem item) { 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 { - mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile()); + // 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 { + mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile()); + } + return true; } - return true; - } default: return super.onOptionsItemSelected(item); } @@ -506,7 +505,7 @@ private void setFiletype(OCFile file) { 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 6c3532f67210..5115127be164 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -937,14 +937,21 @@ public boolean onFileActionChosen(int menuId) { 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 84f7b3e4f1be..544b93b15316 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.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; @@ -526,6 +527,21 @@ public void syncFiles(Collection files) { } } + 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 e502cfa770a0..467d1cd7dda0 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 void setGIFImageFromStoragePath(String storagePath) { } } + 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 e51e89a1ed44..82d97cf4e87f 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java @@ -26,6 +26,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.SharedPreferences; import android.os.Bundle; import android.os.IBinder; import android.support.v4.view.ViewPager; @@ -41,6 +42,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.VirtualFolderType; +import com.owncloud.android.db.PreferenceManager; import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader; @@ -121,13 +123,13 @@ public void onSystemUiVisibilityChange(int flags) { } } }); - + if (savedInstanceState != null) { mRequestWaitingForBinder = savedInstanceState.getBoolean(KEY_WAITING_FOR_BINDER); } else { mRequestWaitingForBinder = false; } - + } private void initViewPager() { @@ -149,7 +151,7 @@ private void initViewPager() { } mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), - parentFolder, getAccount(), getStorageManager(), MainApp.isOnlyOnDevice()); + parentFolder, getAccount(), getStorageManager(), MainApp.isOnlyOnDevice(), getBaseContext()); } mViewPager = (ExtendedViewPager) findViewById(R.id.fragmentPager); @@ -325,7 +327,7 @@ public void showDetails(OCFile file) { 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"); @@ -349,13 +351,16 @@ public void onPageSelected(int position) { mHasSavedPosition = true; if (mDownloaderBinder == null) { mRequestWaitingForBinder = true; - } else { OCFile currentFile = mPreviewImagePagerAdapter.getFileAt(position); getSupportActionBar().setTitle(currentFile.getFileName()); setDrawerIndicatorEnabled(false); - if (!currentFile.isDown() - && !mPreviewImagePagerAdapter.pendingErrorAt(position)) { + + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getBaseContext()); + String behaviour = sharedPreferences.getString("preview_behaviour", "PREVIEW_BEHAVIOUR_PREVIEW"); + boolean resizedBehaviour = behaviour.equalsIgnoreCase("PREVIEW_BEHAVIOUR_PREVIEW"); + + if (!currentFile.isDown() && !mPreviewImagePagerAdapter.pendingErrorAt(position) && !resizedBehaviour) { requestForDownload(currentFile); } @@ -456,6 +461,10 @@ public void toggleFullScreen() { } } + public void switchToFullScreen() { + hideSystemUI(mFullScreenAnchorView); + } + @Override protected void onAccountSet(boolean stateWasRecovered) { super.onAccountSet(stateWasRecovered); @@ -473,7 +482,7 @@ protected void onAccountSet(boolean stateWasRecovered) { if (file.getFileId() > FileDataStorageManager.ROOT_PARENT_ID) { file = getStorageManager().getFileById(file.getFileId()); } - + if (file != null) { /// Refresh the activity according to the Account and OCFile set setFile(file); // reset after getting it fresh from storageManager 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 961f6882d613..25ddb68000fb 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java @@ -35,6 +35,7 @@ import android.os.Process; import android.support.annotation.DrawableRes; 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; @@ -52,8 +53,10 @@ 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; @@ -87,7 +90,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; @@ -99,6 +102,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(); @@ -121,11 +126,14 @@ public class PreviewImageFragment extends FileFragment { * {@link FragmentStatePagerAdapter} * ; TODO better solution */ - public static PreviewImageFragment newInstance(OCFile imageFile, boolean ignoreFirstSavedState) { + public static PreviewImageFragment newInstance(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; } @@ -157,6 +165,7 @@ public void onCreate(Bundle savedInstanceState) { // not right now mIgnoreFirstSavedState = args.getBoolean(ARG_IGNORE_FIRST); + mShowResizedImage = args.getBoolean(ARG_SHOW_RESIZED_IMAGE); setHasOptionsMenu(true); } @@ -170,6 +179,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, 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() { @@ -196,6 +206,14 @@ public void onClick(View v) { 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); @@ -221,9 +239,6 @@ public void onActivityCreated(Bundle savedInstanceState) { if (getFile() == null) { throw new IllegalStateException("Instanced with a NULL OCFile"); } - if (!getFile().isDown()) { - throw new IllegalStateException("There is no local file to preview"); - } } @@ -241,10 +256,60 @@ public void onSaveInstanceState(Bundle outState) { public void onStart() { super.onStart(); if (getFile() != null) { - mLoadBitmapTask = new LoadBitmapTask(mImageView); - //mLoadBitmapTask.execute(new String[]{getFile().getStoragePath()}); -// mLoadBitmapTask.execute(getFile().getStoragePath()); - mLoadBitmapTask.execute(getFile()); + mImageView.setTag(getFile().getFileId()); + + if (mShowResizedImage) { + Bitmap resizedImage = ThumbnailsCacheManager.getBitmapFromDiskCache( + String.valueOf("r" + 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("t" + 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()); + } } } @@ -368,9 +433,15 @@ public boolean onOptionsItemSelected(MenuItem item) { 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; @@ -661,6 +732,24 @@ public void setMessageForMultiList(@StringRes int headline, @StringRes int messa } } + 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. @@ -687,19 +776,22 @@ private void toggleImageBackground() { 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; - layerDrawable.setDrawableByLayerId(layerDrawable.getId(0), 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(); + 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 f5eb0568fb73..9cf050f60df1 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,8 @@ package com.owncloud.android.ui.preview; import android.accounts.Account; +import android.content.Context; +import android.content.SharedPreferences; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; @@ -28,6 +30,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.VirtualFolderType; +import com.owncloud.android.db.PreferenceManager; import com.owncloud.android.ui.fragment.FileFragment; import com.owncloud.android.utils.FileStorageUtils; @@ -50,7 +53,8 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { private Set mObsoletePositions; private Set mDownloadErrors; private FileDataStorageManager mStorageManager; - + private Context mContext; + private Map mCachedFragments; /** @@ -63,7 +67,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 +80,7 @@ public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFo throw new IllegalArgumentException("NULL storage manager"); } + mContext = context; mAccount = account; mStorageManager = storageManager; mImageFiles = mStorageManager.getFolderImages(parentFolder, onlyOnDevice); @@ -140,20 +145,27 @@ public Fragment getItem(int i) { OCFile file = mImageFiles.get(i); Fragment fragment = null; if (file.isDown()) { - fragment = PreviewImageFragment.newInstance(file, - mObsoletePositions.contains(Integer.valueOf(i))); - - } else if (mDownloadErrors.contains(Integer.valueOf(i))) { - fragment = FileDownloadFragment.newInstance(file, mAccount, true); - ((FileDownloadFragment)fragment).setError(true); - mDownloadErrors.remove(Integer.valueOf(i)); + fragment = PreviewImageFragment.newInstance(file, mObsoletePositions.contains(i), false); } else { - fragment = FileDownloadFragment.newInstance( - file, mAccount, mObsoletePositions.contains(Integer.valueOf(i)) - ); + if (mDownloadErrors.contains(i)) { + fragment = FileDownloadFragment.newInstance(file, mAccount, true); + ((FileDownloadFragment)fragment).setError(true); + mDownloadErrors.remove(i); + } else { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); + boolean behaviour = sharedPreferences.getString("preview_behaviour", "PREVIEW_BEHAVIOUR_PREVIEW") + .equalsIgnoreCase("PREVIEW_BEHAVIOUR_PREVIEW"); + + if (behaviour) { + fragment = PreviewImageFragment.newInstance(file, mObsoletePositions.contains(i), true); + } else { + fragment = FileDownloadFragment.newInstance(file, mAccount, mObsoletePositions.contains(i)); + } + } } - mObsoletePositions.remove(Integer.valueOf(i)); + + mObsoletePositions.remove(i); return fragment; } diff --git a/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java b/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java index b45b9be79176..8714f0c62889 100644 --- a/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java +++ b/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java @@ -135,8 +135,8 @@ public static Drawable getFolderTypeIcon(boolean isSharedViaUsers, boolean isSha * Returns the resource identifier of an image to use as icon associated to a type of folder. * * @param isSharedViaUsers flag if the folder is shared via the users system - * @param isSharedViaLink flag if the folder is publicly shared via link - * @param account account which color should be used + * @param isSharedViaLink flag if the folder is publicly shared via link + * @param account account which color should be used * @return Identifier of an image resource. */ public static Drawable getFolderTypeIcon(boolean isSharedViaUsers, boolean isSharedViaLink, Account account) { diff --git a/src/main/java/com/owncloud/android/utils/ThemeUtils.java b/src/main/java/com/owncloud/android/utils/ThemeUtils.java index 6a0eb33d8258..69d5e62e43bb 100644 --- a/src/main/java/com/owncloud/android/utils/ThemeUtils.java +++ b/src/main/java/com/owncloud/android/utils/ThemeUtils.java @@ -64,7 +64,7 @@ public static int primaryAccentColor() { try { float adjust; - if (darkTheme()){ + if (darkTheme()) { adjust = +0.1f; } else { adjust = -0.1f; @@ -121,6 +121,7 @@ public static int fontColor() { /** * Tests if dark color is set + * * @return true if dark theme -> e.g.use light font color, darker accent color */ public static boolean darkTheme() { diff --git a/src/main/java/com/owncloud/android/widgets/ActionEditText.java b/src/main/java/com/owncloud/android/widgets/ActionEditText.java index 600711a137d4..c3680641a025 100644 --- a/src/main/java/com/owncloud/android/widgets/ActionEditText.java +++ b/src/main/java/com/owncloud/android/widgets/ActionEditText.java @@ -74,7 +74,7 @@ protected void onDraw(Canvas canvas) { getDrawingRect(mButtonRect); mButtonRect.top += 10; mButtonRect.bottom -= 10; - mButtonRect.left = (int) (getWidth() - mTextBounds.width() - 18); + mButtonRect.left = getWidth() - mTextBounds.width() - 18; mButtonRect.right = getWidth() - 10; btn_rect = mButtonRect; diff --git a/src/main/java/third_parties/michaelOrtiz/TouchImageViewCustom.java b/src/main/java/third_parties/michaelOrtiz/TouchImageViewCustom.java index 9b3edbe02861..9c2ef807f546 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_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; @@ -42,7 +43,7 @@ */ public class TouchImageViewCustom extends ImageViewCustom { private static final String DEBUG = "DEBUG"; - + // // SuperMin and SuperMax multipliers. Determine how much the image can be // zoomed below or above the zoom boundaries, before animating back to the @@ -56,7 +57,7 @@ public class TouchImageViewCustom extends ImageViewCustom { // when the image is stretched to fit view. // private float normalizedScale; - + // // Matrix applied to image. MSCALE_X and MSCALE_Y should always be equal. // MTRANS_X and MTRANS_Y are the other values used. prevMatrix is the matrix @@ -64,7 +65,7 @@ public class TouchImageViewCustom extends ImageViewCustom { // private Matrix matrix, prevMatrix; - private static enum State { NONE, DRAG, ZOOM, FLING, ANIMATE_ZOOM } + private enum State {NONE, DRAG, ZOOM, FLING, ANIMATE_ZOOM} private State state; private float minScale; @@ -72,27 +73,27 @@ private static enum State { NONE, DRAG, ZOOM, FLING, ANIMATE_ZOOM } private float superMinScale; private float superMaxScale; private float[] m; - + private Context context; private Fling fling; - + private ScaleType mScaleType; - + private boolean imageRenderedAtLeastOnce; private boolean onDrawReady; - + private ZoomVariables delayedZoomVariables; // // Size of view and previous view size (ie before rotation) // private int viewWidth, viewHeight, prevViewWidth, prevViewHeight; - + // // Size of image when it is stretched to fit view. Before and After rotation. // private float matchViewWidth, matchViewHeight, prevMatchViewWidth, prevMatchViewHeight; - + private ScaleGestureDetector mScaleDetector; private GestureDetector mGestureDetector; private GestureDetector.OnDoubleTapListener doubleTapListener = null; @@ -108,12 +109,12 @@ public TouchImageViewCustom(Context context, AttributeSet attrs) { super(context, attrs); sharedConstructing(context); } - + public TouchImageViewCustom(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); sharedConstructing(context); } - + private void sharedConstructing(Context context) { super.setClickable(true); this.context = context; @@ -141,7 +142,7 @@ private void sharedConstructing(Context context) { public void setOnTouchListener(View.OnTouchListener l) { userTouchListener = l; } - + public void setOnTouchImageViewListener(OnTouchImageViewListener l) { touchImageViewListener = l; } @@ -156,28 +157,28 @@ public void setImageResource(int resId) { savePreviousImageValues(); fitImageToView(); } - + @Override public void setImageBitmap(Bitmap bm) { super.setImageBitmap(bm); savePreviousImageValues(); fitImageToView(); } - + @Override public void setImageDrawable(Drawable drawable) { super.setImageDrawable(drawable); savePreviousImageValues(); fitImageToView(); } - + @Override public void setImageURI(Uri uri) { super.setImageURI(uri); savePreviousImageValues(); fitImageToView(); } - + @Override public void setScaleType(ScaleType type) { if (type == ScaleType.FIT_START || type == ScaleType.FIT_END) { @@ -185,7 +186,7 @@ public void setScaleType(ScaleType type) { } if (type == ScaleType.MATRIX) { super.setScaleType(ScaleType.MATRIX); - + } else { mScaleType = type; if (onDrawReady) { @@ -197,12 +198,12 @@ public void setScaleType(ScaleType type) { } } } - + @Override public ScaleType getScaleType() { return mScaleType; } - + /** * Returns false if image is in initial, unzoomed state. False, otherwise. * @return true if image is zoomed @@ -210,7 +211,7 @@ public ScaleType getScaleType() { public boolean isZoomed() { return normalizedScale != 1; } - + /** * Return a Rect representing the zoomed image. * @return rect representing zoomed image @@ -221,12 +222,12 @@ public RectF getZoomedRect() { } PointF topLeft = transformCoordTouchToBitmap(0, 0, true); PointF bottomRight = transformCoordTouchToBitmap(viewWidth, viewHeight, true); - + float w = getDrawable().getIntrinsicWidth(); float h = getDrawable().getIntrinsicHeight(); return new RectF(topLeft.x / w, topLeft.y / h, bottomRight.x / w, bottomRight.y / h); } - + /** * Save the current matrix and view dimensions * in the prevMatrix and prevView variables. @@ -241,7 +242,7 @@ private void savePreviousImageValues() { prevViewWidth = viewWidth; } } - + @Override public Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); @@ -256,7 +257,7 @@ public Parcelable onSaveInstanceState() { bundle.putBoolean("imageRendered", imageRenderedAtLeastOnce); return bundle; } - + @Override public void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { @@ -275,7 +276,7 @@ public void onRestoreInstanceState(Parcelable state) { super.onRestoreInstanceState(state); } - + @Override protected void onDraw(Canvas canvas) { onDrawReady = true; @@ -286,13 +287,13 @@ protected void onDraw(Canvas canvas) { } super.onDraw(canvas); } - + @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); savePreviousImageValues(); } - + /** * Get the max zoom multiplier. * @return max zoom multiplier. @@ -309,7 +310,7 @@ public void setMaxZoom(float max) { maxScale = max; superMaxScale = SUPER_MAX_MULTIPLIER * maxScale; } - + /** * Get the min zoom multiplier. * @return min zoom multiplier. @@ -317,7 +318,7 @@ public void setMaxZoom(float max) { public float getMinZoom() { return minScale; } - + /** * Get the current zoom. This is the zoom relative to the initial * scale, not the original resource. @@ -326,7 +327,7 @@ public float getMinZoom() { public float getCurrentZoom() { return normalizedScale; } - + /** * Set the min zoom multiplier. Default value: 1. * @param min min zoom multiplier. @@ -335,7 +336,7 @@ public void setMinZoom(float min) { minScale = min; superMinScale = SUPER_MIN_MULTIPLIER * minScale; } - + /** * Reset zoom and translation to initial state. */ @@ -343,7 +344,7 @@ public void resetZoom() { normalizedScale = 1; fitImageToView(); } - + /** * Set zoom to the specified scale. Image will be centered by default. * @param scale @@ -351,7 +352,7 @@ public void resetZoom() { public void setZoom(float scale) { setZoom(scale, 0.5f, 0.5f); } - + /** * Set zoom to the specified scale. Image will be centered around the point * (focusX, focusY). These floats range from 0 to 1 and denote the focus point @@ -364,7 +365,7 @@ public void setZoom(float scale) { public void setZoom(float scale, float focusX, float focusY) { setZoom(scale, focusX, focusY, mScaleType); } - + /** * Set zoom to the specified scale. Image will be centered around the point * (focusX, focusY). These floats range from 0 to 1 and denote the focus point @@ -377,7 +378,7 @@ public void setZoom(float scale, float focusX, float focusY) { */ public void setZoom(float scale, float focusX, float focusY, ScaleType scaleType) { // - // setZoom can be called before the image is on the screen, but at this point, + // setZoom can be called before the image is on the screen, but at this point, // image and view sizes have not yet been calculated in onMeasure. Thus, we should // delay calling setZoom until the view has been measured. // @@ -385,7 +386,7 @@ public void setZoom(float scale, float focusX, float focusY, ScaleType scaleType delayedZoomVariables = new ZoomVariables(scale, focusX, focusY, scaleType); return; } - + if (!scaleType.equals(mScaleType)) { setScaleType(scaleType); } @@ -398,7 +399,7 @@ public void setZoom(float scale, float focusX, float focusY, ScaleType scaleType fixTrans(); setImageMatrix(matrix); } - + /** * Set zoom parameters equal to another TouchImageView. Including scale, position, * and ScaleType. @@ -408,7 +409,7 @@ public void setZoom(TouchImageViewCustom img) { PointF center = img.getScrollPosition(); setZoom(img.getCurrentZoom(), center.x, center.y, img.getScaleType()); } - + /** * Return the point at the center of the zoomed image. The PointF coordinates range * in value between 0 and 1 and the focus point is denoted as a fraction from the left @@ -423,13 +424,13 @@ public PointF getScrollPosition() { } int drawableWidth = drawable.getIntrinsicWidth(); int drawableHeight = drawable.getIntrinsicHeight(); - + PointF point = transformCoordTouchToBitmap(viewWidth / 2, viewHeight / 2, true); point.x /= drawableWidth; point.y /= drawableHeight; return point; } - + /** * Set the focus point of the zoomed image. The focus points are denoted as a fraction from the * left and top of the view. The focus points can range in value between 0 and 1. @@ -439,7 +440,7 @@ public PointF getScrollPosition() { public void setScrollPosition(float focusX, float focusY) { setZoom(normalizedScale, focusX, focusY); } - + /** * Performs boundary checking and fixes the image matrix if it * is out of bounds. @@ -448,15 +449,15 @@ private void fixTrans() { matrix.getValues(m); float transX = m[Matrix.MTRANS_X]; float transY = m[Matrix.MTRANS_Y]; - + float fixTransX = getFixTrans(transX, viewWidth, getImageWidth()); float fixTransY = getFixTrans(transY, viewHeight, getImageHeight()); - + if (fixTransX != 0 || fixTransY != 0) { matrix.postTranslate(fixTransX, fixTransY); } } - + /** * When transitioning from zooming from focus to zoom from center (or vice versa) * the image can become unaligned within the view. This is apparent when zooming @@ -470,7 +471,7 @@ private void fixScaleTrans() { if (getImageWidth() < viewWidth) { m[Matrix.MTRANS_X] = (viewWidth - getImageWidth()) / 2; } - + if (getImageHeight() < viewHeight) { m[Matrix.MTRANS_Y] = (viewHeight - getImageHeight()) / 2; } @@ -483,7 +484,7 @@ private float getFixTrans(float trans, float viewSize, float contentSize) { if (contentSize <= viewSize) { minTrans = 0; maxTrans = viewSize - contentSize; - + } else { minTrans = viewSize - contentSize; maxTrans = 0; @@ -497,18 +498,18 @@ private float getFixTrans(float trans, float viewSize, float contentSize) { } return 0; } - + private float getFixDragTrans(float delta, float viewSize, float contentSize) { if (contentSize <= viewSize) { return 0; } return delta; } - + private float getImageWidth() { return matchViewWidth * normalizedScale; } - + private float getImageHeight() { return matchViewHeight * normalizedScale; } @@ -520,7 +521,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(0, 0); return; } - + int drawableWidth = drawable.getIntrinsicWidth(); int drawableHeight = drawable.getIntrinsicHeight(); int widthSize = MeasureSpec.getSize(widthMeasureSpec); @@ -529,18 +530,18 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int heightMode = MeasureSpec.getMode(heightMeasureSpec); viewWidth = setViewSize(widthMode, widthSize, drawableWidth); viewHeight = setViewSize(heightMode, heightSize, drawableHeight); - + // // Set view dimensions // setMeasuredDimension(viewWidth, viewHeight); - + // // Fit content within view // fitImageToView(); } - + /** * If the normalizedScale is equal to 1, then the image is made to fit the screen. Otherwise, * it is made to fit the screen according to the dimensions of the previous image matrix. This @@ -554,41 +555,41 @@ private void fitImageToView() { if (matrix == null || prevMatrix == null) { return; } - + int drawableWidth = drawable.getIntrinsicWidth(); int drawableHeight = drawable.getIntrinsicHeight(); - + // // Scale image for view // float scaleX = (float) viewWidth / drawableWidth; float scaleY = (float) viewHeight / drawableHeight; - + switch (mScaleType) { case CENTER: scaleX = scaleY = 1; break; - + case CENTER_CROP: scaleX = scaleY = Math.max(scaleX, scaleY); break; - + case CENTER_INSIDE: scaleX = scaleY = Math.min(1, Math.min(scaleX, scaleY)); - + case FIT_CENTER: scaleX = scaleY = Math.min(scaleX, scaleY); break; - + case FIT_XY: break; - + default: // // FIT_START and FIT_END not supported // throw new UnsupportedOperationException("TouchImageView does not support FIT_START or FIT_END"); - + } // @@ -605,7 +606,7 @@ private void fitImageToView() { matrix.setScale(scaleX, scaleY); matrix.postTranslate(redundantXSpace / 2, redundantYSpace / 2); normalizedScale = 1; - + } else { // // These values should never be 0 or we will set viewWidth and viewHeight @@ -615,35 +616,35 @@ private void fitImageToView() { if (prevMatchViewWidth == 0 || prevMatchViewHeight == 0) { savePreviousImageValues(); } - + prevMatrix.getValues(m); - + // // Rescale Matrix after rotation // m[Matrix.MSCALE_X] = matchViewWidth / drawableWidth * normalizedScale; m[Matrix.MSCALE_Y] = matchViewHeight / drawableHeight * normalizedScale; - + // // TransX and TransY from previous matrix // float transX = m[Matrix.MTRANS_X]; float transY = m[Matrix.MTRANS_Y]; - + // // Width // float prevActualWidth = prevMatchViewWidth * normalizedScale; float actualWidth = getImageWidth(); translateMatrixAfterRotate(Matrix.MTRANS_X, transX, prevActualWidth, actualWidth, prevViewWidth, viewWidth, drawableWidth); - + // // Height // float prevActualHeight = prevMatchViewHeight * normalizedScale; float actualHeight = getImageHeight(); translateMatrixAfterRotate(Matrix.MTRANS_Y, transY, prevActualHeight, actualHeight, prevViewHeight, viewHeight, drawableHeight); - + // // Set the matrix to the adjusted scale and translate values. // @@ -652,11 +653,11 @@ private void fitImageToView() { fixTrans(); setImageMatrix(matrix); } - + /** * Set view dimensions based on layout params - * - * @param mode + * + * @param mode * @param size * @param drawableWidth * @return @@ -667,26 +668,26 @@ private int setViewSize(int mode, int size, int drawableWidth) { case MeasureSpec.EXACTLY: viewSize = size; break; - + case MeasureSpec.AT_MOST: viewSize = Math.min(drawableWidth, size); break; - + case MeasureSpec.UNSPECIFIED: viewSize = drawableWidth; break; - + default: viewSize = size; break; } return viewSize; } - + /** * After rotating, the matrix needs to be translated. This function finds the area of image * which was previously centered and adjusts translations so that is again the center, post-rotation. - * + * * @param axis Matrix.MTRANS_X or Matrix.MTRANS_Y * @param trans the value of trans in that axis before the rotation * @param prevImageSize the width/height of the image before the rotation @@ -701,13 +702,13 @@ private void translateMatrixAfterRotate(int axis, float trans, float prevImageSi // The width/height of image is less than the view's width/height. Center it. // m[axis] = (viewSize - (drawableSize * m[Matrix.MSCALE_X])) * 0.5f; - + } else if (trans > 0) { // // The image is larger than the view, but was not before rotation. Center it. // m[axis] = -((imageSize - viewSize) * 0.5f); - + } else { // // Find the area of the image which was previously centered in the view. Determine its distance @@ -718,33 +719,33 @@ private void translateMatrixAfterRotate(int axis, float trans, float prevImageSi m[axis] = -((percentage * imageSize) - (viewSize * 0.5f)); } } - + private void setState(State state) { this.state = state; } - + public boolean canScrollHorizontallyFroyo(int direction) { return canScrollHorizontally(direction); } - + @Override public boolean canScrollHorizontally(int direction) { matrix.getValues(m); float x = m[Matrix.MTRANS_X]; - + if (getImageWidth() < viewWidth) { return false; - + } else if (x >= -1 && direction < 0) { return false; - + } else if (Math.abs(x) + viewWidth + 1 >= getImageWidth() && direction > 0) { return false; } - + return true; } - + /** * Gesture Listener detects a single click or long click and passes that on * to the view's listener. @@ -752,7 +753,7 @@ public boolean canScrollHorizontally(int direction) { * */ private class GestureListener extends GestureDetector.SimpleOnGestureListener { - + @Override public boolean onSingleTapConfirmed(MotionEvent e) { @@ -761,13 +762,13 @@ public boolean onSingleTapConfirmed(MotionEvent e) } return performClick(); } - + @Override public void onLongPress(MotionEvent e) { performLongClick(); } - + @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { @@ -782,7 +783,7 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve compatPostOnAnimation(fling); return super.onFling(e1, e2, velocityX, velocityY); } - + @Override public boolean onDoubleTap(MotionEvent e) { boolean consumed = false; @@ -806,11 +807,11 @@ public boolean onDoubleTapEvent(MotionEvent e) { return false; } } - + public interface OnTouchImageViewListener { - public void onMove(); + void onMove(); } - + /** * Responsible for all touch events. Handles the heavy lifting of drag and also sends * touch events to Scale Detector and Gesture Detector. @@ -818,18 +819,18 @@ public interface OnTouchImageViewListener { * */ private class PrivateOnTouchListener implements OnTouchListener { - + // // Remember last point position for dragging // private PointF last = new PointF(); - + @Override public boolean onTouch(View v, MotionEvent event) { mScaleDetector.onTouchEvent(event); mGestureDetector.onTouchEvent(event); PointF curr = new PointF(event.getX(), event.getY()); - + if (state == State.NONE || state == State.DRAG || state == State.FLING) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: @@ -839,7 +840,7 @@ public boolean onTouch(View v, MotionEvent event) { } setState(State.DRAG); break; - + case MotionEvent.ACTION_MOVE: if (state == State.DRAG) { float deltaX = curr.x - last.x; @@ -851,30 +852,30 @@ public boolean onTouch(View v, MotionEvent event) { last.set(curr.x, curr.y); } break; - + case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: setState(State.NONE); break; } } - + setImageMatrix(matrix); - + // // User-defined OnTouchListener // if(userTouchListener != null) { userTouchListener.onTouch(v, event); } - + // // OnTouchImageViewListener is set: TouchImageView dragged by user. // if (touchImageViewListener != null) { touchImageViewListener.onMove(); } - + // // indicate event was handled // @@ -888,16 +889,27 @@ public boolean onTouch(View v, MotionEvent event) { * */ private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { + private boolean snackShown = false; + +// SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(previewImageFragment.getContext()); +// String test = appPrefs.getString(Preferences.PreferenceKeys.STORAGE_PATH) + @Override public boolean onScaleBegin(ScaleGestureDetector detector) { setState(State.ZOOM); + + previewImageFragment.switchToFullScreen(); return true; } @Override 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. // @@ -906,7 +918,26 @@ public boolean onScale(ScaleGestureDetector detector) { } return true; } - + + private void showDownloadSnackbar() { + snackShown = true; + + Snackbar.make(getRootView(), "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("Yes", new OnClickListener() { + @Override + public void onClick(View v) { + previewImageFragment.downloadFile(); + } + }).show(); + } + @Override public void onScaleEnd(ScaleGestureDetector detector) { super.onScaleEnd(detector); @@ -916,31 +947,31 @@ public void onScaleEnd(ScaleGestureDetector detector) { if (normalizedScale > maxScale) { targetZoom = maxScale; animateToZoomBoundary = true; - + } else if (normalizedScale < minScale) { targetZoom = minScale; animateToZoomBoundary = true; } - + if (animateToZoomBoundary) { DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom, viewWidth / 2, viewHeight / 2, true); compatPostOnAnimation(doubleTap); } } } - + private void scaleImage(double deltaScale, float focusX, float focusY, boolean stretchImageToSuper) { - + float lowerScale, upperScale; if (stretchImageToSuper) { lowerScale = superMinScale; upperScale = superMaxScale; - + } else { lowerScale = minScale; upperScale = maxScale; } - + float origScale = normalizedScale; normalizedScale *= deltaScale; if (normalizedScale > upperScale) { @@ -950,11 +981,11 @@ private void scaleImage(double deltaScale, float focusX, float focusY, boolean s normalizedScale = lowerScale; deltaScale = lowerScale / origScale; } - + matrix.postScale((float) deltaScale, (float) deltaScale, focusX, focusY); fixScaleTrans(); } - + /** * DoubleTapZoom calls a series of runnables which apply * an animated zoom in/out graphic to the image. @@ -962,7 +993,7 @@ private void scaleImage(double deltaScale, float focusX, float focusY, boolean s * */ private class DoubleTapZoom implements Runnable { - + private long startTime; private static final float ZOOM_TIME = 500; private float startZoom, targetZoom; @@ -981,7 +1012,7 @@ private class DoubleTapZoom implements Runnable { PointF bitmapPoint = transformCoordTouchToBitmap(focusX, focusY, false); this.bitmapX = bitmapPoint.x; this.bitmapY = bitmapPoint.y; - + // // Used for translating image during scaling // @@ -997,7 +1028,7 @@ public void run() { translateImageToCenterTouchPosition(t); fixScaleTrans(); setImageMatrix(matrix); - + // // OnTouchImageViewListener is set: double tap runnable updates listener // with every frame. @@ -1005,13 +1036,13 @@ public void run() { if (touchImageViewListener != null) { touchImageViewListener.onMove(); } - + if (t < 1f) { // // We haven't finished zooming // compatPostOnAnimation(this); - + } else { // // Finished zooming @@ -1019,7 +1050,7 @@ public void run() { setState(State.NONE); } } - + /** * Interpolate between where the image should start and end in order to translate * the image so that the point that is touched is what ends up centered at the end @@ -1032,7 +1063,7 @@ private void translateImageToCenterTouchPosition(float t) { PointF curr = transformCoordBitmapToTouch(bitmapX, bitmapY); matrix.postTranslate(targetX - curr.x, targetY - curr.y); } - + /** * Use interpolator to get t * @return @@ -1043,7 +1074,7 @@ private float interpolate() { elapsed = Math.min(1f, elapsed); return interpolator.getInterpolation(elapsed); } - + /** * Interpolate the current targeted zoom and get the delta * from the current zoom. @@ -1055,7 +1086,7 @@ private double calculateDeltaScale(float t) { return zoom / normalizedScale; } } - + /** * This function will transform the coordinates in the touch event to the coordinate * system of the drawable that the imageview contain @@ -1073,15 +1104,15 @@ private PointF transformCoordTouchToBitmap(float x, float y, boolean clipToBitma float transY = m[Matrix.MTRANS_Y]; float finalX = ((x - transX) * origW) / getImageWidth(); float finalY = ((y - transY) * origH) / getImageHeight(); - + if (clipToBitmap) { finalX = Math.min(Math.max(finalX, 0), origW); finalY = Math.min(Math.max(finalY, 0), origH); } - + return new PointF(finalX , finalY); } - + /** * Inverse of transformCoordTouchToBitmap. This function will transform the coordinates in the * drawable's coordinate system to the view's coordinate system. @@ -1090,7 +1121,7 @@ private PointF transformCoordTouchToBitmap(float x, float y, boolean clipToBitma * @return Coordinates of the point in the view's coordinate system. */ private PointF transformCoordBitmapToTouch(float bx, float by) { - matrix.getValues(m); + matrix.getValues(m); float origW = getDrawable().getIntrinsicWidth(); float origH = getDrawable().getIntrinsicHeight(); float px = bx / origW; @@ -1099,7 +1130,7 @@ private PointF transformCoordBitmapToTouch(float bx, float by) { float finalY = m[Matrix.MTRANS_Y] + getImageHeight() * py; return new PointF(finalX , finalY); } - + /** * Fling launches sequential runnables which apply * the fling graphic to the image. The values for the translation @@ -1108,51 +1139,51 @@ private PointF transformCoordBitmapToTouch(float bx, float by) { * */ private class Fling implements Runnable { - + CompatScroller scroller; int currX, currY; - + Fling(int velocityX, int velocityY) { setState(State.FLING); scroller = new CompatScroller(context); matrix.getValues(m); - + int startX = (int) m[Matrix.MTRANS_X]; int startY = (int) m[Matrix.MTRANS_Y]; int minX, maxX, minY, maxY; - + if (getImageWidth() > viewWidth) { minX = viewWidth - (int) getImageWidth(); maxX = 0; - + } else { minX = maxX = startX; } - + if (getImageHeight() > viewHeight) { minY = viewHeight - (int) getImageHeight(); maxY = 0; - + } else { minY = maxY = startY; } - - scroller.fling(startX, startY, (int) velocityX, (int) velocityY, minX, + + scroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY); currX = startX; currY = startY; } - + public void cancelFling() { if (scroller != null) { setState(State.NONE); scroller.forceFinished(true); } } - + @Override public void run() { - + // // OnTouchImageViewListener is set: TouchImageView listener has been flung by user. // Listener runnable updated with each frame of fling animation. @@ -1160,12 +1191,12 @@ public void run() { if (touchImageViewListener != null) { touchImageViewListener.onMove(); } - + if (scroller.isFinished()) { scroller = null; return; } - + if (scroller.computeScrollOffset()) { int newX = scroller.getCurrX(); int newY = scroller.getCurrY(); @@ -1180,16 +1211,19 @@ public void run() { } } } - - private class CompatScroller { + + + private class CompatScroller { Scroller scroller; OverScroller overScroller; boolean isPreGingerbread; public CompatScroller(Context context) { - isPreGingerbread = false; - overScroller = new OverScroller(context); - } + + isPreGingerbread = false; + overScroller = new OverScroller(context); + + } public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) { if (isPreGingerbread) { @@ -1198,7 +1232,7 @@ public void fling(int startX, int startY, int velocityX, int velocityY, int minX overScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY); } } - + public void forceFinished(boolean finished) { if (isPreGingerbread) { scroller.forceFinished(finished); @@ -1206,7 +1240,7 @@ public void forceFinished(boolean finished) { overScroller.forceFinished(finished); } } - + public boolean isFinished() { if (isPreGingerbread) { return scroller.isFinished(); @@ -1214,7 +1248,7 @@ public boolean isFinished() { return overScroller.isFinished(); } } - + public boolean computeScrollOffset() { if (isPreGingerbread) { return scroller.computeScrollOffset(); @@ -1223,7 +1257,7 @@ public boolean computeScrollOffset() { return overScroller.computeScrollOffset(); } } - + public int getCurrX() { if (isPreGingerbread) { return scroller.getCurrX(); @@ -1231,7 +1265,7 @@ public int getCurrX() { return overScroller.getCurrX(); } } - + public int getCurrY() { if (isPreGingerbread) { return scroller.getCurrY(); @@ -1240,23 +1274,23 @@ public int getCurrY() { } } } - + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void compatPostOnAnimation(Runnable runnable) { if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { postOnAnimation(runnable); - + } else { postDelayed(runnable, 1000/60); } } - + private class ZoomVariables { public float scale; public float focusX; public float focusY; public ScaleType scaleType; - + public ZoomVariables(float scale, float focusX, float focusY, ScaleType scaleType) { this.scale = scale; this.focusX = focusX; @@ -1264,10 +1298,14 @@ public ZoomVariables(float scale, float focusX, float focusY, ScaleType scaleTyp this.scaleType = scaleType; } } - + private void printMatrixInfo() { float[] n = new float[9]; matrix.getValues(n); Log.d(DEBUG, "Scale: " + n[Matrix.MSCALE_X] + " TransX: " + n[Matrix.MTRANS_X] + " TransY: " + n[Matrix.MTRANS_Y]); } + + public ScaleGestureDetector getmScaleDetector() { + return mScaleDetector; + } } \ No newline at end of file diff --git a/src/main/res/layout-land/account_setup.xml b/src/main/res/layout-land/account_setup.xml index 224556826651..0c1d021f730d 100644 --- a/src/main/res/layout-land/account_setup.xml +++ b/src/main/res/layout-land/account_setup.xml @@ -109,22 +109,22 @@ - - - + + + android:id="@+id/input_layout_account_username" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone"> @@ -273,17 +273,17 @@ android:paddingTop="@dimen/standard_half_padding"> + android:id="@+id/buttonOK" + android:theme="@style/Button.Login" + style="@style/Button.Login" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="@dimen/standard_padding" + android:layout_gravity="center_horizontal" + android:enabled="false" + android:text="@string/setup_btn_connect" + android:contentDescription="@string/setup_btn_connect" + android:visibility="gone"/> + android:text="@string/common_loading"/> diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml index 6f5440cdf114..2602a6b9a4c3 100644 --- a/src/main/res/values/attrs.xml +++ b/src/main/res/values/attrs.xml @@ -21,6 +21,16 @@ LOCAL_BEHAVIOUR_DELETE + + @string/pref_preview_behaviour_entry_download + @string/pref_preview_behaviour_entry_preview + + + + PREVIEW_BEHAVIOUR_DOWNLOAD + PREVIEW_BEHAVIOUR_PREVIEW + + @string/uploader_upload_files_behaviour_move_to_nextcloud_folder @string/uploader_upload_files_behaviour_only_upload diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index e04db0501c87..09e7ccaaaa27 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -19,7 +19,6 @@ Oldest first Biggest first Smallest first - All files Home Favorites @@ -675,6 +674,8 @@ We can\'t find your last backup! Backup scheduled and will start shortly Import scheduled and will start shortly + full sized version + resized version New notification received @@ -689,9 +690,12 @@ Configure folders + Click on image downloads Test server connection , Resharing is not allowed Unlock with fingerprint Use your fingerprint to unlock the app + + No resized image possible. Download full image? diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index 2cade420505a..62616a4d8334 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -35,13 +35,13 @@ @style/ownCloud.SearchView - + - +