diff options
author | tobiasKaminsky <tobias@kaminsky.me> | 2018-07-06 11:16:50 +0300 |
---|---|---|
committer | tobiasKaminsky <tobias@kaminsky.me> | 2018-12-03 17:44:09 +0300 |
commit | b80bba82c189540cf8d155d3ba4d2e20e29a9e09 (patch) | |
tree | 00b3a90b1c486e73ede7343de59a1b632c3b2763 | |
parent | df3bb7936547be8f74e1cc63bca10db471b045ab (diff) |
Glide 4glide
Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
73 files changed, 2275 insertions, 2481 deletions
diff --git a/build.gradle b/build.gradle index 136799b4b3..9c789bf59f 100644 --- a/build.gradle +++ b/build.gradle @@ -239,9 +239,8 @@ dependencies { implementation 'org.parceler:parceler-api:1.1.11' annotationProcessor 'org.parceler:parceler:1.1.11' - implementation ('com.github.bumptech.glide:glide:3.7.0') { - exclude group: "com.android.support" - } + implementation 'com.github.bumptech.glide:glide:4.8.0' + annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1' implementation 'com.caverock:androidsvg:1.3' implementation "com.android.support:support-annotations:${supportLibraryVersion}" implementation 'com.google.code.gson:gson:2.8.5' diff --git a/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java b/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java index 8cccfd9f0b..123e1b13cd 100644 --- a/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java +++ b/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java @@ -91,7 +91,6 @@ public class OCFileUnitTest { mFile.setPublicLink(PUBLIC_LINK); mFile.setPermissions(PERMISSIONS); mFile.setRemoteId(REMOTE_ID); - mFile.setNeedsUpdateThumbnail(true); mFile.setDownloading(true); mFile.setEtagInConflict(ETAG_IN_CONFLICT); @@ -121,12 +120,12 @@ public class OCFileUnitTest { assertThat(fileReadFromParcel.getLastSyncDateForData(), is(LAST_SYNC_DATE_FOR_DATA)); assertThat(fileReadFromParcel.isAvailableOffline(), is(true)); assertThat(fileReadFromParcel.getEtag(), is(ETAG)); + assertThat(fileReadFromParcel.getEtagOnServer(), is(ETAG)); assertThat(fileReadFromParcel.isSharedViaLink(), is(true)); assertThat(fileReadFromParcel.isSharedWithSharee(), is(true)); assertThat(fileReadFromParcel.getPublicLink(), is(PUBLIC_LINK)); assertThat(fileReadFromParcel.getPermissions(), is(PERMISSIONS)); assertThat(fileReadFromParcel.getRemoteId(), is(REMOTE_ID)); - assertThat(fileReadFromParcel.needsUpdateThumbnail(), is(true)); assertThat(fileReadFromParcel.isDownloading(), is(true)); assertThat(fileReadFromParcel.getEtagInConflict(), is(ETAG_IN_CONFLICT)); diff --git a/src/gplay/java/com/owncloud/android/utils/PushUtils.java b/src/gplay/java/com/owncloud/android/utils/PushUtils.java index 648159e71c..71bf2c303e 100644 --- a/src/gplay/java/com/owncloud/android/utils/PushUtils.java +++ b/src/gplay/java/com/owncloud/android/utils/PushUtils.java @@ -397,7 +397,7 @@ public final class PushUtils { if (oldPrivateKeyFile.exists()) { try { FileStorageUtils.moveFile(oldPrivateKeyFile, privateKeyFile); - } catch (IOException e) { + } catch (Exception e) { Log.e(TAG, "Failed to move old private key to new location"); } } @@ -405,7 +405,7 @@ public final class PushUtils { if (oldPublicKeyFile.exists()) { try { FileStorageUtils.moveFile(oldPublicKeyFile, publicKeyFile); - } catch (IOException e) { + } catch (Exception e) { Log.e(TAG, "Failed to move old public key to new location"); } } diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index ee9212f523..984a9052cc 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -203,12 +203,6 @@ android:resource="@xml/exposed_filepaths" /> </provider> - <provider - android:name=".providers.DiskLruImageCacheFileProvider" - android:authorities="@string/image_cache_provider_authority" - android:exported="true"> - </provider> - <activity android:name=".authentication.AuthenticatorActivity" android:exported="true" diff --git a/src/main/java/com/owncloud/android/MainApp.java b/src/main/java/com/owncloud/android/MainApp.java index 0e5d9bac83..347c3c000e 100644 --- a/src/main/java/com/owncloud/android/MainApp.java +++ b/src/main/java/com/owncloud/android/MainApp.java @@ -53,7 +53,6 @@ import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.MediaProvider; import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderProvider; -import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.datastorage.DataStorageProvider; import com.owncloud.android.datastorage.StoragePoint; import com.owncloud.android.db.PreferenceManager; @@ -143,10 +142,6 @@ public class MainApp extends MultiDexApplication { .setDefaultPolicy(Policy.SINGLE_SESSION_PER_ACCOUNT_IF_SERVER_SUPPORTS_SERVER_MONITORING); } - // initialise thumbnails cache on background thread - new ThumbnailsCacheManager.InitDiskCacheTask().execute(); - - if (BuildConfig.DEBUG || getApplicationContext().getResources().getBoolean(R.bool.logger_enabled) || appPrefs.getBoolean(Preferences.PREFERENCE_EXPERT_MODE, false)) { // use app writable dir, no permissions needed diff --git a/src/main/java/com/owncloud/android/authentication/AccountUtils.java b/src/main/java/com/owncloud/android/authentication/AccountUtils.java index 77f39880ba..d24f33f211 100644 --- a/src/main/java/com/owncloud/android/authentication/AccountUtils.java +++ b/src/main/java/com/owncloud/android/authentication/AccountUtils.java @@ -29,6 +29,9 @@ import android.support.annotation.Nullable; import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.ArbitraryDataProvider; +import com.owncloud.android.lib.common.OwnCloudAccount; +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.accounts.AccountUtils.Constants; import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.ui.activity.ManageAccountsActivity; @@ -196,4 +199,33 @@ public final class AccountUtils { public static boolean hasSearchSupport(Account account) { return getServerVersion(account).isSearchSupported(); } + + public static @Nullable + OwnCloudClient getClientForCurrentAccount(Context context) { + try { + Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(context); + + if (currentAccount == null) { + return null; + } + + OwnCloudAccount ocAccount = new OwnCloudAccount(currentAccount, context); + return OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, context); + } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) { + throw new IllegalStateException("Account not found", e); + } catch (Exception e) { + throw new IllegalStateException("Client could not be instantiated", e); + } + } + + public static OwnCloudClient getClientForAccount(Account account, Context context) { + try { + OwnCloudAccount ocAccount = new OwnCloudAccount(account, context); + return OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, context); + } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) { + throw new IllegalStateException("Account not found"); + } catch (Exception e) { + throw new IllegalStateException("Client could not be instantiated"); + } + } } diff --git a/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index c30115c0f4..445c3d6cf8 100644 --- a/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -210,12 +210,12 @@ public class FileDataStorageManager { cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData()); cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isAvailableOffline() ? 1 : 0); cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag()); + cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, file.getEtagOnServer()); cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0); cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0); cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink()); cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions()); cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId()); - cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail()); cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading()); cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict()); @@ -450,6 +450,7 @@ public class FileDataStorageManager { cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, folder.getLastSyncDateForData()); cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, folder.isAvailableOffline() ? 1 : 0); cv.put(ProviderTableMeta.FILE_ETAG, folder.getEtag()); + cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, folder.getEtagOnServer()); cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, folder.isSharedViaLink() ? 1 : 0); cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, folder.isSharedWithSharee() ? 1 : 0); cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, folder.getPublicLink()); @@ -479,12 +480,12 @@ public class FileDataStorageManager { cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData()); cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isAvailableOffline() ? 1 : 0); cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag()); + cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, file.getEtagOnServer()); cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0); cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0); cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink()); cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions()); cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId()); - cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail()); cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading()); cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict()); cv.put(ProviderTableMeta.FILE_FAVORITE, file.isFavorite()); @@ -968,12 +969,12 @@ public class FileDataStorageManager { file.setLastSyncDateForData(c.getLong(c.getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA))); file.setAvailableOffline(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1); file.setEtag(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG))); + file.setEtagOnServer(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG_ON_SERVER))); file.setShareViaLink(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_SHARED_VIA_LINK)) == 1); file.setShareWithSharee(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_SHARED_WITH_SHAREE)) == 1); file.setPublicLink(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PUBLIC_LINK))); file.setPermissions(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PERMISSIONS))); file.setRemoteId(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_REMOTE_ID))); - file.setNeedsUpdateThumbnail(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_UPDATE_THUMBNAIL)) == 1); file.setDownloading(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_IS_DOWNLOADING)) == 1); file.setEtagInConflict(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG_IN_CONFLICT))); file.setFavorite(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_FAVORITE)) == 1); @@ -1394,6 +1395,7 @@ public class FileDataStorageManager { ); cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isAvailableOffline() ? 1 : 0); cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag()); + cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, file.getEtagOnServer()); cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0); cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0); cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink()); @@ -1401,10 +1403,6 @@ public class FileDataStorageManager { cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId()); cv.put(ProviderTableMeta.FILE_FAVORITE, file.isFavorite()); cv.put( - ProviderTableMeta.FILE_UPDATE_THUMBNAIL, - file.needsUpdateThumbnail() ? 1 : 0 - ); - cv.put( ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading() ? 1 : 0 ); diff --git a/src/main/java/com/owncloud/android/datamodel/OCFile.java b/src/main/java/com/owncloud/android/datamodel/OCFile.java index 8b558bebe6..8b4070a2fa 100644 --- a/src/main/java/com/owncloud/android/datamodel/OCFile.java +++ b/src/main/java/com/owncloud/android/datamodel/OCFile.java @@ -81,6 +81,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa private boolean mAvailableOffline; private String mEtag; + private String mEtagOnServer; private boolean mShareByLink; private String mPublicLink; @@ -154,6 +155,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa mLastSyncDateForProperties = source.readLong(); mLastSyncDateForData = source.readLong(); mEtag = source.readString(); + mEtagOnServer = source.readString(); mShareByLink = source.readInt() == 1; mPublicLink = source.readString(); mPermissions = source.readString(); @@ -184,6 +186,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa dest.writeLong(mLastSyncDateForProperties); dest.writeLong(mLastSyncDateForData); dest.writeString(mEtag); + dest.writeString(mEtagOnServer); dest.writeInt(mShareByLink ? 1 : 0); dest.writeString(mPublicLink); dest.writeString(mPermissions); @@ -513,6 +516,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa mAvailableOffline = false; mNeedsUpdating = false; mEtag = null; + mEtagOnServer = null; mShareByLink = false; mPublicLink = null; mPermissions = null; @@ -600,14 +604,6 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa return mNeedsUpdating; } - public boolean needsUpdateThumbnail() { - return mNeedsUpdateThumbnail; - } - - public void setNeedsUpdateThumbnail(boolean needsUpdateThumbnail) { - this.mNeedsUpdateThumbnail = needsUpdateThumbnail; - } - public long getLastSyncDateForProperties() { return mLastSyncDateForProperties; } @@ -685,6 +681,9 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa this.mEtag = (etag != null ? etag : ""); } + public void setEtagOnServer(String eTag) { + this.mEtagOnServer = eTag != null ? eTag : ""; + } public boolean isSharedViaLink() { return mShareByLink; @@ -797,4 +796,8 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa public void setMountType(WebdavEntry.MountType mountType) { mMountType = mountType; } + + public String getEtagOnServer() { + return mEtagOnServer; + } } diff --git a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java deleted file mode 100644 index c40375f463..0000000000 --- a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ /dev/null @@ -1,1169 +0,0 @@ -/* - * ownCloud Android client application - * - * @author Tobias Kaminsky - * @author David A. Velasco - * Copyright (C) 2015 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 <http://www.gnu.org/licenses/>. - * - */ - -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.provider.MediaStore; -import android.support.annotation.Nullable; -import android.text.TextUtils; -import android.view.Display; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.ImageView; - -import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.lib.common.OwnCloudAccount; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.files.ServerFileInterface; -import com.owncloud.android.lib.resources.files.TrashbinFile; -import com.owncloud.android.ui.TextDrawable; -import com.owncloud.android.ui.adapter.DiskLruImageCache; -import com.owncloud.android.ui.fragment.FileFragment; -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; - -import org.apache.commons.httpclient.HttpStatus; -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.net.URLEncoder; -import java.util.List; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - -/** - * Manager for concurrent access to thumbnails cache. - */ -public final class ThumbnailsCacheManager { - - public static final String PREFIX_RESIZED_IMAGE = "r"; - public static final String PREFIX_THUMBNAIL = "t"; - - private static final String TAG = ThumbnailsCacheManager.class.getSimpleName(); - private static final String PNG_MIMETYPE = "image/png"; - private static final String CACHE_FOLDER = "thumbnailCache"; - public static final String AVATAR = "avatar"; - private static final String ETAG = "ETag"; - - private static final Object mThumbnailsDiskCacheLock = new Object(); - private static DiskLruImageCache mThumbnailCache; - private static boolean mThumbnailCacheStarting = true; - - 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; - - public static final Bitmap mDefaultImg = BitmapFactory.decodeResource(MainApp.getAppContext().getResources(), - R.drawable.file_image); - - public static final Bitmap mDefaultVideo = BitmapFactory.decodeResource(MainApp.getAppContext().getResources(), - R.drawable.file_movie); - - private ThumbnailsCacheManager() { - } - - public static class InitDiskCacheTask extends AsyncTask<File, Void, Void> { - @Override - protected Void doInBackground(File... params) { - synchronized (mThumbnailsDiskCacheLock) { - mThumbnailCacheStarting = true; - - if (mThumbnailCache == null) { - try { - // Check if media is mounted or storage is built-in, if so, - // try and use external cache dir; otherwise use internal cache dir - 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 (Exception e) { - Log_OC.d(TAG, e.getMessage()); - mThumbnailCache = null; - } - } - mThumbnailCacheStarting = false; // Finished initialization - mThumbnailsDiskCacheLock.notifyAll(); // Wake any waiting threads - } - return null; - } - } - - /** - * Converts size of file icon from dp to pixel - * @return int - */ - 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 pxW: thumbnail width in pixel - * @param pxH: thumbnail height in pixel - * @return Bitmap - */ - private static Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int pxW, int pxH){ - - Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH); - - // Rotate image, obeying exif tag - thumbnail = BitmapUtils.rotateImage(thumbnail,path); - - // Add thumbnail to cache - addBitmapToCache(imageKey, thumbnail); - - return thumbnail; - } - - public static void addBitmapToCache(String key, Bitmap bitmap) { - synchronized (mThumbnailsDiskCacheLock) { - if (mThumbnailCache != null) { - mThumbnailCache.put(key, bitmap); - } - } - } - - public static Bitmap getBitmapFromDiskCache(String key) { - synchronized (mThumbnailsDiskCacheLock) { - // Wait while disk cache is started from background thread - while (mThumbnailCacheStarting) { - try { - mThumbnailsDiskCacheLock.wait(); - } catch (InterruptedException e) { - Log_OC.e(TAG, "Wait in mThumbnailsDiskCacheLock was interrupted", e); - } - } - if (mThumbnailCache != null) { - return mThumbnailCache.getBitmap(key); - } - } - return null; - } - - public static class ResizedImageGenerationTask extends AsyncTask<Object, Void, Bitmap> { - private FileFragment fileFragment; - private FileDataStorageManager storageManager; - private Account account; - private WeakReference<ImageView> imageViewReference; - private OCFile file; - - - public ResizedImageGenerationTask(FileFragment fileFragment, ImageView imageView, - FileDataStorageManager storageManager, Account account) - throws IllegalArgumentException { - this.fileFragment = fileFragment; - 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) { - Log_OC.e(TAG, "Out of memory"); - } catch (Throwable t) { - // the app should never break due to a problem with thumbnails - Log_OC.e(TAG, "Generation of thumbnail for " + file + " failed", t); - } - - return thumbnail; - } - - private Bitmap doResizedImageInBackground() { - Bitmap thumbnail; - - String imageKey = PREFIX_RESIZED_IMAGE + String.valueOf(file.getRemoteId()); - - // Check disk cache in background thread - thumbnail = getBitmapFromDiskCache(imageKey); - - // Not found in disk cache - if (thumbnail == null || file.needsUpdateThumbnail()) { - Point p = getScreenDimension(); - int pxW = p.x; - int pxH = p.y; - - if (file.isDown()) { - Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getStoragePath(), pxW, pxH); - - if (bitmap != null) { - // Handle PNG - if (file.getMimeType().equalsIgnoreCase(PNG_MIMETYPE)) { - bitmap = handlePNG(bitmap, pxW, pxH); - } - - thumbnail = addThumbnailToCache(imageKey, bitmap, file.getStoragePath(), pxW, pxH); - - file.setNeedsUpdateThumbnail(false); - storageManager.saveFile(file); - } - - } else { - // Download thumbnail from server - if (mClient != null) { - GetMethod getMethod = null; - try { - String uri = mClient.getBaseUri() + "/index.php/core/preview.png?file=" - + URLEncoder.encode(file.getRemotePath()) - + "&x=" + pxW + "&y=" + pxH + "&a=1&mode=cover&forceIcon=0"; - getMethod = new GetMethod(uri); - - 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 (thumbnail != null && file.getMimeType().equalsIgnoreCase(PNG_MIMETYPE)) { - thumbnail = handlePNG(thumbnail, thumbnail.getWidth(), thumbnail.getHeight()); - } - - // 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(); - } - } - } - } - } - - 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 { - new Thread(() -> { - if (ConnectivityUtils.isInternetWalled(MainApp.getAppContext())) { - if (fileFragment instanceof PreviewImageFragment) { - ((PreviewImageFragment) fileFragment).setNoConnectionErrorMessage(); - } - } else { - if (fileFragment instanceof PreviewImageFragment) { - ((PreviewImageFragment) fileFragment).setErrorPreviewMessage(); - } - } - }).start(); - - } - } - } - } - - public static class ThumbnailGenerationTaskObject { - private Object file; - private String imageKey; - - public ThumbnailGenerationTaskObject(Object file, String imageKey) { - this.file = file; - this.imageKey = imageKey; - } - - private Object getFile() { - return file; - } - - private String getImageKey() { - return imageKey; - } - } - - public static class ThumbnailGenerationTask extends AsyncTask<ThumbnailGenerationTaskObject, Void, Bitmap> { - private final WeakReference<ImageView> mImageViewReference; - private static Account mAccount; - private List<ThumbnailGenerationTask> mAsyncTasks; - private Object mFile; - private String mImageKey; - private FileDataStorageManager mStorageManager; - private GetMethod getMethod; - - public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager storageManager, Account account) - throws IllegalArgumentException { - this(imageView, storageManager, account, null); - } - - public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager storageManager, - Account account, List<ThumbnailGenerationTask> asyncTasks) - throws IllegalArgumentException { - // Use a WeakReference to ensure the ImageView can be garbage collected - mImageViewReference = new WeakReference<ImageView>(imageView); - if (storageManager == null) { - throw new IllegalArgumentException("storageManager must not be NULL"); - } - mStorageManager = storageManager; - mAccount = account; - mAsyncTasks = asyncTasks; - } - - public GetMethod getGetMethod() { - return getMethod; - } - - public ThumbnailGenerationTask(FileDataStorageManager storageManager, Account account){ - if (storageManager == null) { - throw new IllegalArgumentException("storageManager must not be NULL"); - } - mStorageManager = storageManager; - mAccount = account; - mImageViewReference = null; - } - - public ThumbnailGenerationTask(ImageView imageView) { - // Use a WeakReference to ensure the ImageView can be garbage collected - mImageViewReference = new WeakReference<ImageView>(imageView); - } - - @SuppressFBWarnings("Dm") - @Override - protected Bitmap doInBackground(ThumbnailGenerationTaskObject... params) { - Bitmap thumbnail = null; - - try { - if (mAccount != null) { - OwnCloudAccount ocAccount = new OwnCloudAccount( - mAccount, - MainApp.getAppContext() - ); - mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, MainApp.getAppContext()); - } - - ThumbnailGenerationTaskObject object = params[0]; - mFile = object.getFile(); - mImageKey = object.getImageKey(); - - if (mFile instanceof ServerFileInterface) { - thumbnail = doThumbnailFromOCFileInBackground(); - - if (MimeTypeUtil.isVideo((ServerFileInterface) mFile) && thumbnail != null) { - thumbnail = addVideoOverlay(thumbnail); - } - } else if (mFile instanceof File) { - thumbnail = doFileInBackground(); - - String url = ((File) mFile).getAbsolutePath(); - String mMimeType = FileStorageUtils.getMimeTypeFromName(url); - - if (MimeTypeUtil.isVideo(mMimeType) && thumbnail != null) { - thumbnail = addVideoOverlay(thumbnail); - } - //} else { do nothing - } - - } catch(OutOfMemoryError oome) { - Log_OC.e(TAG, "Out of memory"); - } catch (Throwable t) { - // the app should never break due to a problem with thumbnails - Log_OC.e(TAG, "Generation of thumbnail for " + mFile + " failed", t); - } - - return thumbnail; - } - - 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) { - tagId = String.valueOf(mFile.hashCode()); - } else if (mFile instanceof TrashbinFile) { - tagId = String.valueOf(((TrashbinFile) mFile).getRemoteId()); - } - if (String.valueOf(imageView.getTag()).equals(tagId)) { - imageView.setImageBitmap(bitmap); - } - } - } - - if (mAsyncTasks != null) { - mAsyncTasks.remove(this); - } - } - - private Bitmap doThumbnailFromOCFileInBackground() { - Bitmap thumbnail; - ServerFileInterface file = (ServerFileInterface) mFile; - String imageKey = PREFIX_THUMBNAIL + String.valueOf(file.getRemoteId()); - - // Check disk cache in background thread - thumbnail = getBitmapFromDiskCache(imageKey); - - // Not found in disk cache - if (thumbnail == null || (file instanceof OCFile && ((OCFile) file).needsUpdateThumbnail())) { - int pxW; - int pxH; - pxW = pxH = getThumbnailDimension(); - - if (file instanceof OCFile) { - OCFile ocFile = (OCFile) file; - if (ocFile.isDown()) { - Bitmap bitmap; - if (MimeTypeUtil.isVideo(ocFile)) { - bitmap = ThumbnailUtils.createVideoThumbnail(ocFile.getStoragePath(), - MediaStore.Images.Thumbnails.MINI_KIND); - } else { - bitmap = BitmapUtils.decodeSampledBitmapFromFile(ocFile.getStoragePath(), pxW, pxH); - } - - if (bitmap != null) { - // Handle PNG - if (ocFile.getMimeType().equalsIgnoreCase(PNG_MIMETYPE)) { - bitmap = handlePNG(bitmap, pxW, pxH); - } - - thumbnail = addThumbnailToCache(imageKey, bitmap, ocFile.getStoragePath(), pxW, pxH); - - ocFile.setNeedsUpdateThumbnail(false); - mStorageManager.saveFile(ocFile); - } - } - } - - if (thumbnail == null) { - // check if resized version is available - String resizedImageKey = PREFIX_RESIZED_IMAGE + String.valueOf(file.getRemoteId()); - Bitmap resizedImage = getBitmapFromDiskCache(resizedImageKey); - - if (resizedImage != null) { - thumbnail = ThumbnailUtils.extractThumbnail(resizedImage, pxW, pxH); - } else { - // Download thumbnail from server - if (mClient != null) { - getMethod = null; - try { - // thumbnail - String uri; - if (file instanceof OCFile) { - uri = mClient.getBaseUri() + "/index.php/apps/files/api/v1/thumbnail/" + - pxW + "/" + pxH + Uri.encode(file.getRemotePath(), "/"); - } else { - uri = mClient.getBaseUri() + "/index.php/apps/files_trashbin/preview?fileId=" + - file.getLocalId() + "&x=" + pxW + "&y=" + pxH; - } - - Log_OC.d(TAG, "generate thumbnail: " + file.getFileName() + " URI: " + uri); - getMethod = new GetMethod(uri); - getMethod.setRequestHeader("Cookie", - "nc_sameSiteCookielax=true;nc_sameSiteCookiestrict=true"); - - getMethod.setRequestHeader(RemoteOperation.OCS_API_HEADER, - RemoteOperation.OCS_API_HEADER_VALUE); - - int status = mClient.executeMethod(getMethod); - if (status == HttpStatus.SC_OK) { - InputStream inputStream = getMethod.getResponseBodyAsStream(); - Bitmap bitmap = BitmapFactory.decodeStream(inputStream); - thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH); - } else { - mClient.exhaustResponse(getMethod.getResponseBodyAsStream()); - } - - // Handle PNG - if (file.getMimeType().equalsIgnoreCase(PNG_MIMETYPE)) { - thumbnail = handlePNG(thumbnail, pxW, pxH); - } - } catch (Exception e) { - Log_OC.d(TAG, e.getMessage(), e); - } finally { - if (getMethod != null) { - getMethod.releaseConnection(); - } - } - } - } - - // Add thumbnail to cache - if (thumbnail != null) { - Log_OC.d(TAG, "add thumbnail to cache: " + file.getFileName()); - addBitmapToCache(imageKey, thumbnail); - } - } - } - - return thumbnail; - } - - /** - * 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; - - final String imageKey; - if (mImageKey != null) { - imageKey = mImageKey; - } else { - imageKey = String.valueOf(file.hashCode()); - } - - // local file should always generate a thumbnail - mImageKey = PREFIX_THUMBNAIL + mImageKey; - - // Check disk cache in background thread - Bitmap thumbnail = getBitmapFromDiskCache(imageKey); - - // Not found in disk cache - if (thumbnail == null) { - int pxW; - int pxH; - pxW = pxH = getThumbnailDimension(); - - Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getAbsolutePath(), pxW, pxH); - - if (bitmap != null) { - thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), pxW, pxH); - } - } - return thumbnail; - } - - } - - public static class MediaThumbnailGenerationTask extends AsyncTask<Object, Void, Bitmap> { - - private static final int IMAGE_KEY_PARAMS_LENGTH = 2; - - private enum Type {IMAGE, VIDEO} - private final WeakReference<ImageView> mImageViewReference; - private File mFile; - private String mImageKey = null; - private Context mContext; - - public MediaThumbnailGenerationTask(ImageView imageView, Context context) { - // Use a WeakReference to ensure the ImageView can be garbage collected - mImageViewReference = new WeakReference<>(imageView); - mContext = context; - } - - @Override - protected Bitmap doInBackground(Object... params) { - Bitmap thumbnail = null; - - try { - if (params[0] instanceof File) { - mFile = (File) params[0]; - if (params.length == IMAGE_KEY_PARAMS_LENGTH) { - mImageKey = (String) params[1]; - } - - if (MimeTypeUtil.isImage(mFile)) { - thumbnail = doFileInBackground(mFile, Type.IMAGE); - } else if (MimeTypeUtil.isVideo(mFile)) { - thumbnail = doFileInBackground(mFile, Type.VIDEO); - } - } - } // the app should never break due to a problem with thumbnails - catch (OutOfMemoryError t) { - Log_OC.e(TAG, "Generation of thumbnail for " + mFile.getAbsolutePath() + " failed", t); - Log_OC.e(TAG, "Out of memory"); - } catch (Throwable t) { - // the app should never break due to a problem with thumbnails - Log_OC.e(TAG, "Generation of thumbnail for " + mFile.getAbsolutePath() + " failed", t); - } - - return thumbnail; - } - - protected void onPostExecute(Bitmap bitmap) { - String tagId = ""; - final ImageView imageView = mImageViewReference.get(); - if (imageView != null) { - if (mFile != null) { - tagId = String.valueOf(mFile.hashCode()); - } - - if (bitmap != null) { - if (tagId.equals(String.valueOf(imageView.getTag()))) { - imageView.setImageBitmap(bitmap); - } - } else { - if (mFile != null) { - if (mFile.isDirectory()) { - imageView.setImageDrawable(MimeTypeUtil.getDefaultFolderIcon(mContext)); - } else { - if (MimeTypeUtil.isVideo(mFile)) { - imageView.setImageBitmap(ThumbnailsCacheManager.mDefaultVideo); - } else { - imageView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(null, mFile.getName(), - mContext)); - } - } - } - } - } - } - - private Bitmap doFileInBackground(File file, Type type) { - final String imageKey; - - if (mImageKey != null) { - imageKey = mImageKey; - } else { - imageKey = String.valueOf(file.hashCode()); - } - - // Check disk cache in background thread - Bitmap thumbnail = getBitmapFromDiskCache(imageKey); - - // Not found in disk cache - if (thumbnail == null) { - - if (Type.IMAGE.equals(type)) { - int px = getThumbnailDimension(); - - Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getAbsolutePath(), px, px); - - if (bitmap != null) { - thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px, px); - } - } else if (Type.VIDEO.equals(type)) { - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - try { - retriever.setDataSource(file.getAbsolutePath()); - thumbnail = retriever.getFrameAtTime(-1); - } catch (Exception ex) { - // can't create a bitmap - Log_OC.w(TAG, "Failed to create bitmap from video " + file.getAbsolutePath()); - } finally { - try { - retriever.release(); - } catch (RuntimeException ex) { - // Ignore failure at this point. - Log_OC.w(TAG, "Failed release MediaMetadataRetriever for " + file.getAbsolutePath()); - } - } - - if (thumbnail != null) { - // Scale down bitmap if too large. - int px = getThumbnailDimension(); - int width = thumbnail.getWidth(); - int height = thumbnail.getHeight(); - int max = Math.max(width, height); - if (max > px) { - thumbnail = BitmapUtils.scaleBitmap(thumbnail, px, width, height, max); - thumbnail = addThumbnailToCache(imageKey, thumbnail, file.getPath(), px, px); - } - } - } - } - - return thumbnail; - } - } - - public static class AvatarGenerationTask extends AsyncTask<String, Void, Drawable> { - private final WeakReference<AvatarGenerationListener> mAvatarGenerationListener; - private final Object mCallContext; - private final Resources mResources; - private final float mAvatarRadius; - private Account mAccount; - private String mUserId; - private String mServerName; - private Context mContext; - - - public AvatarGenerationTask(AvatarGenerationListener avatarGenerationListener, Object callContext, - Account account, Resources resources, float avatarRadius, String userId, - String serverName, Context context) { - mAvatarGenerationListener = new WeakReference<>(avatarGenerationListener); - mCallContext = callContext; - mAccount = account; - mResources = resources; - mAvatarRadius = avatarRadius; - mUserId = userId; - mServerName = serverName; - mContext = context; - } - - @SuppressFBWarnings("Dm") - @Override - protected Drawable doInBackground(String... params) { - Drawable thumbnail = null; - - try { - if (mAccount != null) { - OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); - mClient = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, mContext); - } - - thumbnail = doAvatarInBackground(); - - } catch (OutOfMemoryError oome) { - Log_OC.e(TAG, "Out of memory"); - } catch (Throwable t) { - // the app should never break due to a problem with avatars - Log_OC.e(TAG, "Generation of avatar for " + mUserId + " failed", t); - } - - return thumbnail; - } - - protected void onPostExecute(Drawable drawable) { - if (drawable != null) { - AvatarGenerationListener listener = mAvatarGenerationListener.get(); - AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(mCallContext); - - if (this == avatarWorkerTask && listener.shouldCallGeneratedCallback(mUserId, mCallContext)) { - listener.avatarGenerated(drawable, mCallContext); - } - } - } - - /** - * Converts size of file icon from dp to pixel - * - * @return int - */ - private int getAvatarDimension() { - // Converts dp to pixel - Resources r = MainApp.getAppContext().getResources(); - return Math.round(r.getDimension(R.dimen.file_avatar_size)); - } - - private @Nullable - Drawable doAvatarInBackground() { - Bitmap avatar = null; - - String accountName = mUserId + "@" + mServerName; - - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(mContext.getContentResolver()); - - String eTag = arbitraryDataProvider.getValue(accountName, ThumbnailsCacheManager.AVATAR); - String avatarKey = "a_" + mUserId + "_" + mServerName + "_" + eTag; - - int px = getAvatarDimension(); - - // Download avatar from server - if (mClient != null) { - GetMethod get = null; - try { - String uri = mClient.getBaseUri() + "/index.php/avatar/" + Uri.encode(mUserId) + "/" + px; - Log_OC.d("Avatar", "URI: " + uri); - get = new GetMethod(uri); - - // only use eTag if available and corresponding avatar is still there - // (might be deleted from cache) - if (!eTag.isEmpty() && getBitmapFromDiskCache(avatarKey) != null) { - get.setRequestHeader("If-None-Match", eTag); - } - - int status = mClient.executeMethod(get); - - // we are using eTag to download a new avatar only if it changed - switch (status) { - case HttpStatus.SC_OK: - case HttpStatus.SC_CREATED: - // new avatar - InputStream inputStream = get.getResponseBodyAsStream(); - - String newETag = null; - if (get.getResponseHeader(ETAG) != null) { - newETag = get.getResponseHeader(ETAG).getValue().replace("\"", ""); - arbitraryDataProvider.storeOrUpdateKeyValue(accountName, AVATAR, newETag); - } - - Bitmap bitmap = BitmapFactory.decodeStream(inputStream); - avatar = ThumbnailUtils.extractThumbnail(bitmap, px, px); - - // Add avatar to cache - if (avatar != null && !TextUtils.isEmpty(newETag)) { - avatar = handlePNG(avatar, px, px); - String newImageKey = "a_" + mUserId + "_" + mServerName + "_" + newETag; - addBitmapToCache(newImageKey, avatar); - } else { - return TextDrawable.createAvatar(mAccount.name, mAvatarRadius); - } - break; - - case HttpStatus.SC_NOT_MODIFIED: - // old avatar - avatar = getBitmapFromDiskCache(avatarKey); - mClient.exhaustResponse(get.getResponseBodyAsStream()); - break; - - default: - // everything else - mClient.exhaustResponse(get.getResponseBodyAsStream()); - break; - - } - } catch (Exception e) { - try { - return TextDrawable.createAvatar(mAccount.name, mAvatarRadius); - } catch (Exception e1) { - Log_OC.e(TAG, "Error generating fallback avatar"); - } - } finally { - if (get != null) { - get.releaseConnection(); - } - } - - try { - return TextDrawable.createAvatar(mAccount.name, mAvatarRadius); - } catch (Exception e) { - Log_OC.e(TAG, "Error generating fallback avatar"); - } - } - - return BitmapUtils.bitmapToCircularBitmapDrawable(mResources, avatar); - } - } - - public static boolean cancelPotentialThumbnailWork(Object file, ImageView imageView) { - final ThumbnailGenerationTask bitmapWorkerTask = getBitmapWorkerTask(imageView); - - if (bitmapWorkerTask != null) { - final Object bitmapData = bitmapWorkerTask.mFile; - // If bitmapData is not yet set or it differs from the new data - if (bitmapData == null || !bitmapData.equals(file)) { - // Cancel previous task - bitmapWorkerTask.cancel(true); - Log_OC.v(TAG, "Cancelled generation of thumbnail for a reused imageView"); - } else { - // The same work is already in progress - return false; - } - } - // No task associated with the ImageView, or an existing task was cancelled - return true; - } - - 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 false; - } - - public static boolean cancelPotentialAvatarWork(Object file, ImageView imageView) { - final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(imageView); - - if (avatarWorkerTask != null) { - final Object usernameData = avatarWorkerTask.mUserId; - // If usernameData is not yet set or it differs from the new data - if (usernameData == null || !usernameData.equals(file)) { - // Cancel previous task - avatarWorkerTask.cancel(true); - Log_OC.v(TAG, "Cancelled generation of avatar for a reused imageView"); - } else { - // The same work is already in progress - return false; - } - } - // No task associated with the ImageView, or an existing task was cancelled - return true; - } - - public static boolean cancelPotentialAvatarWork(Object file, MenuItem menuItem) { - final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(menuItem); - - if (avatarWorkerTask != null) { - final Object usernameData = avatarWorkerTask.mUserId; - // If usernameData is not yet set or it differs from the new data - if (usernameData == null || !usernameData.equals(file)) { - // Cancel previous task - avatarWorkerTask.cancel(true); - Log_OC.v(TAG, "Cancelled generation of avatar for a reused imageView"); - } else { - // The same work is already in progress - return false; - } - } - // No task associated with the ImageView, or an existing task was cancelled - return true; - } - - public static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncThumbnailDrawable) { - final AsyncThumbnailDrawable asyncDrawable = (AsyncThumbnailDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } - - private static ResizedImageGenerationTask getResizedImageGenerationWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncResizedImageDrawable) { - final AsyncResizedImageDrawable asyncDrawable = (AsyncResizedImageDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } - - public static Bitmap addVideoOverlay(Bitmap thumbnail){ - Bitmap playButton = BitmapFactory.decodeResource(MainApp.getAppContext().getResources(), - R.drawable.view_play); - - Bitmap resizedPlayButton = Bitmap.createScaledBitmap(playButton, - (int) (thumbnail.getWidth() * 0.3), - (int) (thumbnail.getHeight() * 0.3), true); - - Bitmap resultBitmap = Bitmap.createBitmap(thumbnail.getWidth(), - thumbnail.getHeight(), - Bitmap.Config.ARGB_8888); - - Canvas c = new Canvas(resultBitmap); - - // compute visual center of play button, according to resized image - int x1 = resizedPlayButton.getWidth(); - int y1 = resizedPlayButton.getHeight() / 2; - int x2 = 0; - int y2 = resizedPlayButton.getWidth(); - 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)); - - // offset to top left - double ox = - xm; - - - c.drawBitmap(thumbnail, 0, 0, null); - - Paint p = new Paint(); - p.setAlpha(230); - - c.drawBitmap(resizedPlayButton, (float) ((thumbnail.getWidth() / 2) + ox), - (float) ((thumbnail.getHeight() / 2) - ym), p); - - return resultBitmap; - } - - public static AvatarGenerationTask getAvatarWorkerTask(Object callContext) { - if (callContext instanceof ImageView) { - return getAvatarWorkerTask(((ImageView)callContext).getDrawable()); - } else if (callContext instanceof MenuItem) { - return getAvatarWorkerTask(((MenuItem)callContext).getIcon()); - } - - return null; - } - - private static AvatarGenerationTask getAvatarWorkerTask(Drawable drawable) { - if (drawable instanceof AsyncAvatarDrawable) { - final AsyncAvatarDrawable asyncDrawable = (AsyncAvatarDrawable) drawable; - return asyncDrawable.getAvatarWorkerTask(); - } - return null; - } - - - public static class AsyncThumbnailDrawable extends BitmapDrawable { - private final WeakReference<ThumbnailGenerationTask> bitmapWorkerTaskReference; - - public AsyncThumbnailDrawable( - Resources res, Bitmap bitmap, ThumbnailGenerationTask bitmapWorkerTask - ) { - - super(res, bitmap); - bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); - } - - public ThumbnailGenerationTask getBitmapWorkerTask() { - return bitmapWorkerTaskReference.get(); - } - } - - public static class AsyncResizedImageDrawable extends BitmapDrawable { - private final WeakReference<ResizedImageGenerationTask> bitmapWorkerTaskReference; - - public AsyncResizedImageDrawable(Resources res, Bitmap bitmap, ResizedImageGenerationTask bitmapWorkerTask) { - super(res, bitmap); - bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); - } - - private ResizedImageGenerationTask getBitmapWorkerTask() { - return bitmapWorkerTaskReference.get(); - } - } - - public static class AsyncMediaThumbnailDrawable extends BitmapDrawable { - private final WeakReference<MediaThumbnailGenerationTask> bitmapWorkerTaskReference; - - public AsyncMediaThumbnailDrawable(Resources res, Bitmap bitmap, - MediaThumbnailGenerationTask bitmapWorkerTask) { - - super(res, bitmap); - bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); - } - } - - public static class AsyncAvatarDrawable extends BitmapDrawable { - private final WeakReference<AvatarGenerationTask> avatarWorkerTaskReference; - - public AsyncAvatarDrawable(Resources res, Drawable bitmap, AvatarGenerationTask avatarWorkerTask) { - super(res, BitmapUtils.drawableToBitmap(bitmap)); - avatarWorkerTaskReference = new WeakReference<>(avatarWorkerTask); - } - - public AvatarGenerationTask getAvatarWorkerTask() { - return avatarWorkerTaskReference.get(); - } - } - - private static Bitmap handlePNG(Bitmap bitmap, int pxW, int pxH) { - Bitmap resultBitmap = Bitmap.createBitmap(pxW, pxH, Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(resultBitmap); - - c.drawColor(MainApp.getAppContext().getResources().getColor(R.color.background_color)); - c.drawBitmap(bitmap, 0, 0, null); - - return resultBitmap; - } - - public static void generateResizedImage(OCFile file) { - Point p = getScreenDimension(); - int pxW = p.x; - int pxH = p.y; - String imageKey = PREFIX_RESIZED_IMAGE + String.valueOf(file.getRemoteId()); - - Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getStoragePath(), pxW, pxH); - - if (bitmap != null) { - // Handle PNG - if (file.getMimeType().equalsIgnoreCase(PNG_MIMETYPE)) { - bitmap = handlePNG(bitmap, pxW, pxH); - } - - addThumbnailToCache(imageKey, bitmap, file.getStoragePath(), pxW, pxH); - } - } -}
\ No newline at end of file diff --git a/src/main/java/com/owncloud/android/db/ProviderMeta.java b/src/main/java/com/owncloud/android/db/ProviderMeta.java index 7dae332ae3..bb96f8516b 100644 --- a/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -32,7 +32,7 @@ import com.owncloud.android.MainApp; public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 34; + public static final int DB_VERSION = 35; private ProviderMeta() { } @@ -92,6 +92,7 @@ public class ProviderMeta { public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data"; public static final String FILE_KEEP_IN_SYNC = "keep_in_sync"; public static final String FILE_ETAG = "etag"; + public static final String FILE_ETAG_ON_SERVER = "etag_on_server"; public static final String FILE_SHARED_VIA_LINK = "share_by_link"; public static final String FILE_SHARED_WITH_SHAREE = "shared_via_users"; public static final String FILE_PUBLIC_LINK = "public_link"; @@ -104,12 +105,12 @@ public class ProviderMeta { public static final String FILE_IS_ENCRYPTED = "is_encrypted"; public static final String FILE_MOUNT_TYPE = "mount_type"; - public static final String [] FILE_ALL_COLUMNS = {_ID, FILE_PARENT, FILE_NAME - , FILE_CREATION, FILE_MODIFIED, + public static final String[] FILE_ALL_COLUMNS = {_ID, FILE_PARENT, FILE_NAME, FILE_CREATION, FILE_MODIFIED, FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, FILE_CONTENT_LENGTH, FILE_CONTENT_TYPE, FILE_STORAGE_PATH, FILE_PATH, FILE_ACCOUNT_OWNER, FILE_LAST_SYNC_DATE, FILE_LAST_SYNC_DATE_FOR_DATA, FILE_KEEP_IN_SYNC, - FILE_ETAG, FILE_SHARED_VIA_LINK, FILE_SHARED_WITH_SHAREE, FILE_PUBLIC_LINK, FILE_PERMISSIONS, - FILE_REMOTE_ID, FILE_UPDATE_THUMBNAIL, FILE_IS_DOWNLOADING, FILE_ETAG_IN_CONFLICT, FILE_FAVORITE}; + FILE_ETAG, FILE_ETAG_ON_SERVER, FILE_SHARED_VIA_LINK, FILE_SHARED_WITH_SHAREE, FILE_PUBLIC_LINK, + FILE_PERMISSIONS, FILE_REMOTE_ID, FILE_IS_DOWNLOADING, FILE_ETAG_IN_CONFLICT, + FILE_FAVORITE}; public static final String FILE_DEFAULT_SORT_ORDER = FILE_NAME + " collate nocase asc"; diff --git a/src/main/java/com/owncloud/android/files/services/FileDownloader.java b/src/main/java/com/owncloud/android/files/services/FileDownloader.java index 560d51570e..e811e3d511 100644 --- a/src/main/java/com/owncloud/android/files/services/FileDownloader.java +++ b/src/main/java/com/owncloud/android/files/services/FileDownloader.java @@ -492,7 +492,6 @@ public class FileDownloader extends Service long syncDate = System.currentTimeMillis(); file.setLastSyncDateForProperties(syncDate); file.setLastSyncDateForData(syncDate); - file.setNeedsUpdateThumbnail(true); file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp()); file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp()); file.setEtag(mCurrentDownload.getEtag()); 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 4a9409eca7..e1d143147e 100644 --- a/src/main/java/com/owncloud/android/files/services/FileUploader.java +++ b/src/main/java/com/owncloud/android/files/services/FileUploader.java @@ -53,7 +53,6 @@ import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.datamodel.UploadsStorageManager.UploadStatus; import com.owncloud.android.db.OCUpload; @@ -71,6 +70,7 @@ import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.UploadListActivity; import com.owncloud.android.ui.notifications.NotificationUtils; import com.owncloud.android.utils.ConnectivityUtils; +import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.ErrorMessageAdapter; import com.owncloud.android.utils.PowerUtils; import com.owncloud.android.utils.ThemeUtils; @@ -1120,13 +1120,7 @@ public class FileUploader extends Service } // generate new Thumbnail - final ThumbnailsCacheManager.ThumbnailGenerationTask task = - new ThumbnailsCacheManager.ThumbnailGenerationTask(mStorageManager, mCurrentAccount); - - File file = new File(mCurrentUpload.getOriginalStoragePath()); - String remoteId = mCurrentUpload.getFile().getRemoteId(); - - task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, remoteId)); + DisplayUtils.generateThumbnail(mCurrentUpload.getFile(), mCurrentUpload.getOriginalStoragePath(), getBaseContext()); } } diff --git a/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index 6dc0a42d46..faf9a7c715 100644 --- a/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -477,7 +477,6 @@ public class RefreshFolderOperation extends RemoteOperation { } else if (remoteFolderChanged && MimeTypeUtil.isImage(remoteFile) && remoteFile.getModificationTimestamp() != localFile.getModificationTimestamp()) { - updatedFile.setNeedsUpdateThumbnail(true); Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server"); } @@ -488,6 +487,9 @@ public class RefreshFolderOperation extends RemoteOperation { // remote eTag will not be updated unless file CONTENTS are synchronized updatedFile.setEtag(""); } + + // eTag on Server is used for thumbnail validation + updatedFile.setEtagOnServer(remoteFile.getEtag()); } @NonNull diff --git a/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java b/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java index 7c955de72b..254911a2a5 100644 --- a/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java +++ b/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java @@ -26,19 +26,21 @@ import android.accounts.Account; import android.content.Context; 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.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation; import com.owncloud.android.operations.common.SyncOperation; +import com.owncloud.android.utils.DisplayUtils; /** * Remote operation performing the removal of a remote file or folder in the ownCloud server. */ public class RemoveFileOperation extends SyncOperation { + private final static String TAG = RemoveFileOperation.class.getSimpleName(); private OCFile fileToRemove; private String remotePath; @@ -92,7 +94,11 @@ public class RemoveFileOperation extends SyncOperation { fileToRemove = getStorageManager().getFileByPath(remotePath); // store resized image - ThumbnailsCacheManager.generateResizedImage(fileToRemove); + try { + DisplayUtils.generateResizedImage(fileToRemove, context); + } catch (Exception e) { + Log_OC.e(TAG, "Thumbnail generation failed", e); + } boolean localRemovalFailed = false; if (!onlyLocalCopy) { diff --git a/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java b/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java index 2ec650b5ca..edd168bb06 100644 --- a/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -23,7 +23,6 @@ package com.owncloud.android.operations; import android.accounts.Account; import android.content.Context; import android.content.Intent; -import android.util.Log; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; @@ -39,7 +38,6 @@ import com.owncloud.android.lib.resources.files.RemoteFile; import com.owncloud.android.operations.common.SyncOperation; import com.owncloud.android.services.OperationsService; import com.owncloud.android.utils.FileStorageUtils; -import com.owncloud.android.utils.MimeTypeUtil; import java.io.File; import java.util.HashMap; @@ -332,11 +330,6 @@ public class SynchronizeFolderOperation extends SyncOperation { if (updatedFile.isFolder()) { updatedFile.setFileLength(localFile.getFileLength()); // TODO move operations about size of folders to FileContentProvider - } else if (mRemoteFolderChanged && MimeTypeUtil.isImage(remoteFile) && - remoteFile.getModificationTimestamp() != - localFile.getModificationTimestamp()) { - updatedFile.setNeedsUpdateThumbnail(true); - Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server"); } updatedFile.setPublicLink(localFile.getPublicLink()); updatedFile.setShareViaLink(localFile.isSharedViaLink()); diff --git a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 4f2d5b6d25..dbb95947ee 100644 --- a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -35,7 +35,6 @@ import com.owncloud.android.datamodel.DecryptedFolderMetadata; import com.owncloud.android.datamodel.EncryptedFolderMetadata; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.db.OCUpload; import com.owncloud.android.files.services.FileUploader; @@ -59,6 +58,7 @@ import com.owncloud.android.lib.resources.files.UpdateMetadataOperation; import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation; import com.owncloud.android.operations.common.SyncOperation; import com.owncloud.android.utils.ConnectivityUtils; +import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.EncryptionUtils; import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.MimeType; @@ -1332,16 +1332,13 @@ public class UploadFileOperation extends SyncOperation { // coincidence; nothing else is needed, the storagePath is right // in the instance returned by mCurrentUpload.getFile() } - file.setNeedsUpdateThumbnail(true); getStorageManager().saveFile(file); getStorageManager().saveConflict(file, null); FileDataStorageManager.triggerMediaScan(file.getStoragePath()); // generate new Thumbnail - final ThumbnailsCacheManager.ThumbnailGenerationTask task = - new ThumbnailsCacheManager.ThumbnailGenerationTask(getStorageManager(), mAccount); - task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, file.getRemoteId())); + DisplayUtils.generateThumbnail(file, mOriginalStoragePath, getContext()); } private void updateOCFile(OCFile file, RemoteFile remoteFile) { @@ -1352,6 +1349,7 @@ public class UploadFileOperation extends SyncOperation { file.setModificationTimestampAtLastSyncForData(remoteFile.getModifiedTimestamp()); file.setEtag(remoteFile.getEtag()); file.setRemoteId(remoteFile.getRemoteId()); + file.setEtagOnServer(remoteFile.getEtag()); } public interface OnRenameListener { diff --git a/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java b/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java deleted file mode 100644 index 3759086b78..0000000000 --- a/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Tobias Kaminsky - * Copyright (C) 2017 Tobias Kaminsky - * Copyright (C) 2017 Nextcloud GmbH. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.owncloud.android.providers; - -import android.accounts.Account; -import android.content.ContentProvider; -import android.content.ContentValues; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.graphics.Bitmap; -import android.net.Uri; -import android.os.ParcelFileDescriptor; -import android.provider.OpenableColumns; -import android.support.annotation.NonNull; - -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 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(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException { - OCFile ocFile = getFile(uri); - - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + ocFile.getRemoteId())); - - // fallback to thumbnail - if (thumbnail == null) { - thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(ThumbnailsCacheManager.PREFIX_THUMBNAIL + ocFile.getRemoteId())); - } - - // fallback to default image - if (thumbnail == null) { - thumbnail = ThumbnailsCacheManager.mDefaultImg; - } - - // create a file to write bitmap data - File f = new File(MainApp.getAppContext().getCacheDir(), ocFile.getFileName()); - try { - f.createNewFile(); - - //Convert bitmap to byte array - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - thumbnail.compress(Bitmap.CompressFormat.JPEG, 90, bos); - byte[] bitmapData = bos.toByteArray(); - - //write the bytes in file - try (FileOutputStream fos = new FileOutputStream(f)){ - fos.write(bitmapData); - } catch (FileNotFoundException e) { - Log_OC.e(TAG, "File not found: " + e.getMessage()); - } - - } catch (Exception e) { - Log_OC.e(TAG, "Error opening file: " + e.getMessage()); - } - - return ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY); - } - - @Override - public String getType(@NonNull Uri uri) { - OCFile ocFile = getFile(uri); - return ocFile.getMimeType(); - } - - @Override - public Cursor query(@NonNull 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(@NonNull Uri uri, ContentValues values) { - return null; - } - - @Override - public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) { - return 0; - } - - @Override - public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) { - return 0; - } -} diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 2c2770c248..75f514e7bd 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -84,7 +84,7 @@ public class FileContentProvider extends ContentProvider { private static final String TEXT = " TEXT, "; private static final String ALTER_TABLE = "ALTER TABLE "; private static final String ADD_COLUMN = " ADD COLUMN "; - private static final String REMOVE_COLUMN = " REMOVE COLUMN "; + private static final String REMOVE_COLUMN = " DROP COLUMN "; private static final String UPGRADE_VERSION_MSG = "OUT of the ADD in onUpgrade; oldVersion == %d, newVersion == %d"; private static final int SINGLE_PATH_SEGMENT = 1; public static final int ARBITRARY_DATA_TABLE_INTRODUCTION_VERSION = 20; @@ -738,11 +738,11 @@ public class FileContentProvider extends ContentProvider { + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + INTEGER + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + INTEGER + ProviderTableMeta.FILE_ETAG + TEXT + + ProviderTableMeta.FILE_ETAG_ON_SERVER + TEXT + ProviderTableMeta.FILE_SHARED_VIA_LINK + INTEGER + ProviderTableMeta.FILE_PUBLIC_LINK + TEXT + ProviderTableMeta.FILE_PERMISSIONS + " TEXT null," + ProviderTableMeta.FILE_REMOTE_ID + " TEXT null," - + ProviderTableMeta.FILE_UPDATE_THUMBNAIL + INTEGER //boolean + ProviderTableMeta.FILE_IS_DOWNLOADING + INTEGER //boolean + ProviderTableMeta.FILE_FAVORITE + INTEGER // boolean + ProviderTableMeta.FILE_IS_ENCRYPTED + INTEGER // boolean @@ -1745,6 +1745,24 @@ public class FileContentProvider extends ContentProvider { if (!upgraded) { Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); } + + if (oldVersion < 35 && newVersion >= 35) { + Log_OC.i(SQL, "Entering in the #35 add eTagOnServer"); + db.beginTransaction(); + try { + db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME + + ADD_COLUMN + ProviderTableMeta.FILE_ETAG_ON_SERVER + " TEXT "); + + upgraded = true; + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + + if (!upgraded) { + Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); + } } @Override diff --git a/src/main/java/com/owncloud/android/ui/CustomPopup.java b/src/main/java/com/owncloud/android/ui/CustomPopup.java index 5eea6d4a35..a4c8b55149 100644 --- a/src/main/java/com/owncloud/android/ui/CustomPopup.java +++ b/src/main/java/com/owncloud/android/ui/CustomPopup.java @@ -32,6 +32,8 @@ import android.view.ViewGroup.LayoutParams; import android.view.WindowManager;
import android.widget.PopupWindow;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
/**
* Represents a custom PopupWindows
*/
@@ -121,6 +123,7 @@ public class CustomPopup { showLikeQuickAction(0, 0);
}
+ @SuppressFBWarnings("CLI")
public void showLikeQuickAction(int x, int y) {
preShow();
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 08901133a1..6e8427a3a8 100644 --- a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -39,6 +39,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.design.widget.NavigationView; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; @@ -56,9 +57,8 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.animation.GlideAnimation; import com.bumptech.glide.request.target.SimpleTarget; +import com.bumptech.glide.request.transition.Transition; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; @@ -94,6 +94,7 @@ import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.DrawerMenuUtil; import com.owncloud.android.utils.FilesSyncHelper; import com.owncloud.android.utils.ThemeUtils; +import com.owncloud.android.utils.glide.GlideKey; import com.owncloud.android.utils.svg.MenuSimpleTarget; import org.greenrobot.eventbus.EventBus; @@ -630,11 +631,8 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU // activate second/end account avatar if (mAvatars[1] != null) { - View accountEndView = findNavigationViewChildById(R.id.drawer_account_end); - accountEndView.setTag(mAvatars[1].name); - - DisplayUtils.setAvatar(mAvatars[1], this, mOtherAccountAvatarRadiusDimension, getResources(), - accountEndView, this); + ImageView accountEndView = (ImageView) findNavigationViewChildById(R.id.drawer_account_end); + DisplayUtils.setAvatar(mAvatars[1], this, accountEndView, mOtherAccountAvatarRadiusDimension); mAccountEndAccountAvatar.setVisibility(View.VISIBLE); } else { mAccountEndAccountAvatar.setVisibility(View.GONE); @@ -642,11 +640,8 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU // activate third/middle account avatar if (mAvatars[2] != null) { - View accountMiddleView = findNavigationViewChildById(R.id.drawer_account_middle); - accountMiddleView.setTag(mAvatars[2].name); - - DisplayUtils.setAvatar(mAvatars[2], this, mOtherAccountAvatarRadiusDimension, getResources(), - accountMiddleView, this); + ImageView accountMiddleView = (ImageView) findNavigationViewChildById(R.id.drawer_account_middle); + DisplayUtils.setAvatar(mAvatars[2], this, accountMiddleView, mOtherAccountAvatarRadiusDimension); mAccountMiddleAccountAvatar.setVisibility(View.VISIBLE); } else { mAccountMiddleAccountAvatar.setVisibility(View.GONE); @@ -666,12 +661,11 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU private void repopulateAccountList(List<Account> accounts) { // remove all accounts from list mNavigationView.getMenu().removeGroup(R.id.drawer_menu_accounts); - + SimpleTarget<Drawable> menuTarget; // add all accounts to list for (Account account: accounts) { try { // show all accounts except the currently active one and those pending for removal - if (!getAccount().name.equals(account.name)) { MenuItem accountMenuItem = mNavigationView.getMenu().add( R.id.drawer_menu_accounts, @@ -679,8 +673,16 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU MENU_ORDER_ACCOUNT, account.name) .setIcon(TextDrawable.createAvatar(account.name, mMenuAccountAvatarRadiusDimension)); - DisplayUtils.setAvatar(account, this, mMenuAccountAvatarRadiusDimension, getResources(), - accountMenuItem, this); + + menuTarget = new SimpleTarget<Drawable>() { + @Override + public void onResourceReady(@NonNull Drawable resource, + @Nullable Transition<? super Drawable> transition) { + accountMenuItem.setIcon(resource); + } + }; + + DisplayUtils.setAvatar(account, this, menuTarget, mMenuAccountAvatarRadiusDimension); } } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for account menu item.", e); @@ -748,11 +750,9 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU username.setText(AccountUtils.getAccountUsername(account.name)); } - View currentAccountView = findNavigationViewChildById(R.id.drawer_current_account); - currentAccountView.setTag(account.name); + ImageView currentAccountView = (ImageView) findNavigationViewChildById(R.id.drawer_current_account); - DisplayUtils.setAvatar(account, this, mCurrentAccountAvatarRadiusDimension, getResources(), - currentAccountView, this); + DisplayUtils.setAvatar(account, this, currentAccountView, mCurrentAccountAvatarRadiusDimension); // check and show quota info if available getAndDisplayUserQuota(); @@ -862,31 +862,26 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU mQuotaTextLink.setText(firstQuota.name); mQuotaTextLink.setClickable(true); mQuotaTextLink.setVisibility(View.VISIBLE); - mQuotaTextLink.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class); - externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, firstQuota.name); - externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, firstQuota.url); - externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, true); - externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, -1); - startActivity(externalWebViewIntent); - } + mQuotaTextLink.setOnClickListener(v -> { + Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class); + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, firstQuota.name); + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, firstQuota.url); + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, true); + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, -1); + startActivity(externalWebViewIntent); }); - - SimpleTarget target = new SimpleTarget<Drawable>() { + SimpleTarget<Drawable> target = new SimpleTarget<Drawable>() { @Override - public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) { + public void onResourceReady(@NonNull Drawable resource, + @Nullable Transition<? super Drawable> transition) { Drawable test = resource.getCurrent(); test.setBounds(0, 0, size, size); mQuotaTextLink.setCompoundDrawablesWithIntrinsicBounds(test, null, null, null); } @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - + public void onLoadFailed(@Nullable Drawable errorDrawable) { Drawable test = errorDrawable.getCurrent(); test.setBounds(0, 0, size, size); @@ -894,8 +889,8 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU } }; - DisplayUtils.downloadIcon(this, firstQuota.iconUrl, target, R.drawable.ic_link_grey, size, size); - + DisplayUtils.downloadIcon(this, firstQuota.iconUrl, target, R.drawable.ic_link_grey, + R.drawable.ic_link_grey); } else { mQuotaTextLink.setVisibility(View.GONE); } @@ -1018,28 +1013,28 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU mNavigationView.getMenu().removeGroup(R.id.drawer_menu_external_links); float density = getResources().getDisplayMetrics().density; - final int size = Math.round(24 * density); int greyColor = getResources().getColor(R.color.standard_grey); + MenuSimpleTarget<Drawable> target; for (final ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.LINK)) { int id = mNavigationView.getMenu().add(R.id.drawer_menu_external_links, MENU_ITEM_EXTERNAL_LINK + link.id, MENU_ORDER_EXTERNAL_LINKS, link.name) .setCheckable(true).getItemId(); - MenuSimpleTarget target = new MenuSimpleTarget<Drawable>(id) { + target = new MenuSimpleTarget<Drawable>(id) { @Override - public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) { + public void onResourceReady(@NonNull Drawable resource, + @Nullable Transition<? super Drawable> transition) { setExternalLinkIcon(getIdMenuItem(), resource, greyColor); } @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); + public void onLoadFailed(@Nullable Drawable errorDrawable) { setExternalLinkIcon(getIdMenuItem(), errorDrawable, greyColor); } }; - DisplayUtils.downloadIcon(this, link.iconUrl, target, R.drawable.ic_link_grey, size, size); + DisplayUtils.downloadIcon(this, link.iconUrl, target, R.drawable.ic_link_grey, R.drawable.ic_link_grey); } setDrawerMenuItemChecked(mCheckedMenuItem); @@ -1083,16 +1078,17 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU // use url if (URLUtil.isValidUrl(background) || background.isEmpty()) { // background image - SimpleTarget target = new SimpleTarget<Drawable>() { + SimpleTarget<Drawable> target = new SimpleTarget<Drawable>() { @Override - public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) { + public void onResourceReady(@NonNull Drawable resource, + @Nullable Transition<? super Drawable> transition) { Drawable[] drawables = {new ColorDrawable(primaryColor), resource}; LayerDrawable layerDrawable = new LayerDrawable(drawables); setNavigationHeaderBackground(layerDrawable, navigationHeader); } @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { + public void onLoadFailed(@Nullable Drawable errorDrawable) { Drawable[] drawables = {new ColorDrawable(primaryColor), errorDrawable}; LayerDrawable layerDrawable = new LayerDrawable(drawables); setNavigationHeaderBackground(layerDrawable, navigationHeader); @@ -1107,13 +1103,8 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU backgroundResource = R.drawable.background; } - Glide.with(this) - .load(background) - .centerCrop() - .placeholder(backgroundResource) - .error(backgroundResource) - .crossFade() - .into(target); + DisplayUtils.downloadImage(background, backgroundResource, backgroundResource, target, + GlideKey.url(background), this); } else { // plain color setNavigationHeaderBackground(new ColorDrawable(primaryColor), navigationHeader); diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 835a2f2b21..c81fdc25da 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -441,7 +441,7 @@ public class FileDisplayActivity extends HookActivity setFile(file); if (mAccountWasSet) { - setAccountInDrawer(getAccount()); + // setAccountInDrawer(getAccount()); // no need to call this here, as updateAccountList will be used setupDrawer(); } diff --git a/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java b/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java index 56247affd1..c1ae3c50c7 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java @@ -58,7 +58,7 @@ public class ManageSpaceActivity extends AppCompatActivity { clearDataButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - ClearDataAsynTask clearDataTask = new ClearDataAsynTask(); + ClearDataAsyncTask clearDataTask = new ClearDataAsyncTask(); clearDataTask.execute(); } }); @@ -82,7 +82,7 @@ public class ManageSpaceActivity extends AppCompatActivity { /** * AsyncTask for Clear Data, saving the passcode */ - private class ClearDataAsynTask extends AsyncTask<Void, Void, Boolean>{ + private class ClearDataAsyncTask extends AsyncTask<Void, Void, Boolean> { @Override protected Boolean doInBackground(Void... params) { diff --git a/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java b/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java index be7eb670c3..8ddd6fd56c 100755 --- a/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java @@ -776,7 +776,7 @@ public class ReceiveExternalFilesActivity extends FileActivity R.layout.uploader_list_item_layout, new String[]{"dirname"}, new int[]{R.id.filename}, - getStorageManager(), getAccount()); + getAccount()); mListView.setAdapter(sa); } diff --git a/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java b/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java index 2a3abdeb43..602062fc41 100644 --- a/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java @@ -56,9 +56,8 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.animation.GlideAnimation; import com.bumptech.glide.request.target.SimpleTarget; +import com.bumptech.glide.request.transition.Transition; import com.google.gson.Gson; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; @@ -68,11 +67,13 @@ import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation; import com.owncloud.android.ui.events.TokenPushEvent; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.PushUtils; import com.owncloud.android.utils.ThemeUtils; +import com.owncloud.android.utils.glide.GlideKey; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -221,34 +222,38 @@ public class UserInfoActivity extends FileActivity { ImageView backgroundImageView = appBar.findViewById(R.id.drawer_header_background); String background = getStorageManager().getCapability(account.name).getServerBackground(); - int primaryColor = ThemeUtils.primaryColor(getAccount(), false, this); + int primaryColor = ThemeUtils.primaryColor(account, false, this); if (URLUtil.isValidUrl(background)) { + Drawable backgroundResource; + OwnCloudVersion ownCloudVersion = AccountUtils.getServerVersion(account); + if (ownCloudVersion.compareTo(OwnCloudVersion.nextcloud_13) >= 0) { + backgroundResource = getResources().getDrawable(R.drawable.background_nc13); + } else { + backgroundResource = getResources().getDrawable(R.drawable.background); + } + // background image - SimpleTarget target = new SimpleTarget<Drawable>() { + SimpleTarget<Drawable> target = new SimpleTarget<Drawable>() { @Override - public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) { + public void onResourceReady(@NonNull Drawable resource, + @Nullable Transition<? super Drawable> transition) { Drawable[] drawables = {new ColorDrawable(primaryColor), resource}; LayerDrawable layerDrawable = new LayerDrawable(drawables); backgroundImageView.setImageDrawable(layerDrawable); } @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { + public void onLoadFailed(@Nullable Drawable errorDrawable) { Drawable[] drawables = {new ColorDrawable(primaryColor), - getResources().getDrawable(R.drawable.background)}; + backgroundResource}; LayerDrawable layerDrawable = new LayerDrawable(drawables); backgroundImageView.setImageDrawable(layerDrawable); } }; - Glide.with(this) - .load(background) - .centerCrop() - .placeholder(R.drawable.background) - .error(R.drawable.background) - .crossFade() - .into(target); + DisplayUtils.downloadImage(background, R.drawable.background, R.drawable.background, target, + GlideKey.url(background), this); } else { // plain color backgroundImageView.setImageDrawable(new ColorDrawable(primaryColor)); @@ -259,8 +264,8 @@ public class UserInfoActivity extends FileActivity { private void populateUserInfoUi(UserInfo userInfo) { userName.setText(account.name); - avatar.setTag(account.name); - DisplayUtils.setAvatar(account, this, mCurrentAccountAvatarRadiusDimension, getResources(), avatar, this); + + DisplayUtils.setAvatar(account, this, avatar, mCurrentAccountAvatarRadiusDimension); int tint = ThemeUtils.primaryColor(account, true, this); @@ -485,6 +490,7 @@ public class UserInfoActivity extends FileActivity { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View view = inflater.inflate(R.layout.user_info_details_table_item, parent, false); + return new ViewHolder(view); } diff --git a/src/main/java/com/owncloud/android/ui/adapter/AccountListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/AccountListAdapter.java index 34b1159ada..35763cafd1 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/AccountListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/AccountListAdapter.java @@ -3,16 +3,16 @@ * * @author Andy Scherzinger * Copyright (C) 2016 ownCloud Inc. - * <p/> + * * 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. - * <p/> + * * 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. - * <p/> + * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -155,10 +155,7 @@ public class AccountListAdapter extends ArrayAdapter<AccountListItem> implements private void setAvatar(AccountViewHolderItem viewHolder, Account account) { try { - View viewItem = viewHolder.imageViewItem; - viewItem.setTag(account.name); - DisplayUtils.setAvatar(account, this, mAccountAvatarRadiusDimension, mContext.getResources(), viewItem, - mContext); + DisplayUtils.setAvatar(account, mContext, viewHolder.imageViewItem, mAccountAvatarRadiusDimension); } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for account list item.", e); // use user icon as a fallback diff --git a/src/main/java/com/owncloud/android/ui/adapter/ActivityListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/ActivityListAdapter.java index 7bbf165e48..aec89ac31e 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/ActivityListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/ActivityListAdapter.java @@ -22,8 +22,6 @@ package com.owncloud.android.ui.adapter; import android.content.Context; import android.content.res.Resources; import android.graphics.Color; -import android.graphics.drawable.PictureDrawable; -import android.net.Uri; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.text.Spannable; @@ -44,12 +42,6 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -import com.bumptech.glide.GenericRequestBuilder; -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.model.StreamEncoder; -import com.bumptech.glide.load.resource.file.FileToStreamDecoder; -import com.caverock.androidsvg.SVG; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -63,12 +55,7 @@ import com.owncloud.android.lib.resources.files.FileUtils; import com.owncloud.android.ui.interfaces.ActivityListInterface; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; -import com.owncloud.android.utils.glide.CustomGlideStreamLoader; -import com.owncloud.android.utils.svg.SvgDecoder; -import com.owncloud.android.utils.svg.SvgDrawableTranscoder; -import com.owncloud.android.utils.svg.SvgSoftwareLayerSetter; -import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -175,7 +162,8 @@ public class ActivityListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH } if (!TextUtils.isEmpty(activity.getIcon())) { - downloadIcon(activity.getIcon(), activityViewHolder.activityIcon); + DisplayUtils.downloadSVG(activity.getIcon(), R.drawable.ic_activity_light_grey, + R.drawable.ic_activity_light_grey, activityViewHolder.activityIcon, context); } if (activity.getRichSubjectElement() != null && @@ -240,20 +228,11 @@ public class ActivityListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH // No Folder if (!file.isFolder()) { if (MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file)) { - int placeholder; - - if (MimeTypeUtil.isImage(file)) { - placeholder = R.drawable.file_image; + if (TextUtils.isEmpty(file.getEtag())) { + DisplayUtils.downloadActivityThumbnail(file, fileIcon, client, context); } else { - placeholder = R.drawable.file_movie; + DisplayUtils.downloadThumbnail(file, fileIcon, client, context); } - - String uri = client.getBaseUri() + "/index.php/apps/files/api/v1/thumbnail/" + px + "/" + px + - Uri.encode(file.getRemotePath(), "/"); - - Glide.with(context).using(new CustomGlideStreamLoader()).load(uri).placeholder(placeholder) - .error(placeholder).into(fileIcon); // using custom fetcher - } else { if (isDetailView) { fileIcon.setVisibility(View.GONE); @@ -274,27 +253,6 @@ public class ActivityListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH } } - private void downloadIcon(String icon, ImageView itemViewType) { - GenericRequestBuilder<Uri, InputStream, SVG, PictureDrawable> requestBuilder = Glide.with(context) - .using(Glide.buildStreamModelLoader(Uri.class, context), InputStream.class) - .from(Uri.class) - .as(SVG.class) - .transcode(new SvgDrawableTranscoder(), PictureDrawable.class) - .sourceEncoder(new StreamEncoder()) - .cacheDecoder(new FileToStreamDecoder<>(new SvgDecoder())) - .decoder(new SvgDecoder()) - .placeholder(R.drawable.ic_activity) - .error(R.drawable.ic_activity) - .animate(android.R.anim.fade_in) - .listener(new SvgSoftwareLayerSetter<>()); - - Uri uri = Uri.parse(icon); - requestBuilder - .diskCacheStrategy(DiskCacheStrategy.SOURCE) - .load(uri) - .into(itemViewType); - } - private SpannableStringBuilder addClickablePart(RichElement richElement) { String text = richElement.getRichSubject(); SpannableStringBuilder ssb = new SpannableStringBuilder(text); 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 1702e39b94..a55b19fa8f 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java @@ -23,7 +23,6 @@ package com.owncloud.android.ui.adapter; import android.content.Context; import android.content.res.Resources; import android.database.DataSetObserver; -import android.graphics.Bitmap; import android.graphics.Color; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; @@ -37,7 +36,6 @@ import android.widget.ListView; import android.widget.TextView; import com.owncloud.android.R; -import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.db.PreferenceManager; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.interfaces.LocalFileListFragmentInterface; @@ -45,6 +43,7 @@ import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.FileSortOrder; import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.ThemeUtils; +import com.owncloud.android.utils.glide.GlideKey; import java.io.File; import java.util.ArrayList; @@ -192,9 +191,14 @@ public class LocalFileListAdapter extends RecyclerView.Adapter<RecyclerView.View gridViewHolder.checkbox.setImageResource(R.drawable.ic_checkbox_blank_outline); } - gridViewHolder.thumbnail.setTag(file.hashCode()); setThumbnail(file, gridViewHolder.thumbnail); + if (MimeTypeUtil.isVideo(file)) { + gridViewHolder.playIcon.setVisibility(View.VISIBLE); + } else { + gridViewHolder.playIcon.setVisibility(View.GONE); + } + if (file.isDirectory()) { gridViewHolder.checkbox.setVisibility(View.GONE); } else { @@ -236,45 +240,10 @@ public class LocalFileListAdapter extends RecyclerView.Adapter<RecyclerView.View if (file.isDirectory()) { thumbnailView.setImageDrawable(MimeTypeUtil.getDefaultFolderIcon(mContext)); } else { - thumbnailView.setImageResource(R.drawable.file); - - /* Cancellation needs do be checked and done before changing the drawable in fileIcon, or - * {@link ThumbnailsCacheManager#cancelPotentialThumbnailWork} will NEVER cancel any task. - */ - boolean allowedToCreateNewThumbnail = ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailView); - - - // get Thumbnail if file is image - if (MimeTypeUtil.isImage(file)) { - // Thumbnail in Cache? - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - ThumbnailsCacheManager.PREFIX_THUMBNAIL + file.hashCode() - ); - if (thumbnail != null) { - thumbnailView.setImageBitmap(thumbnail); - } else { - - // generate new Thumbnail - if (allowedToCreateNewThumbnail) { - final ThumbnailsCacheManager.ThumbnailGenerationTask task = - new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView); - if (MimeTypeUtil.isVideo(file)) { - thumbnail = ThumbnailsCacheManager.mDefaultVideo; - } else { - thumbnail = ThumbnailsCacheManager.mDefaultImg; - } - final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncThumbnailDrawable( - mContext.getResources(), - thumbnail, - task - ); - thumbnailView.setImageDrawable(asyncDrawable); - 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 - } + if (MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file)) { + int placeholder = MimeTypeUtil.isImage(file) ? R.drawable.file_image : R.drawable.file_movie; + DisplayUtils.localImage(file, placeholder, placeholder, thumbnailView, GlideKey.localFile(file), + mContext); } else { thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(null, file.getName(), mContext)); } @@ -544,6 +513,7 @@ public class LocalFileListAdapter extends RecyclerView.Adapter<RecyclerView.View private final TextView fileName; private final ImageView checkbox; private final LinearLayout itemLayout; + private final ImageView playIcon; private LocalFileListGridViewHolder(View itemView) { super(itemView); @@ -552,6 +522,7 @@ public class LocalFileListAdapter extends RecyclerView.Adapter<RecyclerView.View fileName = itemView.findViewById(R.id.Filename); checkbox = itemView.findViewById(R.id.custom_checkbox); itemLayout = itemView.findViewById(R.id.ListItemLayout); + playIcon = itemView.findViewById(R.id.play_icon); itemView.findViewById(R.id.sharedIcon).setVisibility(View.GONE); itemView.findViewById(R.id.favorite_action).setVisibility(View.GONE); diff --git a/src/main/java/com/owncloud/android/ui/adapter/NotificationListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/NotificationListAdapter.java index dde75dc80b..19214a4eb6 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/NotificationListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/NotificationListAdapter.java @@ -23,7 +23,6 @@ import android.content.Intent; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.Typeface; -import android.graphics.drawable.PictureDrawable; import android.net.Uri; import android.os.AsyncTask; import android.support.annotation.NonNull; @@ -41,12 +40,6 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -import com.bumptech.glide.GenericRequestBuilder; -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.model.StreamEncoder; -import com.bumptech.glide.load.resource.file.FileToStreamDecoder; -import com.caverock.androidsvg.SVG; import com.owncloud.android.R; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; @@ -57,9 +50,6 @@ import com.owncloud.android.lib.resources.notifications.models.RichObject; import com.owncloud.android.ui.activity.NotificationsActivity; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.ThemeUtils; -import com.owncloud.android.utils.svg.SvgDecoder; -import com.owncloud.android.utils.svg.SvgDrawableTranscoder; -import com.owncloud.android.utils.svg.SvgSoftwareLayerSetter; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpStatus; @@ -68,7 +58,6 @@ import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -130,7 +119,8 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi // Todo set proper action icon (to be clarified how to pick) if (!TextUtils.isEmpty(notification.getIcon())) { - downloadIcon(notification.getIcon(), holder.icon); + DisplayUtils.downloadSVG(notification.getIcon(), R.drawable.ic_notification, R.drawable.ic_notification, + holder.icon, notificationsActivity); } // add action buttons @@ -237,28 +227,6 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi } } - private void downloadIcon(String icon, ImageView itemViewType) { - GenericRequestBuilder<Uri, InputStream, SVG, PictureDrawable> requestBuilder = Glide.with(notificationsActivity) - .using(Glide.buildStreamModelLoader(Uri.class, notificationsActivity), InputStream.class) - .from(Uri.class) - .as(SVG.class) - .transcode(new SvgDrawableTranscoder(), PictureDrawable.class) - .sourceEncoder(new StreamEncoder()) - .cacheDecoder(new FileToStreamDecoder<>(new SvgDecoder())) - .decoder(new SvgDecoder()) - .placeholder(R.drawable.ic_notification) - .error(R.drawable.ic_notification) - .animate(android.R.anim.fade_in) - .listener(new SvgSoftwareLayerSetter<>()); - - - Uri uri = Uri.parse(icon); - requestBuilder - .diskCacheStrategy(DiskCacheStrategy.SOURCE) - .load(uri) - .into(itemViewType); - } - private void openLink(String link) { notificationsActivity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(link))); } diff --git a/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index c2048f51de..7a43d66a14 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -21,16 +21,16 @@ package com.owncloud.android.ui.adapter; - import android.accounts.Account; import android.content.ContentValues; import android.content.Context; import android.content.res.Resources; -import android.graphics.Bitmap; import android.graphics.Color; +import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.view.LayoutInflater; @@ -41,16 +41,18 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import com.bumptech.glide.request.target.CustomViewTarget; +import com.bumptech.glide.request.transition.Transition; import com.owncloud.android.R; 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.datamodel.VirtualFolderType; import com.owncloud.android.db.PreferenceManager; import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; @@ -88,6 +90,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol private final FileUploader.FileUploaderBinder uploaderBinder; private final OperationsService.OperationsServiceBinder operationsServiceBinder; private Context mContext; + private OwnCloudClient client; private List<OCFile> mFiles = new ArrayList<>(); private List<OCFile> mFilesAll = new ArrayList<>(); private boolean mHideItemOptions; @@ -107,15 +110,15 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol private static final int VIEWTYPE_ITEM = 1; private static final int VIEWTYPE_IMAGE = 2; - private List<ThumbnailsCacheManager.ThumbnailGenerationTask> asyncTasks = new ArrayList<>(); private boolean onlyOnDevice = false; - public OCFileListAdapter(Context context, ComponentsGetter transferServiceGetter, + public OCFileListAdapter(Context context, OwnCloudClient client, ComponentsGetter transferServiceGetter, OCFileListFragmentInterface ocFileListFragmentInterface, boolean argHideItemOptions, boolean gridView) { this.ocFileListFragmentInterface = ocFileListFragmentInterface; mContext = context; + this.client = client; mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext); mHideItemOptions = argHideItemOptions; this.gridView = gridView; @@ -124,9 +127,6 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol downloaderBinder = transferServiceGetter.getFileDownloaderBinder(); uploaderBinder = transferServiceGetter.getFileUploaderBinder(); operationsServiceBinder = transferServiceGetter.getOperationsServiceBinder(); - - // initialise thumbnails cache on background thread - new ThumbnailsCacheManager.InitDiskCacheTask().execute(); } public boolean isMultiSelect() { @@ -263,8 +263,13 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol boolean gridImage = MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file); - gridViewHolder.thumbnail.setTag(file.getFileId()); - setThumbnail(file, gridViewHolder.thumbnail); + setThumbnail(file, gridViewHolder); + + if (MimeTypeUtil.isVideo(file)) { + gridViewHolder.playIcon.setVisibility(View.VISIBLE); + } else { + gridViewHolder.playIcon.setVisibility(View.GONE); + } if (isCheckedFile(file)) { gridViewHolder.itemLayout.setBackgroundColor(mContext.getResources() @@ -373,52 +378,42 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol } } - private void setThumbnail(OCFile file, ImageView thumbnailView) { + private void setThumbnail(OCFile file, OCFileListGridImageViewHolder gridImageViewHolder) { + ImageView thumbnailView = gridImageViewHolder.thumbnail; + if (file.isFolder()) { thumbnailView.setImageDrawable(MimeTypeUtil.getFolderTypeIcon(file.isSharedWithMe() || file.isSharedWithSharee(), file.isSharedViaLink(), file.isEncrypted(), file.getMountType(), mContext)); } else { if ((MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file)) && file.getRemoteId() != null) { - // Thumbnail in cache? - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - ThumbnailsCacheManager.PREFIX_THUMBNAIL + file.getRemoteId() - ); - - if (thumbnail != null && !file.needsUpdateThumbnail()) { - if (MimeTypeUtil.isVideo(file)) { - Bitmap withOverlay = ThumbnailsCacheManager.addVideoOverlay(thumbnail); - thumbnailView.setImageBitmap(withOverlay); - } else { - thumbnailView.setImageBitmap(thumbnail); + + if (client == null) { + client = AccountUtils.getClientForCurrentAccount(mContext); + } + + CustomViewTarget thumbnailTarget = new CustomViewTarget<ImageView, Drawable>(thumbnailView) { + @Override + protected void onResourceCleared(@Nullable Drawable placeholder) { + thumbnailView.setImageDrawable(placeholder); } - } else { - // generate new thumbnail - if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailView)) { - try { - final ThumbnailsCacheManager.ThumbnailGenerationTask task = - new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView, mStorageManager, - mAccount, asyncTasks); - - if (thumbnail == null) { - if (MimeTypeUtil.isVideo(file)) { - thumbnail = ThumbnailsCacheManager.mDefaultVideo; - } else { - thumbnail = ThumbnailsCacheManager.mDefaultImg; - } - } - final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncThumbnailDrawable(mContext.getResources(), - thumbnail, task); - thumbnailView.setImageDrawable(asyncDrawable); - asyncTasks.add(task); - task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, - file.getRemoteId())); - } catch (IllegalArgumentException e) { - Log_OC.d(TAG, "ThumbnailGenerationTask : " + e.getMessage()); - } + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + int placeholder = MimeTypeUtil.isVideo(file) ? R.drawable.file_movie : R.drawable.file_image; + thumbnailView.setImageResource(placeholder); + gridImageViewHolder.playIcon.setVisibility(View.GONE); + } + + @Override + public void onResourceReady(@NonNull Drawable resource, + @Nullable Transition<? super Drawable> transition) { + thumbnailView.setImageDrawable(resource); } - } + + }; + + DisplayUtils.downloadThumbnail(file, thumbnailTarget, client, mContext); if ("image/png".equalsIgnoreCase(file.getMimeType())) { thumbnailView.setBackgroundColor(mContext.getResources().getColor(R.color.background_color)); @@ -789,20 +784,6 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol return ret; } - public void cancelAllPendingTasks() { - for (ThumbnailsCacheManager.ThumbnailGenerationTask task : asyncTasks) { - if (task != null) { - task.cancel(true); - if (task.getGetMethod() != null) { - Log_OC.d(TAG, "cancel: abort get method directly"); - task.getGetMethod().abort(); - } - } - } - - asyncTasks.clear(); - } - public void setGridView(boolean bool) { gridView = bool; } @@ -838,6 +819,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol private final ImageView localFileIndicator; private final ImageView shared; private final ImageView checkbox; + private final ImageView playIcon; private final LinearLayout itemLayout; private OCFileListGridImageViewHolder(View itemView) { @@ -849,6 +831,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol localFileIndicator = itemView.findViewById(R.id.localFileIndicator); shared = itemView.findViewById(R.id.sharedIcon); checkbox = itemView.findViewById(R.id.custom_checkbox); + playIcon = itemView.findViewById(R.id.play_icon); itemLayout = itemView.findViewById(R.id.ListItemLayout); } } diff --git a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java index e7af2e555a..639963c851 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java @@ -22,6 +22,7 @@ package com.owncloud.android.ui.adapter; import android.content.Context; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; @@ -37,8 +38,10 @@ import com.afollestad.sectionedrecyclerview.SectionedViewHolder; import com.owncloud.android.R; import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; -import com.owncloud.android.datamodel.ThumbnailsCacheManager; +import com.owncloud.android.utils.DisplayUtils; +import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.ThemeUtils; +import com.owncloud.android.utils.glide.GlideKey; import java.io.File; import java.util.ArrayList; @@ -179,21 +182,8 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SyncedFold if (mSyncFolderItems.get(section).getFilePaths() != null) { File file = new File(mSyncFolderItems.get(section).getFilePaths().get(relativePosition)); - ThumbnailsCacheManager.MediaThumbnailGenerationTask task = - new ThumbnailsCacheManager.MediaThumbnailGenerationTask(holder.image, mContext); - - ThumbnailsCacheManager.AsyncMediaThumbnailDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncMediaThumbnailDrawable( - mContext.getResources(), - ThumbnailsCacheManager.mDefaultImg, - task - ); - holder.image.setImageDrawable(asyncDrawable); - - task.execute(file); - - // set proper tag - holder.image.setTag(file.hashCode()); + int placeholder = MimeTypeUtil.isImage(file) ? R.drawable.file_image : R.drawable.file_movie; + DisplayUtils.localImage(file, placeholder, placeholder, holder.image, GlideKey.localFile(file), mContext); holder.itemView.setTag(relativePosition % mGridWidth); @@ -208,8 +198,9 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SyncedFold } } + @NonNull @Override - public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + public MainViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate( viewType == VIEW_TYPE_HEADER ? R.layout.synced_folders_item_header : R.layout.grid_sync_item, parent, false); diff --git a/src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java index edde6e2d3e..41e0589d6a 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java @@ -23,7 +23,6 @@ package com.owncloud.android.ui.adapter; import android.accounts.Account; import android.content.Context; import android.content.res.Resources; -import android.graphics.Bitmap; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -35,15 +34,15 @@ import android.widget.TextView; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.db.PreferenceManager; +import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.files.TrashbinFile; import com.owncloud.android.ui.interfaces.TrashbinActivityInterface; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.FileSortOrder; import com.owncloud.android.utils.MimeTypeUtil; +import com.owncloud.android.utils.glide.GlideKey; import java.util.ArrayList; import java.util.List; @@ -64,17 +63,15 @@ public class TrashbinListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH private List<TrashbinFile> files; private Context context; private Account account; - private FileDataStorageManager storageManager; + private OwnCloudClient client; - private List<ThumbnailsCacheManager.ThumbnailGenerationTask> asyncTasks = new ArrayList<>(); - - public TrashbinListAdapter(TrashbinActivityInterface trashbinActivityInterface, - FileDataStorageManager storageManager, Context context) { + public TrashbinListAdapter(TrashbinActivityInterface trashbinActivityInterface, Context context) { this.files = new ArrayList<>(); this.trashbinActivityInterface = trashbinActivityInterface; this.account = AccountUtils.getCurrentOwnCloudAccount(context); - this.storageManager = storageManager; this.context = context; + + client = AccountUtils.getClientForCurrentAccount(context); } public void setTrashbinFiles(List<Object> trashbinFiles, boolean clear) { @@ -113,7 +110,6 @@ public class TrashbinListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH trashbinFileViewHolder.itemLayout.setOnClickListener(v -> trashbinActivityInterface.onItemClicked(file)); // thumbnail - trashbinFileViewHolder.thumbnail.setTag(file.getRemoteId()); setThumbnail(file, trashbinFileViewHolder.thumbnail); // fileName @@ -208,42 +204,21 @@ public class TrashbinListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH if (file.isFolder()) { thumbnailView.setImageDrawable(MimeTypeUtil.getDefaultFolderIcon(context)); } else { - if ((MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file)) && file.getRemoteId() != null) { - // Thumbnail in cache? - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - ThumbnailsCacheManager.PREFIX_THUMBNAIL + file.getRemoteId() - ); - - if (thumbnail != null) { - if (MimeTypeUtil.isVideo(file)) { - Bitmap withOverlay = ThumbnailsCacheManager.addVideoOverlay(thumbnail); - thumbnailView.setImageBitmap(withOverlay); - } else { - thumbnailView.setImageBitmap(thumbnail); - } - } else { - // generate new thumbnail - if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailView)) { - try { - final ThumbnailsCacheManager.ThumbnailGenerationTask task = - new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView, storageManager, - account, asyncTasks); - - final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncThumbnailDrawable(context.getResources(), - thumbnail, task); - thumbnailView.setImageDrawable(asyncDrawable); - asyncTasks.add(task); - task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, - file.getRemoteId())); - } catch (IllegalArgumentException e) { - Log_OC.d(TAG, "ThumbnailGenerationTask : " + e.getMessage()); - } - } - } + if (MimeTypeUtil.isImageOrVideo(file)) { + try { + int placeholder = MimeTypeUtil.isImage(file) ? R.drawable.file_image : R.drawable.file_movie; + + int px = DisplayUtils.getThumbnailDimension(); + + String url = DisplayUtils.getThumbnailUri(client, file, px); + DisplayUtils.downloadImage(url, placeholder, placeholder, thumbnailView, client, + GlideKey.trashbinThumbnail(file), context); - if ("image/png".equalsIgnoreCase(file.getMimeType())) { - thumbnailView.setBackgroundColor(context.getResources().getColor(R.color.background_color)); + if ("image/png".equalsIgnoreCase(file.getMimeType())) { + thumbnailView.setBackgroundColor(context.getResources().getColor(R.color.background_color)); + } + } catch (Exception e) { + Log_OC.e(TAG, e.getMessage()); } } else { thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(), file.getFileName(), @@ -266,20 +241,6 @@ public class TrashbinListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH return files.size() + 1; } - public void cancelAllPendingTasks() { - for (ThumbnailsCacheManager.ThumbnailGenerationTask task : asyncTasks) { - if (task != null) { - task.cancel(true); - if (task.getGetMethod() != null) { - Log_OC.d(TAG, "cancel: abort get method directly"); - task.getGetMethod().abort(); - } - } - } - - asyncTasks.clear(); - } - public void setSortOrder(FileSortOrder sortOrder) { PreferenceManager.setSortOrder(context, null, sortOrder); files = sortOrder.sortTrashbinFiles(files); diff --git a/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index 4c1298ce46..b61bfe6bd2 100755 --- a/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -24,7 +24,6 @@ package com.owncloud.android.ui.adapter; import android.accounts.Account; import android.content.ActivityNotFoundException; import android.content.Intent; -import android.graphics.Bitmap; import android.net.Uri; import android.support.annotation.NonNull; import android.text.format.DateUtils; @@ -42,17 +41,18 @@ import com.afollestad.sectionedrecyclerview.SectionedViewHolder; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.datamodel.UploadsStorageManager.UploadStatus; import com.owncloud.android.db.OCUpload; import com.owncloud.android.db.UploadResult; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.ThemeUtils; +import com.owncloud.android.utils.glide.GlideKey; import java.io.File; import java.util.Arrays; @@ -66,6 +66,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedViewHolder> { private static final String TAG = UploadListAdapter.class.getSimpleName(); + private OwnCloudClient client; private ProgressListener mProgressListener; @@ -130,6 +131,9 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie fixAndSortItems(mUploadsStorageManager.getFinishedUploadsForCurrentAccount()); } }; + + client = AccountUtils.getClientForCurrentAccount(mParentActivity); + loadUploadItemsFromDb(); } @@ -285,100 +289,31 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie }); } } else { - itemViewHolder.itemLayout.setOnClickListener(v -> - onUploadItemClick(item)); + itemViewHolder.itemLayout.setOnClickListener(v -> onUploadItemClick(item)); } - // Set icon or thumbnail - itemViewHolder.thumbnail.setImageResource(R.drawable.file); - - /* - * Cancellation needs do be checked and done before changing the drawable in fileIcon, or - * {@link ThumbnailsCacheManager#cancelPotentialWork} will NEVER cancel any task. - */ - OCFile fakeFileToCheatThumbnailsCacheManagerInterface = new OCFile(item.getRemotePath()); - fakeFileToCheatThumbnailsCacheManagerInterface.setStoragePath(item.getLocalPath()); - fakeFileToCheatThumbnailsCacheManagerInterface.setMimetype(item.getMimeType()); - - boolean allowedToCreateNewThumbnail = ThumbnailsCacheManager.cancelPotentialThumbnailWork( - fakeFileToCheatThumbnailsCacheManagerInterface, itemViewHolder.thumbnail - ); - - // TODO this code is duplicated; refactor to a common place - if (MimeTypeUtil.isImage(fakeFileToCheatThumbnailsCacheManagerInterface) - && fakeFileToCheatThumbnailsCacheManagerInterface.getRemoteId() != null && - item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED) { - // Thumbnail in Cache? - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(fakeFileToCheatThumbnailsCacheManagerInterface.getRemoteId()) - ); - if (thumbnail != null && !fakeFileToCheatThumbnailsCacheManagerInterface.needsUpdateThumbnail()) { - itemViewHolder.thumbnail.setImageBitmap(thumbnail); - } else { - // generate new Thumbnail - if (allowedToCreateNewThumbnail) { - final ThumbnailsCacheManager.ThumbnailGenerationTask task = - new ThumbnailsCacheManager.ThumbnailGenerationTask( - itemViewHolder.thumbnail, mParentActivity.getStorageManager(), mParentActivity.getAccount() - ); - if (thumbnail == null) { - if (MimeTypeUtil.isVideo(fakeFileToCheatThumbnailsCacheManagerInterface)) { - thumbnail = ThumbnailsCacheManager.mDefaultVideo; - } else { - thumbnail = ThumbnailsCacheManager.mDefaultImg; - } - } - final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncThumbnailDrawable( - mParentActivity.getResources(), - thumbnail, - task - ); - itemViewHolder.thumbnail.setImageDrawable(asyncDrawable); - task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject( - fakeFileToCheatThumbnailsCacheManagerInterface, null)); - } - } + if (MimeTypeUtil.isImageOrVideo(new File(item.getLocalPath()))) { + if (item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED) { - if ("image/png".equals(item.getMimeType())) { - itemViewHolder.thumbnail.setBackgroundColor(mParentActivity.getResources() - .getColor(R.color.background_color)); - } + OCFile file = mParentActivity.getStorageManager().getFileByPath(item.getRemotePath()); + if (file != null) { + DisplayUtils.downloadThumbnail(file, itemViewHolder.thumbnail, client, mParentActivity); + } - } else if (MimeTypeUtil.isImage(fakeFileToCheatThumbnailsCacheManagerInterface)) { - File file = new File(item.getLocalPath()); - // Thumbnail in Cache? - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(file.hashCode())); - if (thumbnail != null) { - itemViewHolder.thumbnail.setImageBitmap(thumbnail); } else { - // generate new Thumbnail - if (allowedToCreateNewThumbnail) { - final ThumbnailsCacheManager.ThumbnailGenerationTask task = - new ThumbnailsCacheManager.ThumbnailGenerationTask(itemViewHolder.thumbnail); + File file = new File(item.getLocalPath()); - if (MimeTypeUtil.isVideo(file)) { - thumbnail = ThumbnailsCacheManager.mDefaultVideo; - } else { - thumbnail = ThumbnailsCacheManager.mDefaultImg; - } + int placeholder = MimeTypeUtil.isImage(new File(item.getLocalPath())) ? + R.drawable.file_image : R.drawable.file_movie; + DisplayUtils.localImage(file, placeholder, placeholder, itemViewHolder.thumbnail, + GlideKey.localFile(file), mParentActivity); - final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncThumbnailDrawable(mParentActivity.getResources(), thumbnail, - task); - - itemViewHolder.thumbnail.setImageDrawable(asyncDrawable); - task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, null)); - Log_OC.v(TAG, "Executing task to generate a new thumbnail"); + if ("image/png".equalsIgnoreCase(item.getMimeType())) { + itemViewHolder.thumbnail.setBackgroundColor(mParentActivity.getResources() + .getColor(R.color.background_color)); } } - - if ("image/png".equalsIgnoreCase(item.getMimeType())) { - itemViewHolder.thumbnail.setBackgroundColor(mParentActivity.getResources() - .getColor(R.color.background_color)); - } } else { itemViewHolder.thumbnail.setImageDrawable(MimeTypeUtil.getFileTypeIcon(item.getMimeType(), fileName, account, mParentActivity)); 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 61368ab5fb..835ee6ee60 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java @@ -1,4 +1,4 @@ -/** +/* * ownCloud Android client application * * @author Tobias Kaminsky @@ -22,7 +22,6 @@ package com.owncloud.android.ui.adapter; import android.accounts.Account; import android.content.Context; -import android.graphics.Bitmap; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -31,10 +30,9 @@ import android.widget.SimpleAdapter; import android.widget.TextView; import com.owncloud.android.R; -import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.datamodel.ThumbnailsCacheManager; -import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncThumbnailDrawable; +import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; @@ -43,21 +41,19 @@ import java.util.List; import java.util.Map; public class UploaderAdapter extends SimpleAdapter { - private Context mContext; private Account mAccount; - private FileDataStorageManager mStorageManager; private LayoutInflater inflater; + private OwnCloudClient client; public UploaderAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, - int[] to, FileDataStorageManager storageManager, Account account) { + int[] to, Account account) { super(context, data, resource, from, to); mAccount = account; - mStorageManager = storageManager; mContext = context; - inflater = (LayoutInflater) mContext - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + client = AccountUtils.getClientForCurrentAccount(context); } @Override @@ -74,7 +70,6 @@ public class UploaderAdapter extends SimpleAdapter { filename.setText(file.getFileName()); ImageView fileIcon = vi.findViewById(R.id.thumbnail); - fileIcon.setTag(file.getFileId()); TextView lastModV = vi.findViewById(R.id.last_mod); lastModV.setText(DisplayUtils.getRelativeTimestamp(mContext, file.getModificationTimestamp())); @@ -97,35 +92,8 @@ public class UploaderAdapter extends SimpleAdapter { file.getMountType(), mContext)); } else { // get Thumbnail if file is image - if (MimeTypeUtil.isImage(file) && file.getRemoteId() != null) { - // Thumbnail in Cache? - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(file.getRemoteId()) - ); - if (thumbnail != null && !file.needsUpdateThumbnail()) { - fileIcon.setImageBitmap(thumbnail); - } else { - // generate new Thumbnail - if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon)) { - final ThumbnailsCacheManager.ThumbnailGenerationTask task = - new ThumbnailsCacheManager.ThumbnailGenerationTask(fileIcon, mStorageManager, - mAccount); - if (thumbnail == null) { - if (MimeTypeUtil.isVideo(file)) { - thumbnail = ThumbnailsCacheManager.mDefaultVideo; - } else { - thumbnail = ThumbnailsCacheManager.mDefaultImg; - } - } - final AsyncThumbnailDrawable asyncDrawable = new AsyncThumbnailDrawable( - mContext.getResources(), - thumbnail, - task - ); - fileIcon.setImageDrawable(asyncDrawable); - task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, file.getRemoteId())); - } - } + if (MimeTypeUtil.isImageOrVideo(file)) { + DisplayUtils.downloadThumbnail(file, fileIcon, client, mContext); } else { fileIcon.setImageDrawable( MimeTypeUtil.getFileTypeIcon(file.getMimeType(), file.getFileName(), mAccount, mContext) @@ -135,6 +103,4 @@ public class UploaderAdapter extends SimpleAdapter { return vi; } - - } 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 e7122cfcd1..34450b3c20 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -25,6 +25,9 @@ package com.owncloud.android.ui.fragment; import android.accounts.Account; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -42,14 +45,20 @@ import android.widget.PopupMenu; import android.widget.ProgressBar; import android.widget.TextView; +import com.bumptech.glide.Priority; +import com.bumptech.glide.request.target.SimpleTarget; +import com.bumptech.glide.request.transition.Transition; import com.owncloud.android.MainApp; import com.owncloud.android.R; +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.files.FileMenuFilter; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; +import com.owncloud.android.lib.common.OwnCloudAccount; +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.activity.FileActivity; @@ -61,8 +70,12 @@ import com.owncloud.android.ui.dialog.RenameFileDialogFragment; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.ThemeUtils; +import com.owncloud.android.utils.glide.GlideApp; +import com.owncloud.android.utils.glide.GlideContainer; +import com.owncloud.android.utils.glide.GlideKey; import java.lang.ref.WeakReference; +import java.net.URLEncoder; import butterknife.BindView; import butterknife.ButterKnife; @@ -552,56 +565,86 @@ public class FileDetailFragment extends FileFragment implements OnClickListener * @param file a {@link OCFile} to be previewed */ private void setFilePreview(OCFile file) { - Bitmap resizedImage; - if (MimeTypeUtil.isImage(file) && activity != null) { - String tagId = String.valueOf(ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + getFile().getRemoteId()); - resizedImage = ThumbnailsCacheManager.getBitmapFromDiskCache(tagId); + if (MimeTypeUtil.isImage(file) && activity != null && activity.getPreviewImageView() != null) { + try { + Account account = AccountUtils.getCurrentOwnCloudAccount(getContext()); + OwnCloudAccount ocAccount = new OwnCloudAccount(account, MainApp.getAppContext()); + + OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). + getClientFor(ocAccount, MainApp.getAppContext()); + + int thumbnailW = DisplayUtils.getThumbnailDimension(); + int thumbnailH = DisplayUtils.getThumbnailDimension(); - if (resizedImage != null && !file.needsUpdateThumbnail()) { - activity.setPreviewImageBitmap(resizedImage); activatePreviewImage(); previewLoaded = true; - } else { - // show thumbnail while loading resized image - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(ThumbnailsCacheManager.PREFIX_THUMBNAIL + getFile().getRemoteId())); - if (thumbnail != null) { - activity.setPreviewImageBitmap(thumbnail); - } else { - thumbnail = ThumbnailsCacheManager.mDefaultImg; - } - - // generate new resized image - if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(getFile(), activity.getPreviewImageView()) && - mContainerActivity.getStorageManager() != null) { - final ThumbnailsCacheManager.ResizedImageGenerationTask task = - new ThumbnailsCacheManager.ResizedImageGenerationTask(this, - activity.getPreviewImageView(), - mContainerActivity.getStorageManager(), - mContainerActivity.getStorageManager().getAccount()); - - if (resizedImage == null) { - resizedImage = thumbnail; - } - - final ThumbnailsCacheManager.AsyncResizedImageDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncResizedImageDrawable( - MainApp.getAppContext().getResources(), - resizedImage, - task - ); - - activity.setPreviewImageDrawable(asyncDrawable); - activatePreviewImage(); - previewLoaded = true; - task.execute(getFile()); - } + // Thumbnail + GlideContainer container = new GlideContainer(); + int placeholder = MimeTypeUtil.isVideo(getFile()) ? R.drawable.file_movie : R.drawable.file_image; + + container.url = mClient.getBaseUri() + "/index.php/apps/files/api/v1/thumbnail/" + + thumbnailW + "/" + thumbnailH + Uri.encode(getFile().getRemotePath(), "/"); + container.client = mClient; + container.key = GlideKey.serverThumbnail(getFile()); + + // resized image + GlideContainer containerResizedImage = new GlideContainer(); + + Point p = DisplayUtils.getScreenDimension(); + int pxW = p.x; + int pxH = p.y; + + containerResizedImage.url = mClient.getBaseUri() + "/index.php/core/preview.png?file=" + + URLEncoder.encode(getFile().getRemotePath()) + + "&x=" + pxW + "&y=" + pxH + "&a=1&mode=cover&forceIcon=0"; + containerResizedImage.key = GlideKey.resizedImage(getFile()); + containerResizedImage.client = mClient; + + GlideApp.with(requireContext()) + .asBitmap() + .load(container) + .placeholder(placeholder) + .priority(Priority.IMMEDIATE) + .onlyRetrieveFromCache(true) + .into(new SimpleTarget<Bitmap>() { + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + activity.setPreviewImageDrawable(activity.getResources() + .getDrawable(placeholder)); + loadResizedImage(containerResizedImage); + } + + @Override + public void onResourceReady(@NonNull Bitmap resource, + @Nullable Transition<? super Bitmap> transition) { + activity.setPreviewImageBitmap(resource); + loadResizedImage(containerResizedImage); + } + } + ); + } catch (Exception e) { + Log_OC.e(TAG, e.getMessage()); } } } + private void loadResizedImage(GlideContainer containerResizedImage) { + GlideApp.with(requireContext()) + .asBitmap() + .load(containerResizedImage) + .priority(Priority.IMMEDIATE) + .into(new SimpleTarget<Bitmap>() { + @Override + public void onResourceReady(@NonNull Bitmap resource, + @Nullable Transition<? super Bitmap> transition) { + activity.setPreviewImageBitmap(resource); + } + } + ); + } + /** * Enables or disables buttons for a file being downloaded */ 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 72a57313ab..a6a2d98b91 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -25,8 +25,6 @@ package com.owncloud.android.ui.fragment; import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -68,9 +66,7 @@ import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.VirtualFolderType; import com.owncloud.android.db.PreferenceManager; import com.owncloud.android.files.FileMenuFilter; -import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; @@ -113,7 +109,6 @@ import org.greenrobot.eventbus.ThreadMode; import org.parceler.Parcels; import java.io.File; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -175,6 +170,7 @@ public class OCFileListFragment extends ExtendedListFragment implements private boolean searchFragment; private SearchEvent searchEvent; private AsyncTask remoteOperationAsyncTask; + private OwnCloudClient client; private enum MenuItemAddRemove { DO_NOTHING, REMOVE_SORT, REMOVE_GRID_AND_SORT, ADD_SORT, ADD_GRID_AND_SORT, ADD_GRID_AND_SORT_WITH_SEARCH, @@ -288,13 +284,6 @@ public class OCFileListFragment extends ExtendedListFragment implements super.onDetach(); } - @Override - public void onPause() { - super.onPause(); - mAdapter.cancelAllPendingTasks(); - } - - /** * {@inheritDoc} */ @@ -312,7 +301,9 @@ public class OCFileListFragment extends ExtendedListFragment implements mOnlyFoldersClickable = args != null && args.getBoolean(ARG_ONLY_FOLDERS_CLICKABLE, false); boolean hideItemOptions = args != null && args.getBoolean(ARG_HIDE_ITEM_OPTIONS, false); - mAdapter = new OCFileListAdapter(getActivity(), mContainerActivity, this, hideItemOptions, + client = AccountUtils.getClientForCurrentAccount(getContext()); + + mAdapter = new OCFileListAdapter(getActivity(), client, mContainerActivity, this, hideItemOptions, isGridViewPreferred(mFile)); setRecyclerViewAdapter(mAdapter); @@ -1317,36 +1308,21 @@ public class OCFileListFragment extends ExtendedListFragment implements public void onMessageEvent(FavoriteEvent event) { Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(MainApp.getAppContext()); - OwnCloudAccount ocAccount = null; AccountManager mAccountMgr = AccountManager.get(getActivity()); - try { - ocAccount = new OwnCloudAccount( - currentAccount, - MainApp.getAppContext() - ); - - OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, MainApp.getAppContext()); - - String userId = mAccountMgr.getUserData(currentAccount, - com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID); - - if (TextUtils.isEmpty(userId)) { - userId = mClient.getCredentials().getUsername(); - } + String userId = mAccountMgr.getUserData(currentAccount, + com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID); - ToggleFavoriteOperation toggleFavoriteOperation = new ToggleFavoriteOperation(event.shouldFavorite, - event.remotePath, userId); - RemoteOperationResult remoteOperationResult = toggleFavoriteOperation.execute(mClient); + if (TextUtils.isEmpty(userId)) { + userId = client.getCredentials().getUsername(); + } - if (remoteOperationResult.isSuccess()) { - mAdapter.setFavoriteAttributeForItemID(event.remoteId, event.shouldFavorite); - } + ToggleFavoriteOperation toggleFavoriteOperation = new ToggleFavoriteOperation(event.shouldFavorite, + event.remotePath, userId); + RemoteOperationResult remoteOperationResult = toggleFavoriteOperation.execute(client); - } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException | AuthenticatorException - | IOException | OperationCanceledException e) { - Log_OC.e(TAG, "Error processing event", e); + if (remoteOperationResult.isSuccess()) { + mAdapter.setFavoriteAttributeForItemID(event.remoteId, event.shouldFavorite); } } @@ -1475,35 +1451,16 @@ public class OCFileListFragment extends ExtendedListFragment implements @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onMessageEvent(EncryptionEvent event) { - Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(MainApp.getAppContext()); - - OwnCloudAccount ocAccount = null; - try { - ocAccount = new OwnCloudAccount(currentAccount, MainApp.getAppContext()); - - OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, MainApp.getAppContext()); - - ToggleEncryptionOperation toggleEncryptionOperation = new ToggleEncryptionOperation(event.localId, - event.remotePath, event.shouldBeEncrypted); - RemoteOperationResult remoteOperationResult = toggleEncryptionOperation.execute(mClient, true); - - if (remoteOperationResult.isSuccess()) { - mAdapter.setEncryptionAttributeForItemID(event.remoteId, event.shouldBeEncrypted); - } else if (remoteOperationResult.getHttpCode() == HttpStatus.SC_FORBIDDEN) { - Snackbar.make(getRecyclerView(), R.string.end_to_end_encryption_folder_not_empty, Snackbar.LENGTH_LONG).show(); - } else { - Snackbar.make(getRecyclerView(), R.string.common_error_unknown, Snackbar.LENGTH_LONG).show(); - } - - } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) { - Log_OC.e(TAG, "Account not found", e); - } catch (AuthenticatorException e) { - Log_OC.e(TAG, "Authentication failed", e); - } catch (IOException e) { - Log_OC.e(TAG, "IO error", e); - } catch (OperationCanceledException e) { - Log_OC.e(TAG, "Operation has been canceled", e); + ToggleEncryptionOperation toggleEncryptionOperation = new ToggleEncryptionOperation(event.localId, + event.remotePath, event.shouldBeEncrypted); + RemoteOperationResult remoteOperationResult = toggleEncryptionOperation.execute(client, true); + + if (remoteOperationResult.isSuccess()) { + mAdapter.setEncryptionAttributeForItemID(event.remoteId, event.shouldBeEncrypted); + } else if (remoteOperationResult.getHttpCode() == HttpStatus.SC_FORBIDDEN) { + Snackbar.make(getRecyclerView(), R.string.end_to_end_encryption_folder_not_empty, Snackbar.LENGTH_LONG).show(); + } else { + Snackbar.make(getRecyclerView(), R.string.common_error_unknown, Snackbar.LENGTH_LONG).show(); } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java b/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java index 25db421e28..b58bde603a 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java @@ -25,8 +25,8 @@ package com.owncloud.android.ui.fragment; import android.accounts.Account; import android.app.Activity; +import android.content.Context; import android.content.Intent; -import android.graphics.Bitmap; import android.graphics.PorterDuff; import android.net.Uri; import android.os.Bundle; @@ -47,8 +47,9 @@ import android.widget.ScrollView; import android.widget.TextView; import com.owncloud.android.R; +import com.owncloud.android.authentication.AccountUtils; 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.utils.Log_OC; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; @@ -62,8 +63,8 @@ import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.ThemeUtils; import java.text.SimpleDateFormat; -import java.util.List; import java.util.Date; +import java.util.List; /** * Fragment for Sharing a file with sharees (users or groups) or creating @@ -189,17 +190,20 @@ public class ShareFileFragment extends Fragment implements ShareUserListAdapter. // Setup layout // Image ImageView icon = view.findViewById(R.id.shareFileIcon); - icon.setImageDrawable( - MimeTypeUtil.getFileTypeIcon(mFile.getMimeType(), mFile.getFileName(), mAccount, getContext()) - ); - if (MimeTypeUtil.isImage(mFile)) { - String remoteId = String.valueOf(mFile.getRemoteId()); - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(remoteId); - if (thumbnail != null) { - icon.setImageBitmap(thumbnail); + Context context = getContext(); + + if ((MimeTypeUtil.isImage(mFile) || MimeTypeUtil.isVideo(mFile)) && mFile.getRemoteId() != null) { + OwnCloudClient client = AccountUtils.getClientForCurrentAccount(context); + DisplayUtils.downloadThumbnail(mFile, icon, client, context); + + if ("image/png".equalsIgnoreCase(mFile.getMimeType())) { + icon.setBackgroundColor(container.getResources().getColor(R.color.background_color)); } + } else { + icon.setImageDrawable(MimeTypeUtil.getFileTypeIcon(mFile.getMimeType(), mFile.getFileName(), + mAccount, getContext())); } - + // Title TextView title = view.findViewById(R.id.shareWithUsersSectionTitle); title.setTextColor(accentColor); diff --git a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java index f50945bd90..3aa8bf496d 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java @@ -32,7 +32,6 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; @@ -61,15 +60,15 @@ import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; -import com.bumptech.glide.request.animation.GlideAnimation; -import com.bumptech.glide.request.target.SimpleTarget; import com.evernote.android.job.JobRequest; import com.evernote.android.job.util.support.PersistableBundleCompat; import com.owncloud.android.R; +import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.jobs.ContactsImportJob; +import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.TextDrawable; import com.owncloud.android.ui.activity.ContactsPreferenceActivity; @@ -79,6 +78,7 @@ import com.owncloud.android.utils.BitmapUtils; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.ThemeUtils; +import com.owncloud.android.utils.glide.GlideKey; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -168,6 +168,8 @@ public class ContactListFragment extends FileFragment { View view = inflater.inflate(R.layout.contactlist_fragment, container, false); ButterKnife.bind(this, view); + OwnCloudClient client = AccountUtils.getClientForCurrentAccount(requireContext()); + setHasOptionsMenu(true); ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity(); @@ -184,7 +186,7 @@ public class ContactListFragment extends FileFragment { recyclerView = view.findViewById(R.id.contactlist_recyclerview); if (savedInstanceState == null) { - contactListAdapter = new ContactListAdapter(getContext(), vCards); + contactListAdapter = new ContactListAdapter(getContext(), client, vCards); } else { Set<Integer> checkedItems = new HashSet<>(); int[] itemsArray = savedInstanceState.getIntArray(CHECKED_ITEMS_ARRAY_KEY); @@ -196,7 +198,7 @@ public class ContactListFragment extends FileFragment { if (checkedItems.size() > 0) { onMessageEvent(new VCardToggleEvent(true)); } - contactListAdapter = new ContactListAdapter(getContext(), vCards, checkedItems); + contactListAdapter = new ContactListAdapter(getContext(), client, vCards, checkedItems); } recyclerView.setAdapter(contactListAdapter); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); @@ -572,20 +574,22 @@ class ContactListAdapter extends RecyclerView.Adapter<ContactListFragment.Contac private List<VCard> vCards; private Set<Integer> checkedVCards; - + private OwnCloudClient client; + private Context context; - ContactListAdapter(Context context, List<VCard> vCards) { + ContactListAdapter(Context context, OwnCloudClient client, List<VCard> vCards) { this.vCards = vCards; this.context = context; this.checkedVCards = new HashSet<>(); + this.client = client; } - ContactListAdapter(Context context, List<VCard> vCards, - Set<Integer> checkedVCards) { + ContactListAdapter(Context context, OwnCloudClient client, List<VCard> vCards, Set<Integer> checkedVCards) { this.vCards = vCards; this.context = context; this.checkedVCards = checkedVCards; + this.client = client; } public int getCheckedCount() { @@ -667,20 +671,8 @@ class ContactListAdapter extends RecyclerView.Adapter<ContactListFragment.Contac imageView.setImageDrawable(drawable); } else if (url != null) { - SimpleTarget target = new SimpleTarget<Drawable>() { - @Override - public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) { - imageView.setImageDrawable(resource); - } - - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - imageView.setImageDrawable(errorDrawable); - } - }; - DisplayUtils.downloadIcon(context, url, target, R.drawable.ic_user, imageView.getWidth(), - imageView.getHeight()); + DisplayUtils.downloadImage(url, R.drawable.ic_user, R.drawable.ic_user, imageView, client, + GlideKey.url(url), context); } } 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 de885ef389..b7bcedc266 100755 --- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -30,7 +30,9 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.graphics.Point; import android.net.Uri; +import android.os.AsyncTask; import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -71,6 +73,9 @@ import com.owncloud.android.utils.ConnectivityUtils; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.UriUtils; +import com.owncloud.android.utils.glide.GlideApp; +import com.owncloud.android.utils.glide.GlideContainer; +import com.owncloud.android.utils.glide.GlideKey; import org.greenrobot.eventbus.EventBus; @@ -83,6 +88,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -699,23 +705,81 @@ public class FileOperationsHelper { public void sendCachedImage(OCFile file, String packageName, String activityName) { if (file != null) { - Context context = MainApp.getAppContext(); - Intent sendIntent = new Intent(Intent.ACTION_SEND); - // set MimeType - sendIntent.setType(file.getMimeType()); - sendIntent.setComponent(new ComponentName(packageName, activityName)); - sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + - context.getResources().getString(R.string.image_cache_provider_authority) + - file.getRemotePath())); - sendIntent.putExtra(Intent.ACTION_SEND, true); // Send Action - - mFileActivity.startActivity(Intent.createChooser(sendIntent, - context.getString(R.string.actionbar_send_file))); + + + LoadCachedImageAsyncTask loadTask = new LoadCachedImageAsyncTask(packageName, activityName, mFileActivity); + loadTask.execute(file); + + } else { Log_OC.wtf(TAG, "Trying to send a NULL OCFile"); } } + private static class LoadCachedImageAsyncTask extends AsyncTask<OCFile, Void, Uri> { + private OCFile file; + private String packageName; + private String activityName; + private FileActivity fileActivity; + + LoadCachedImageAsyncTask(String packageName, String activityName, FileActivity fileActivity) { + this.packageName = packageName; + this.activityName = activityName; + this.fileActivity = fileActivity; + } + + @Override + protected Uri doInBackground(OCFile... ocFiles) { + File cachedImage = null; + file = ocFiles[0]; + + try { + GlideContainer container = new GlideContainer(); + container.key = GlideKey.resizedImage(file); + + Point p = DisplayUtils.getScreenDimension(); + int pxW = p.x; + int pxH = p.y; + + cachedImage = GlideApp + .with(fileActivity) + .downloadOnly() + .load(container) + .submit(pxW, pxH) + .get(); // needs to be called on background thread + } catch (InterruptedException | ExecutionException e) { + Log_OC.e(TAG, "Error generating image", e); + } + + Context context = MainApp.getAppContext(); + + return FileProvider.getUriForFile(context, + context.getResources().getString(R.string.file_provider_authority), cachedImage); + } + + @Override + protected void onPostExecute(Uri uri) { + super.onPostExecute(uri); + + if (uri != null) { + Context context = MainApp.getAppContext(); + Intent sendIntent = new Intent(Intent.ACTION_SEND); + + // set MimeType + sendIntent.setType(file.getMimeType()); + sendIntent.setComponent(new ComponentName(packageName, activityName)); +// sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + +// context.getResources().getString(R.string.image_cache_provider_authority) + +// file.getRemotePath())); + sendIntent.putExtra(Intent.EXTRA_STREAM, uri); + sendIntent.putExtra(Intent.ACTION_SEND, true); // Send Action + + fileActivity.startActivity(Intent.createChooser(sendIntent, + context.getString(R.string.actionbar_send_file))); + } + } + } + public void setPictureAs(OCFile file, View view) { if (file != null) { Context context = MainApp.getAppContext(); 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 b4b7dc6b7b..7177e467b3 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java @@ -29,12 +29,14 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.PictureDrawable; +import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Process; import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.design.widget.Snackbar; import android.support.v4.app.FragmentStatePagerAdapter; @@ -51,14 +53,20 @@ import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; +import com.bumptech.glide.Priority; +import com.bumptech.glide.request.target.SimpleTarget; +import com.bumptech.glide.request.transition.Transition; import com.caverock.androidsvg.SVG; import com.caverock.androidsvg.SVGParseException; import com.github.chrisbanes.photoview.PhotoView; import com.owncloud.android.MainApp; import com.owncloud.android.R; +import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.files.FileMenuFilter; +import com.owncloud.android.lib.common.OwnCloudAccount; +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.dialog.ConfirmationDialogFragment; import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment; @@ -67,11 +75,15 @@ import com.owncloud.android.utils.BitmapUtils; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeType; import com.owncloud.android.utils.MimeTypeUtil; +import com.owncloud.android.utils.glide.GlideApp; +import com.owncloud.android.utils.glide.GlideContainer; +import com.owncloud.android.utils.glide.GlideKey; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.ref.WeakReference; +import java.net.URLEncoder; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import pl.droidsonroids.gif.GifDrawable; @@ -221,54 +233,66 @@ public class PreviewImageFragment extends FileFragment { public void onStart() { super.onStart(); if (getFile() != null) { - mImageView.setTag(getFile().getFileId()); - if (mShowResizedImage) { - Bitmap resizedImage = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + getFile().getRemoteId())); - - if (resizedImage != null && !getFile().needsUpdateThumbnail()) { - mImageView.setImageBitmap(resizedImage); - mImageView.setVisibility(View.VISIBLE); - mBitmap = resizedImage; - } else { - // show thumbnail while loading resized image - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(ThumbnailsCacheManager.PREFIX_THUMBNAIL + getFile().getRemoteId())); - - if (thumbnail != null) { - mImageView.setImageBitmap(thumbnail); - mImageView.setVisibility(View.VISIBLE); - mBitmap = thumbnail; - } else { - thumbnail = ThumbnailsCacheManager.mDefaultImg; - } - - // generate new resized image - if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(getFile(), mImageView) && - mContainerActivity.getStorageManager() != null) { - final ThumbnailsCacheManager.ResizedImageGenerationTask task = - new ThumbnailsCacheManager.ResizedImageGenerationTask(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()); - } + try { + Account account = AccountUtils.getCurrentOwnCloudAccount(getContext()); + OwnCloudAccount ocAccount = new OwnCloudAccount(account, MainApp.getAppContext()); + + OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). + getClientFor(ocAccount, MainApp.getAppContext()); + + int thumbnailW = DisplayUtils.getThumbnailDimension(); + int thumbnailH = DisplayUtils.getThumbnailDimension(); + + // Thumbnail + GlideContainer container = new GlideContainer(); + int placeholder = MimeTypeUtil.isVideo(getFile()) ? R.drawable.file_movie : R.drawable.file_image; + + container.url = mClient.getBaseUri() + "/index.php/apps/files/api/v1/thumbnail/" + + thumbnailW + "/" + thumbnailH + Uri.encode(getFile().getRemotePath(), "/"); + container.client = mClient; + container.key = GlideKey.serverThumbnail(getFile()); + + // resized image + GlideContainer containerResizedImage = new GlideContainer(); + + Point p = DisplayUtils.getScreenDimension(); + int pxW = p.x; + int pxH = p.y; + + containerResizedImage.url = mClient.getBaseUri() + "/index.php/core/preview.png?file=" + + URLEncoder.encode(getFile().getRemotePath()) + + "&x=" + pxW + "&y=" + pxH + "&a=1&mode=cover&forceIcon=0"; + containerResizedImage.key = GlideKey.resizedImage(getFile()); + containerResizedImage.client = mClient; + + GlideApp.with(getContext()) + .asBitmap() + .load(container) + .placeholder(placeholder) + .priority(Priority.IMMEDIATE) + .onlyRetrieveFromCache(true) + .into(new SimpleTarget<Bitmap>() { + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + mImageView.setImageResource(placeholder); + loadResizedImage(containerResizedImage); + } + + @Override + public void onResourceReady(@NonNull Bitmap resource, + @Nullable Transition<? super Bitmap> transition) { + mImageView.setImageBitmap(resource); + loadResizedImage(containerResizedImage); + } + } + ); + } catch (Exception e) { + Log_OC.e(TAG, e.getMessage()); } + mMultiView.setVisibility(View.GONE); - if (getResources() != null) { - mImageView.setBackgroundColor(getResources().getColor(R.color.black)); - } + mImageView.setBackgroundColor(getResources().getColor(R.color.black)); mImageView.setVisibility(View.VISIBLE); } else { @@ -280,6 +304,21 @@ public class PreviewImageFragment extends FileFragment { } } + private void loadResizedImage(GlideContainer containerResizedImage) { + GlideApp.with(requireContext()) + .asBitmap() + .load(containerResizedImage) + .priority(Priority.IMMEDIATE) + .into(new SimpleTarget<Bitmap>() { + @Override + public void onResourceReady(@NonNull Bitmap resource, + @Nullable Transition<? super Bitmap> transition) { + mImageView.setImageBitmap(resource); + } + } + ); + } + @Override public void onStop() { Log_OC.d(TAG, "onStop starts"); @@ -482,8 +521,7 @@ public class PreviewImageFragment extends FileFragment { } try { - bitmapResult = BitmapUtils.decodeSampledBitmapFromFile(storagePath, minWidth, - minHeight); + bitmapResult = BitmapUtils.decodeSampledBitmapFromFile(storagePath, minWidth, minHeight); if (isCancelled()) { return new LoadImage(bitmapResult, null, ocFile); 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 98325ebb62..bc408806ff 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java @@ -22,6 +22,7 @@ package com.owncloud.android.ui.preview; import android.accounts.Account; import android.content.Context; import android.graphics.Matrix; +import android.support.annotation.NonNull; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; @@ -217,6 +218,7 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { return super.getItemPosition(object); } + @NonNull @Override public Object instantiateItem(ViewGroup container, int position) { Object fragment = super.instantiateItem(container, position); diff --git a/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java b/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java index e5c71aa39c..424839de63 100644 --- a/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java +++ b/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java @@ -121,7 +121,7 @@ public class TrashbinActivity extends FileActivity implements TrashbinActivityIn emptyContentMessage.setText(noResultsMessage); emptyContentMessage.setVisibility(View.VISIBLE); - trashbinListAdapter = new TrashbinListAdapter(this, getStorageManager(), this); + trashbinListAdapter = new TrashbinListAdapter(this, this); recyclerView.setAdapter(trashbinListAdapter); recyclerView.setHasFixedSize(true); recyclerView.setHasFooter(true); @@ -227,12 +227,6 @@ public class TrashbinActivity extends FileActivity implements TrashbinActivityIn } @Override - protected void onPause() { - super.onPause(); - trashbinListAdapter.cancelAllPendingTasks(); - } - - @Override public void onBackPressed() { trashbinPresenter.navigateUp(); } @@ -286,4 +280,4 @@ public class TrashbinActivity extends FileActivity implements TrashbinActivityIn emptyContentIcon.setVisibility(View.VISIBLE); } } -}
\ No newline at end of file +} diff --git a/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/src/main/java/com/owncloud/android/utils/DisplayUtils.java index 049548561f..63bafa19da 100644 --- a/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -36,6 +36,7 @@ import android.graphics.Point; import android.graphics.drawable.Drawable; import android.graphics.drawable.PictureDrawable; import android.net.Uri; +import android.os.AsyncTask; import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -50,39 +51,50 @@ import android.text.format.DateUtils; import android.text.style.StyleSpan; import android.util.DisplayMetrics; import android.util.Log; +import android.view.Display; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; -import com.bumptech.glide.GenericRequestBuilder; -import com.bumptech.glide.Glide; +import com.bumptech.glide.load.Key; import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.model.StreamEncoder; -import com.bumptech.glide.load.resource.file.FileToStreamDecoder; +import com.bumptech.glide.request.RequestOptions; +import com.bumptech.glide.request.target.CustomViewTarget; import com.bumptech.glide.request.target.SimpleTarget; import com.bumptech.glide.request.target.Target; -import com.caverock.androidsvg.SVG; +import com.bumptech.glide.signature.ObjectKey; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.lib.common.OwnCloudAccount; +import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.files.SearchOperation; +import com.owncloud.android.lib.resources.files.ServerFileInterface; import com.owncloud.android.ui.TextDrawable; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.events.MenuItemClickEvent; import com.owncloud.android.ui.events.SearchEvent; import com.owncloud.android.ui.fragment.OCFileListFragment; -import com.owncloud.android.utils.svg.SvgDecoder; -import com.owncloud.android.utils.svg.SvgDrawableTranscoder; - +import com.owncloud.android.utils.glide.GlideApp; +import com.owncloud.android.utils.glide.GlideAvatar; +import com.owncloud.android.utils.glide.GlideContainer; +import com.owncloud.android.utils.glide.GlideKey; +import com.owncloud.android.utils.glide.GlideOCFileType; +import com.owncloud.android.utils.glide.GlideOcFile; +import com.owncloud.android.utils.svg.SvgSoftwareLayerSetter; + +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.methods.GetMethod; import org.greenrobot.eventbus.EventBus; import org.parceler.Parcels; import java.io.BufferedReader; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -90,6 +102,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.math.BigDecimal; import java.net.IDN; +import java.security.NoSuchAlgorithmException; import java.text.DateFormat; import java.util.Collection; import java.util.Date; @@ -98,6 +111,7 @@ import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutionException; /** * A helper class for UI/display related operations. @@ -117,6 +131,7 @@ public final class DisplayUtils { private static final int BYTE_SIZE_DIVIDER = 1024; private static final double BYTE_SIZE_DIVIDER_DOUBLE = 1024.0; private static final int DATE_TIME_PARTS_SIZE = 2; + private static final String ETAG = "ETag"; private static Map<String, String> mimeType2HumanReadable; @@ -433,18 +448,28 @@ public final class DisplayUtils { * fetches and sets the avatar of the given account in the passed callContext * * @param account the account to be used to connect to server - * @param avatarRadius the avatar radius - * @param resources reference for density information - * @param callContext which context is called to set the generated avatar */ - public static void setAvatar(@NonNull Account account, AvatarGenerationListener listener, - float avatarRadius, Resources resources, Object callContext, Context context) { +// public static void setAvatar(@NonNull Account account, Context context, SimpleTarget<Drawable> target) { +// +// AccountManager accountManager = AccountManager.get(context); +// String userId = accountManager.getUserData(account, +// com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID); +// +// setAvatar(account, userId, context, target); +// } + + /** + * fetches and sets the avatar of the given account in the passed callContext + * + * @param account the account to be used to connect to server + */ + public static void setAvatar(@NonNull Account account, Context context, Object view, float radius) { AccountManager accountManager = AccountManager.get(context); String userId = accountManager.getUserData(account, com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID); - setAvatar(account, userId, listener, avatarRadius, resources, callContext, context); + setAvatar(account, userId, context, view, radius); } /** @@ -452,54 +477,158 @@ public final class DisplayUtils { * * @param account the account to be used to connect to server * @param userId the userId which avatar should be set - * @param avatarRadius the avatar radius - * @param resources reference for density information - * @param callContext which context is called to set the generated avatar + * @param view where the image is shown in */ - public static void setAvatar(@NonNull Account account, @NonNull String userId, AvatarGenerationListener listener, - float avatarRadius, Resources resources, Object callContext, Context context) { - if (callContext instanceof View) { - ((View) callContext).setContentDescription(account.name); + public static void setAvatar(@NonNull Account account, @NonNull String userId, Context context, Object view, + float radius) { + Drawable placeholder = context.getResources().getDrawable(R.drawable.ic_user); + + Drawable fallback; + try { + fallback = TextDrawable.createAvatar(account.name, radius); + } catch (NoSuchAlgorithmException e) { + fallback = placeholder; + } + + // show avatar immediately, might be outdated + if (view instanceof ImageView) { + ImageView imageView = (ImageView) view; + imageView.setContentDescription(account.name); + + GlideApp.with(context) + .asBitmap() + .load(new GlideAvatar(GlideKey.avatar(account, userId, context), null)) + .apply(RequestOptions.circleCropTransform()) + .placeholder(placeholder) + .error(fallback) + .onlyRetrieveFromCache(true) + .into(imageView); + } else { + GlideApp.with(context) + .load(new GlideAvatar(GlideKey.avatar(account, userId, context), null)) + .apply(RequestOptions.circleCropTransform()) + .placeholder(placeholder) + .error(fallback) + .onlyRetrieveFromCache(true) + .into((SimpleTarget<Drawable>) view); } + + AsyncTask task = new AsyncTask<Object, Void, InputStream>() { + + GetMethod get; + ArbitraryDataProvider arbitraryDataProvider; + String accountName; + + @Override + protected InputStream doInBackground(Object[] objects) { + InputStream inputStream = null; + + // we need to create client here, as different servers can be used + OwnCloudClient client = AccountUtils.getClientForAccount(account, context); + + int px = getAvatarDimension(context); + + arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver()); + String serverName = account.name.substring(account.name.lastIndexOf('@') + 1, account.name.length()); + accountName = userId + "@" + serverName; + String eTag = arbitraryDataProvider.getValue(accountName, GlideKey.AVATAR_KEY); + Log_OC.d(TAG, "glide: old etag: " + eTag); + + try { + String uri = client.getBaseUri() + "/index.php/avatar/" + Uri.encode(userId) + "/" + px; + Log_OC.d("Avatar", "URI: " + uri); + get = new GetMethod(uri); + + // only use eTag if available + if (!eTag.isEmpty()) { + get.setRequestHeader("If-None-Match", eTag); + } - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver()); + int status = client.executeMethod(get); - String serverName = account.name.substring(account.name.lastIndexOf('@') + 1, account.name.length()); - String eTag = arbitraryDataProvider.getValue(userId + "@" + serverName, ThumbnailsCacheManager.AVATAR); - String avatarKey = "a_" + userId + "_" + serverName + "_" + eTag; + Log_OC.d(TAG, "glide: status: " + status); - // first show old one - Drawable avatar = BitmapUtils.bitmapToCircularBitmapDrawable(resources, - ThumbnailsCacheManager.getBitmapFromDiskCache(avatarKey)); + // we are using eTag to download a new avatar only if it changed + switch (status) { + case HttpStatus.SC_OK: + case HttpStatus.SC_CREATED: + // new avatar + inputStream = get.getResponseBodyAsStream(); - // if no one exists, show colored icon with initial char - if (avatar == null) { - try { - avatar = TextDrawable.createAvatarByUserId(userId, avatarRadius); - } catch (Exception e) { - Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); - avatar = resources.getDrawable(R.drawable.account_circle_white); + if (get.getResponseHeader(ETAG) != null) { + String newETag = get.getResponseHeader(ETAG).getValue().replace("\"", ""); + Log_OC.d(TAG, "glide: new etag: " + newETag); + arbitraryDataProvider.storeOrUpdateKeyValue(accountName, GlideKey.AVATAR_KEY, newETag); + } + break; + + case HttpStatus.SC_NOT_MODIFIED: + default: + client.exhaustResponse(get.getResponseBodyAsStream()); + break; + + } + } catch (Exception e) { + // do nothing, fallback in glide + if (get != null) { + get.releaseConnection(); + } + } + + return inputStream; } - } - // check for new avatar, eTag is compared, so only new one is downloaded - if (ThumbnailsCacheManager.cancelPotentialAvatarWork(userId, callContext)) { - final ThumbnailsCacheManager.AvatarGenerationTask task = - new ThumbnailsCacheManager.AvatarGenerationTask(listener, callContext, account, resources, - avatarRadius, userId, serverName, context); + @Override + protected void onPostExecute(InputStream inputStream) { + Drawable placeholder = context.getResources().getDrawable(R.drawable.ic_user); + + Drawable fallback; + try { + fallback = TextDrawable.createAvatar(account.name, radius); + } catch (NoSuchAlgorithmException e) { + fallback = placeholder; + } + + try { + if (view instanceof ImageView) { + ImageView imageView = (ImageView) view; + imageView.setContentDescription(account.name); + + GlideApp.with(context) + .asBitmap() + .load(new GlideAvatar(GlideKey.avatar(account, userId, context), inputStream)) + .apply(RequestOptions.circleCropTransform()) + .placeholder(placeholder) + .error(fallback) + .onlyRetrieveFromCache(inputStream == null) + .into(imageView); + } else { + GlideApp.with(context) + .load(new GlideAvatar(GlideKey.avatar(account, userId, context), inputStream)) + .apply(RequestOptions.circleCropTransform()) + .placeholder(placeholder) + .error(fallback) + .onlyRetrieveFromCache(inputStream == null) + .into((SimpleTarget<Drawable>) view); + } + + } catch (Exception e) { + Log_OC.e(TAG, "Avatar generation failed", e); + // reset eTag + arbitraryDataProvider.storeOrUpdateKeyValue(accountName, GlideKey.AVATAR_KEY, ""); + // context may be null + } + } + }; - final ThumbnailsCacheManager.AsyncAvatarDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncAvatarDrawable(resources, avatar, task); - listener.avatarGenerated(asyncDrawable, callContext); - task.execute(userId); - } + task.execute(); } - public static void downloadIcon(Context context, String iconUrl, SimpleTarget imageView, int placeholder, - int width, int height) { + public static void downloadIcon(Context context, String iconUrl, SimpleTarget<Drawable> imageView, int placeholder, + int error) { try { if (iconUrl.endsWith(".svg")) { - downloadSVGIcon(context, iconUrl, imageView, placeholder, width, height); + downloadSVG(iconUrl, placeholder, error, imageView, context); } else { downloadPNGIcon(context, iconUrl, imageView, placeholder); } @@ -508,44 +637,43 @@ public final class DisplayUtils { } } - private static void downloadPNGIcon(Context context, String iconUrl, SimpleTarget imageView, int placeholder) { - Glide - .with(context) + private static void downloadPNGIcon(Context context, String iconUrl, SimpleTarget<Drawable> imageView, + int placeholder) { + GlideApp.with(context) .load(iconUrl) .centerCrop() .placeholder(placeholder) .error(placeholder) - .crossFade() .into(imageView); } - private static void downloadSVGIcon(Context context, String iconUrl, SimpleTarget imageView, int placeholder, - int width, int height) { - GenericRequestBuilder<Uri, InputStream, SVG, PictureDrawable> requestBuilder = Glide.with(context) - .using(Glide.buildStreamModelLoader(Uri.class, context), InputStream.class) - .from(Uri.class) - .as(SVG.class) - .transcode(new SvgDrawableTranscoder(), PictureDrawable.class) - .sourceEncoder(new StreamEncoder()) - .cacheDecoder(new FileToStreamDecoder<>(new SvgDecoder(height, width))) - .decoder(new SvgDecoder(height, width)) + public static void downloadSVG(String url, int placeholder, int error, ImageView imageView, Context context) { + GlideApp.with(context) + .as(PictureDrawable.class) .placeholder(placeholder) - .error(placeholder) - .animate(android.R.anim.fade_in); - - - Uri uri = Uri.parse(iconUrl); - requestBuilder - .diskCacheStrategy(DiskCacheStrategy.SOURCE) - .load(uri) + .fitCenter() + .error(error) + .listener(new SvgSoftwareLayerSetter()) + .load(url) .into(imageView); } + private static void downloadSVG(String url, int placeholder, int error, SimpleTarget<Drawable> imageView, + Context context) { + GlideApp.with(context) + .as(PictureDrawable.class) + .load(url) + .placeholder(placeholder) + .error(error) + .listener(new SvgSoftwareLayerSetter()) + .into((SimpleTarget) imageView); + } + public static Bitmap downloadImageSynchronous(Context context, String imageUrl) { try { - return Glide.with(context) - .load(imageUrl) + return GlideApp.with(context) .asBitmap() + .load(imageUrl) .diskCacheStrategy(DiskCacheStrategy.NONE) .skipMemoryCache(true) .into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) @@ -556,6 +684,139 @@ public final class DisplayUtils { } } + public static void localImage(File file, int placeholder, int error, ImageView view, Key key, Context context) { + GlideApp.with(context) + .load(file) + .placeholder(placeholder) + .error(error) + .into(view); + } + + public static void downloadThumbnail(OCFile file, Object view, OwnCloudClient client, Context context) { + GlideContainer container = new GlideContainer(); + + int placeholder = MimeTypeUtil.isVideo(file) ? R.drawable.file_movie : R.drawable.file_image; + int pxW = DisplayUtils.getThumbnailDimension(); + int pxH = DisplayUtils.getThumbnailDimension(); + + container.url = client.getBaseUri() + "/index.php/apps/files/api/v1/thumbnail/" + pxW + "/" + pxH + + Uri.encode(file.getRemotePath(), "/"); + container.client = client; + container.key = GlideKey.serverThumbnail(file); + + if (view instanceof ImageView) { + GlideApp.with(context) + .load(container) + .placeholder(placeholder) + .error(placeholder) + .into((ImageView) view); + } else { + GlideApp.with(context) + .load(container) + .placeholder(placeholder) + .error(placeholder) + .into((CustomViewTarget<ImageView, Drawable>) view); + } + } + + public static void downloadActivityThumbnail(OCFile file, ImageView view, OwnCloudClient client, Context context) { + GlideContainer container = new GlideContainer(); + + int placeholder = MimeTypeUtil.isVideo(file) ? R.drawable.file_movie : R.drawable.file_image; + int pxW = DisplayUtils.getThumbnailDimension(); + int pxH = DisplayUtils.getThumbnailDimension(); + + container.url = client.getBaseUri() + "/index.php/apps/files/api/v1/thumbnail/" + pxW + "/" + pxH + + Uri.encode(file.getRemotePath(), "/"); + container.client = client; + container.key = GlideKey.activityThumbnail(file); + + GlideApp.with(context) + .load(container) + .placeholder(placeholder) + .into(view); + } + + public static Drawable getThumbnail(OCFile file, ImageView view, OwnCloudClient client, Context context) { + GlideContainer container = new GlideContainer(); + + int placeholder = MimeTypeUtil.isVideo(file) ? R.drawable.file_movie : R.drawable.file_image; + int pxW = DisplayUtils.getThumbnailDimension(); + int pxH = DisplayUtils.getThumbnailDimension(); + + container.url = client.getBaseUri() + "/index.php/apps/files/api/v1/thumbnail/" + pxW + "/" + pxH + + Uri.encode(file.getRemotePath(), "/"); + container.client = client; + container.key = GlideKey.serverThumbnail(file); + + try { + return GlideApp.with(context) + .load(container) + .placeholder(placeholder) + .into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) + .get(); + } catch (InterruptedException | ExecutionException e) { + Log_OC.e(TAG, "Could not download image " + container.url); + return context.getResources().getDrawable(placeholder); + } + } + + public static void downloadImage(String uri, int placeholder, int error, ImageView view, OwnCloudClient client, + ObjectKey key, Context context) { + GlideContainer container = new GlideContainer(); + + container.url = uri; + container.key = key; + container.client = client; + + GlideApp.with(context) + .load(container) + .placeholder(placeholder) + .error(error) + .into(view); + } + + public static String getThumbnailUri(OwnCloudClient client, ServerFileInterface file, int size) { + return client.getBaseUri() + "/index.php/apps/files_trashbin/preview?fileId=" + + file.getLocalId() + "&x=" + size + "&y=" + size; + } + + public static void downloadImage(String uri, int placeholder, int error, SimpleTarget<Drawable> target, Key key, + Context context) { + GlideApp.with(context) + .load(uri) + .placeholder(placeholder) + .error(error) + .into(target); + } + + public static void generateResizedImage(OCFile file, Context context) { + Point p = DisplayUtils.getScreenDimension(); + int pxW = p.x; + int pxH = p.y; + + try { + GlideApp.with(context) + .load(new GlideOcFile(file, GlideOCFileType.resizedImage)) + .downloadOnly(pxW, pxH).get(); + } catch (InterruptedException | ExecutionException e) { + Log_OC.e(TAG, "Thumbnail generation failed", e); + } + } + + public static void generateThumbnail(OCFile file, String path, Context context) { + int pxW = DisplayUtils.getThumbnailDimension(); + int pxH = DisplayUtils.getThumbnailDimension(); + + try { + GlideApp.with(context) + .load(new GlideOcFile(file, GlideOCFileType.thumbnail, path)) + .downloadOnly(pxW, pxH).get(); + } catch (InterruptedException | ExecutionException e) { + Log_OC.e(TAG, "Thumbnail generation failed", e); + } + } + public static void setupBottomBar(BottomNavigationView view, Resources resources, final Activity activity, int checkedMenuItem) { @@ -773,4 +1034,40 @@ public final class DisplayUtils { }) .show(); } + + /** + * Converts size of file icon from dp to pixel + * + * @return int + */ + public 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 + */ + public static Point getScreenDimension() { + WindowManager wm = (WindowManager) MainApp.getAppContext().getSystemService(Context.WINDOW_SERVICE); + + if (wm == null) { + // fallback to reasonable size for resized images + return new Point(1024, 868); + } else { + Display display = wm.getDefaultDisplay(); + Point point = new Point(); + display.getSize(point); + return point; + } + } + + private static int getAvatarDimension(Context context) { + // Converts dp to pixel + Resources r = context.getResources(); + return Math.round(r.getDimension(R.dimen.file_avatar_size)); + } } diff --git a/src/main/java/com/owncloud/android/utils/FileStorageUtils.java b/src/main/java/com/owncloud/android/utils/FileStorageUtils.java index 608279a317..151229fa1f 100644 --- a/src/main/java/com/owncloud/android/utils/FileStorageUtils.java +++ b/src/main/java/com/owncloud/android/utils/FileStorageUtils.java @@ -318,7 +318,7 @@ public final class FileStorageUtils { return ret; } - public static boolean moveFile(File sourceFile, File targetFile) throws IOException { + public static boolean moveFile(File sourceFile, File targetFile) { if (copyFile(sourceFile, targetFile)) { return sourceFile.delete(); } else { diff --git a/src/main/java/com/owncloud/android/utils/GlideUtils.java b/src/main/java/com/owncloud/android/utils/GlideUtils.java new file mode 100644 index 0000000000..50a6c31dc5 --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/GlideUtils.java @@ -0,0 +1,25 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.owncloud.android.utils; + +public class GlideUtils { +} diff --git a/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java b/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java index 693e82003b..ca612ce1a2 100644 --- a/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java +++ b/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java @@ -281,6 +281,14 @@ public final class MimeTypeUtil { || MimeTypeUtil.isImage(getMimeTypeFromPath(file.getRemotePath())); } + public static boolean isImageOrVideo(ServerFileInterface file) { + return isImage(file) || isVideo(file); + } + + public static boolean isImageOrVideo(File file) { + return isImage(file) || isVideo(file); + } + /** * @param file the file to be analyzed * @return 'True' if the file is simple text (e.g. not application-dependent, like .doc or .docx) diff --git a/src/main/java/com/owncloud/android/utils/glide/AvatarFetcher.java b/src/main/java/com/owncloud/android/utils/glide/AvatarFetcher.java new file mode 100644 index 0000000000..daae2a0cb0 --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/AvatarFetcher.java @@ -0,0 +1,64 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.owncloud.android.utils.glide; + +import android.support.annotation.NonNull; + +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.data.DataFetcher; + +import java.io.InputStream; + +public class AvatarFetcher implements DataFetcher<InputStream> { + private GlideAvatar avatar; + + public AvatarFetcher(GlideAvatar avatar) { + this.avatar = avatar; + } + + @Override + public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { + callback.onDataReady(avatar.getInputStream()); + } + + public void cleanup() { + // not needed + } + + @Override + public void cancel() { + // not needed + } + + @NonNull + @Override + public Class<InputStream> getDataClass() { + return InputStream.class; + } + + @NonNull + @Override + public DataSource getDataSource() { + return DataSource.REMOTE; + } +} diff --git a/src/main/java/com/owncloud/android/utils/glide/AvatarLoader.java b/src/main/java/com/owncloud/android/utils/glide/AvatarLoader.java new file mode 100644 index 0000000000..c62711dbb1 --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/AvatarLoader.java @@ -0,0 +1,45 @@ +/** + * Nextcloud Android client application + * + * @author Alejandro Bautista + * Copyright (C) 2017 Alejandro Bautista + * <p> + * 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. + * <p> + * 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. + * <p> + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.owncloud.android.utils.glide; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.model.ModelLoader; + +import java.io.InputStream; + +/** + * Custom Model for OwnCloudClient + */ + +public class AvatarLoader implements ModelLoader<GlideAvatar, InputStream> { + @Nullable + @Override + public LoadData<InputStream> buildLoadData(@NonNull GlideAvatar avatar, int width, int height, @NonNull Options options) { + return new LoadData<>(avatar.getKey(), new AvatarFetcher(avatar)); + } + + @Override + public boolean handles(@NonNull GlideAvatar a) { + return true; + } +} diff --git a/src/main/java/com/owncloud/android/utils/glide/AvatarModelLoaderFactory.java b/src/main/java/com/owncloud/android/utils/glide/AvatarModelLoaderFactory.java new file mode 100644 index 0000000000..f9cac4d111 --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/AvatarModelLoaderFactory.java @@ -0,0 +1,44 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.owncloud.android.utils.glide; + +import android.support.annotation.NonNull; + +import com.bumptech.glide.load.model.ModelLoader; +import com.bumptech.glide.load.model.ModelLoaderFactory; +import com.bumptech.glide.load.model.MultiModelLoaderFactory; + +import java.io.InputStream; + +public class AvatarModelLoaderFactory implements ModelLoaderFactory<GlideAvatar, InputStream> { + + @NonNull + @Override + public ModelLoader<GlideAvatar, InputStream> build(@NonNull MultiModelLoaderFactory unused) { + return new AvatarLoader(); + } + + @Override + public void teardown() { + // Do nothing. + } +} diff --git a/src/main/java/com/owncloud/android/utils/glide/FileFetcher.java b/src/main/java/com/owncloud/android/utils/glide/FileFetcher.java new file mode 100644 index 0000000000..7ab9367356 --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/FileFetcher.java @@ -0,0 +1,84 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.owncloud.android.utils.glide; + +import android.support.annotation.NonNull; + +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.data.DataFetcher; +import com.owncloud.android.lib.common.utils.Log_OC; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +public class FileFetcher implements DataFetcher<InputStream> { + private final static String TAG = FileFetcher.class.getSimpleName(); + private String storagePath; + private InputStream data; + + public FileFetcher(String storagePath) { + this.storagePath = storagePath; + } + + @Override + public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { + try { + data = new FileInputStream(storagePath); + } catch (FileNotFoundException e) { + Log_OC.d(TAG, "Failed to open file", e); + callback.onLoadFailed(e); + return; + } + callback.onDataReady(data); + } + + @Override + public void cleanup() { + if (data != null) { + try { + data.close(); + } catch (IOException e) { + // ignore + } + } + } + + @Override + public void cancel() { + // unused + } + + @NonNull + @Override + public Class<InputStream> getDataClass() { + return InputStream.class; + } + + @NonNull + @Override + public DataSource getDataSource() { + return DataSource.LOCAL; + } +} diff --git a/src/main/java/com/owncloud/android/utils/glide/CustomGlideStreamLoader.java b/src/main/java/com/owncloud/android/utils/glide/GlideAvatar.java index 648f265d0f..938281fc19 100644 --- a/src/main/java/com/owncloud/android/utils/glide/CustomGlideStreamLoader.java +++ b/src/main/java/com/owncloud/android/utils/glide/GlideAvatar.java @@ -1,8 +1,9 @@ -/** +/* * Nextcloud Android client application * - * @author Alejandro Bautista - * Copyright (C) 2017 Alejandro Bautista + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -17,20 +18,27 @@ * You should have received a copy of the GNU Affero General Public * License along with this program. If not, see <http://www.gnu.org/licenses/>. */ + package com.owncloud.android.utils.glide; -import com.bumptech.glide.load.data.DataFetcher; -import com.bumptech.glide.load.model.stream.StreamModelLoader; +import com.bumptech.glide.signature.ObjectKey; import java.io.InputStream; -/** - * Custom Model for OwnCloudClient - */ +public class GlideAvatar { + private ObjectKey key; + private InputStream inputStream; + + public GlideAvatar(ObjectKey key, InputStream avatar) { + this.key = key; + this.inputStream = avatar; + } + + public ObjectKey getKey() { + return key; + } -public class CustomGlideStreamLoader implements StreamModelLoader<String> { - @Override - public DataFetcher<InputStream> getResourceFetcher(String url, int width, int height) { - return new HttpStreamFetcher(url); + public InputStream getInputStream() { + return inputStream; } } diff --git a/src/main/java/com/owncloud/android/utils/glide/GlideContainer.java b/src/main/java/com/owncloud/android/utils/glide/GlideContainer.java new file mode 100644 index 0000000000..51f9239bea --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/GlideContainer.java @@ -0,0 +1,31 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.owncloud.android.utils.glide; + +import com.bumptech.glide.signature.ObjectKey; +import com.owncloud.android.lib.common.OwnCloudClient; + +public class GlideContainer { + public OwnCloudClient client; + public ObjectKey key; + public String url; +} diff --git a/src/main/java/com/owncloud/android/utils/glide/GlideContainerModelLoaderFactory.java b/src/main/java/com/owncloud/android/utils/glide/GlideContainerModelLoaderFactory.java new file mode 100644 index 0000000000..b2faf8bc35 --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/GlideContainerModelLoaderFactory.java @@ -0,0 +1,44 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.owncloud.android.utils.glide; + +import android.support.annotation.NonNull; + +import com.bumptech.glide.load.model.ModelLoader; +import com.bumptech.glide.load.model.ModelLoaderFactory; +import com.bumptech.glide.load.model.MultiModelLoaderFactory; + +import java.io.InputStream; + +public class GlideContainerModelLoaderFactory implements ModelLoaderFactory<GlideContainer, InputStream> { + + @NonNull + @Override + public ModelLoader<GlideContainer, InputStream> build(@NonNull MultiModelLoaderFactory unused) { + return new GlideContainerStreamLoader(); + } + + @Override + public void teardown() { + // Do nothing. + } +} diff --git a/src/main/java/com/owncloud/android/utils/glide/GlideContainerStreamLoader.java b/src/main/java/com/owncloud/android/utils/glide/GlideContainerStreamLoader.java new file mode 100644 index 0000000000..0c41eff9be --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/GlideContainerStreamLoader.java @@ -0,0 +1,49 @@ +/* + * Nextcloud Android client application + * + * @author Alejandro Bautista + * Copyright (C) 2017 Alejandro Bautista + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.owncloud.android.utils.glide; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.model.ModelLoader; +import com.bumptech.glide.signature.ObjectKey; + +import java.io.InputStream; + +import static com.owncloud.android.utils.glide.GlideKey.RESIZED_IMAGE_KEY; +import static com.owncloud.android.utils.glide.GlideKey.THUMBNAIL_KEY; + +/** + * Custom model for Nextcloud Client + */ +public class GlideContainerStreamLoader implements ModelLoader<GlideContainer, InputStream> { + @Nullable + @Override + public LoadData<InputStream> buildLoadData(@NonNull GlideContainer container, int width, int height, + @NonNull Options options) { + return new LoadData<>(container.key, new HttpStreamGlideContainerFetcher(container)); + } + + @Override + public boolean handles(@NonNull GlideContainer s) { + return !s.key.equals(new ObjectKey(THUMBNAIL_KEY)) && !s.key.equals(new ObjectKey(RESIZED_IMAGE_KEY)); + } +} diff --git a/src/main/java/com/owncloud/android/utils/glide/GlideKey.java b/src/main/java/com/owncloud/android/utils/glide/GlideKey.java new file mode 100644 index 0000000000..8130b4d3e7 --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/GlideKey.java @@ -0,0 +1,75 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.owncloud.android.utils.glide; + +import android.accounts.Account; +import android.content.Context; + +import com.bumptech.glide.signature.ObjectKey; +import com.owncloud.android.datamodel.ArbitraryDataProvider; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.lib.resources.files.TrashbinFile; + +import java.io.File; + +public final class GlideKey { + public static final String AVATAR_KEY = "AVATAR"; + static final String THUMBNAIL_KEY = "THUMBNAIL_"; + static final String RESIZED_IMAGE_KEY = "RESIZED_IMAGE_"; + + private GlideKey() { + // Required empty constructor + } + + public static ObjectKey serverThumbnail(OCFile file) { + return new ObjectKey(THUMBNAIL_KEY + file.getEtagOnServer()); + } + + public static ObjectKey resizedImage(OCFile file) { + return new ObjectKey(RESIZED_IMAGE_KEY + file.getEtagOnServer()); + } + + public static ObjectKey localFile(File file) { + return new ObjectKey(file.hashCode()); + } + + public static ObjectKey url(String url) { + return new ObjectKey(url); + } + + public static ObjectKey trashbinThumbnail(TrashbinFile file) { + return new ObjectKey(THUMBNAIL_KEY + file.getRemoteId()); + } + + public static ObjectKey activityThumbnail(OCFile file) { + return new ObjectKey(THUMBNAIL_KEY + file.getRemoteId()); + } + + public static ObjectKey avatar(Account account, String userId, Context context) { + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver()); + + String serverName = account.name.substring(account.name.lastIndexOf('@') + 1, account.name.length()); + String eTag = arbitraryDataProvider.getValue(userId + "@" + serverName, GlideKey.AVATAR_KEY); + + return new ObjectKey("a_" + userId + "_" + serverName + "_" + eTag); + } +} diff --git a/src/main/java/com/owncloud/android/utils/glide/GlideOCFileType.java b/src/main/java/com/owncloud/android/utils/glide/GlideOCFileType.java new file mode 100644 index 0000000000..726d169e1f --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/GlideOCFileType.java @@ -0,0 +1,26 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.owncloud.android.utils.glide; + +public enum GlideOCFileType { + resizedImage, thumbnail +} diff --git a/src/main/java/com/owncloud/android/utils/glide/GlideOcFile.java b/src/main/java/com/owncloud/android/utils/glide/GlideOcFile.java new file mode 100644 index 0000000000..d9f49cb12e --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/GlideOcFile.java @@ -0,0 +1,54 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.owncloud.android.utils.glide; + +import com.owncloud.android.datamodel.OCFile; + +public class GlideOcFile { + private OCFile file; + private GlideOCFileType type; + private String path = ""; + + public GlideOcFile(OCFile file, GlideOCFileType type) { + this.file = file; + this.type = type; + } + + public GlideOcFile(OCFile file, GlideOCFileType type, String path) { + this.file = file; + this.type = type; + this.path = path; + } + + public OCFile getFile() { + return file; + } + + public GlideOCFileType getType() { + return type; + } + + public String getPath() { + return path; + } +} + diff --git a/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamFetcher.java b/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamFetcher.java new file mode 100644 index 0000000000..faa5a55b5c --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamFetcher.java @@ -0,0 +1,94 @@ +/* + * Nextcloud Android client application + * + * @author Alejandro Bautista + * Copyright (C) 2017 Alejandro Bautista + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.owncloud.android.utils.glide; + +import android.support.annotation.NonNull; + +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.data.DataFetcher; +import com.owncloud.android.MainApp; +import com.owncloud.android.authentication.AccountUtils; +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.operations.RemoteOperation; +import com.owncloud.android.lib.common.utils.Log_OC; + +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.methods.GetMethod; + +import java.io.InputStream; + +/** + * Fetcher with Nextcloud client + */ +public class GlideStringStreamFetcher implements DataFetcher<InputStream> { + + private static final String TAG = GlideStringStreamFetcher.class.getName(); + private final String url; + + public GlideStringStreamFetcher(String url) { + this.url = url; + } + + @Override + public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { + OwnCloudClient client = AccountUtils.getClientForCurrentAccount(MainApp.getAppContext()); + + GetMethod get = null; + try { + get = new GetMethod(url); + get.setRequestHeader("Cookie", "nc_sameSiteCookielax=true;nc_sameSiteCookiestrict=true"); + get.setRequestHeader(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE); + int status = client.executeMethod(get); + if (status == HttpStatus.SC_OK) { + callback.onDataReady(get.getResponseBodyAsStream()); + } else { + client.exhaustResponse(get.getResponseBodyAsStream()); + } + } catch (Exception e) { + Log_OC.e(TAG, e.getMessage(), e); + } finally { + if (get != null) { + get.releaseConnection(); + } + } + } + + public void cleanup() { + Log_OC.i(TAG, "Cleanup"); + } + + @Override + public void cancel() { + Log_OC.i(TAG, "Cancel"); + } + + @NonNull + @Override + public Class<InputStream> getDataClass() { + return InputStream.class; + } + + @NonNull + @Override + public DataSource getDataSource() { + return DataSource.REMOTE; + } +} diff --git a/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamLoader.java b/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamLoader.java new file mode 100644 index 0000000000..e0b6120acf --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamLoader.java @@ -0,0 +1,47 @@ +/* + * Nextcloud Android client application + * + * @author Alejandro Bautista + * Copyright (C) 2017 Alejandro Bautista + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.owncloud.android.utils.glide; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.model.ModelLoader; +import com.bumptech.glide.signature.ObjectKey; + +import java.io.InputStream; + +/** + * Custom model for Nextcloud client + */ +public class GlideStringStreamLoader implements ModelLoader<String, InputStream> { + @Nullable + @Override + public LoadData<InputStream> buildLoadData(@NonNull String url, int width, int height, @NonNull Options options) { + // TODO replace key with etag? and type? (avatar, thumbnail, resized image) + // TODO pass client to stream fetcher? + return new LoadData<>(new ObjectKey(url), new GlideStringStreamFetcher(url)); + } + + @Override + public boolean handles(@NonNull String s) { + return true; + } +} diff --git a/src/main/java/com/owncloud/android/utils/glide/HttpStreamFetcher.java b/src/main/java/com/owncloud/android/utils/glide/HttpStreamFetcher.java deleted file mode 100644 index 08ceb71e70..0000000000 --- a/src/main/java/com/owncloud/android/utils/glide/HttpStreamFetcher.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Nextcloud Android client application - * - * @author Alejandro Bautista - * Copyright (C) 2017 Alejandro Bautista - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.owncloud.android.utils.glide; - -import android.accounts.Account; - -import com.bumptech.glide.Priority; -import com.bumptech.glide.load.data.DataFetcher; -import com.owncloud.android.MainApp; -import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.lib.common.OwnCloudAccount; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.utils.Log_OC; - -import org.apache.commons.httpclient.HttpStatus; -import org.apache.commons.httpclient.methods.GetMethod; - -import java.io.InputStream; - -/** - * Fetcher with OwnCloudClient - */ - -public class HttpStreamFetcher implements DataFetcher<InputStream> { - - private static final String TAG = HttpStreamFetcher.class.getName(); - private final String mURL; - - public HttpStreamFetcher(String url) { - this.mURL = url; - - } - - @Override - public InputStream loadData(Priority priority) throws Exception { - - Account mAccount = AccountUtils.getCurrentOwnCloudAccount(MainApp.getAppContext()); - OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, MainApp.getAppContext()); - OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, MainApp.getAppContext()); - - if (mClient != null) { - GetMethod get; - try { - get = new GetMethod(mURL); - get.setRequestHeader("Cookie", "nc_sameSiteCookielax=true;nc_sameSiteCookiestrict=true"); - get.setRequestHeader(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE); - int status = mClient.executeMethod(get); - if (status == HttpStatus.SC_OK) { - return get.getResponseBodyAsStream(); - } else { - mClient.exhaustResponse(get.getResponseBodyAsStream()); - } - } catch (Exception e) { - Log_OC.e(TAG, e.getMessage(), e); - } - } - return null; - } - - @Override - public void cleanup() { - Log_OC.i(TAG,"Cleanup"); - } - - @Override - public String getId() { - return mURL; - } - - @Override - public void cancel() { - Log_OC.i(TAG,"Cancel"); - } -} diff --git a/src/main/java/com/owncloud/android/utils/glide/HttpStreamGlideContainerFetcher.java b/src/main/java/com/owncloud/android/utils/glide/HttpStreamGlideContainerFetcher.java new file mode 100644 index 0000000000..2f3bc0425b --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/HttpStreamGlideContainerFetcher.java @@ -0,0 +1,89 @@ +/* + * Nextcloud Android client application + * + * @author Alejandro Bautista + * Copyright (C) 2017 Alejandro Bautista + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.owncloud.android.utils.glide; + +import android.support.annotation.NonNull; + +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.data.DataFetcher; +import com.owncloud.android.lib.common.operations.RemoteOperation; +import com.owncloud.android.lib.common.utils.Log_OC; + +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.methods.GetMethod; + +import java.io.InputStream; + +/** + * Fetcher with Nextcloud client + */ +public class HttpStreamGlideContainerFetcher implements DataFetcher<InputStream> { + + private static final String TAG = HttpStreamGlideContainerFetcher.class.getName(); + private final GlideContainer container; + + public HttpStreamGlideContainerFetcher(GlideContainer container) { + this.container = container; + } + + @Override + public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { + Log_OC.d(TAG, "load thumbnail for: " + container.url); + + GetMethod get; + try { + get = new GetMethod(container.url); + get.setRequestHeader("Cookie", "nc_sameSiteCookielax=true;nc_sameSiteCookiestrict=true"); + get.setRequestHeader(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE); + + int status = container.client.executeMethod(get); + if (status == HttpStatus.SC_OK) { + callback.onDataReady(get.getResponseBodyAsStream()); + } else { + container.client.exhaustResponse(get.getResponseBodyAsStream()); + callback.onLoadFailed(new Exception("Thumbnail failed")); + } + } catch (Exception e) { + Log_OC.e(TAG, e.getMessage(), e); + } + } + + public void cleanup() { + Log_OC.i(TAG, "Cleanup"); + } + + @Override + public void cancel() { + Log_OC.i(TAG, "Cancel"); + } + + @NonNull + @Override + public Class<InputStream> getDataClass() { + return InputStream.class; + } + + @NonNull + @Override + public DataSource getDataSource() { + return DataSource.REMOTE; + } +} diff --git a/src/main/java/com/owncloud/android/utils/glide/NextcloudGlideModule.java b/src/main/java/com/owncloud/android/utils/glide/NextcloudGlideModule.java new file mode 100644 index 0000000000..3b9d4fbff0 --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/NextcloudGlideModule.java @@ -0,0 +1,58 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.owncloud.android.utils.glide; + +import android.content.Context; +import android.graphics.drawable.PictureDrawable; +import android.support.annotation.NonNull; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.Registry; +import com.bumptech.glide.annotation.GlideModule; +import com.bumptech.glide.module.AppGlideModule; +import com.caverock.androidsvg.SVG; +import com.owncloud.android.utils.svg.SvgDecoder; +import com.owncloud.android.utils.svg.SvgDrawableTranscoder; + +import java.io.InputStream; + +/** + * Module for generating api. + */ +@GlideModule +public class NextcloudGlideModule extends AppGlideModule { + @Override + public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { + registry.prepend(GlideContainer.class, InputStream.class, new GlideContainerModelLoaderFactory()); + registry.prepend(GlideOcFile.class, InputStream.class, new OCFileModelLoaderFactory()); + registry.prepend(String.class, InputStream.class, new StringModelLoaderFactory()); + registry.prepend(GlideAvatar.class, InputStream.class, new AvatarModelLoaderFactory()); + registry.register(SVG.class, PictureDrawable.class, new SvgDrawableTranscoder()) + .append(InputStream.class, SVG.class, new SvgDecoder()); + } + + // Disable manifest parsing to avoid adding similar modules twice. + @Override + public boolean isManifestParsingEnabled() { + return false; + } +} diff --git a/src/main/java/com/owncloud/android/utils/glide/OCFileModelLoader.java b/src/main/java/com/owncloud/android/utils/glide/OCFileModelLoader.java new file mode 100644 index 0000000000..a9bf0ea563 --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/OCFileModelLoader.java @@ -0,0 +1,55 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.owncloud.android.utils.glide; + +import android.support.annotation.NonNull; + +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.model.ModelLoader; +import com.owncloud.android.datamodel.OCFile; + +import java.io.InputStream; + +public class OCFileModelLoader implements ModelLoader<GlideOcFile, InputStream> { + @Override + public boolean handles(@NonNull GlideOcFile model) { + return true; + } + + @Override + public LoadData<InputStream> buildLoadData(@NonNull GlideOcFile model, int width, int height, @NonNull Options options) { + OCFile file = model.getFile(); + + if (GlideOCFileType.thumbnail.equals(model.getType())) { + String path; + if (model.getFile().getStoragePath().isEmpty()) { + path = model.getPath(); + } else { + path = model.getFile().getStoragePath(); + } + + return new LoadData<>(GlideKey.serverThumbnail(file), new FileFetcher(path)); + } else { + return new LoadData<>(GlideKey.resizedImage(file), new FileFetcher(file.getStoragePath())); + } + } +} diff --git a/src/main/java/com/owncloud/android/utils/glide/OCFileModelLoaderFactory.java b/src/main/java/com/owncloud/android/utils/glide/OCFileModelLoaderFactory.java new file mode 100644 index 0000000000..7892095e51 --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/OCFileModelLoaderFactory.java @@ -0,0 +1,44 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.owncloud.android.utils.glide; + +import android.support.annotation.NonNull; + +import com.bumptech.glide.load.model.ModelLoader; +import com.bumptech.glide.load.model.ModelLoaderFactory; +import com.bumptech.glide.load.model.MultiModelLoaderFactory; + +import java.io.InputStream; + +public class OCFileModelLoaderFactory implements ModelLoaderFactory<GlideOcFile, InputStream> { + + @NonNull + @Override + public ModelLoader<GlideOcFile, InputStream> build(@NonNull MultiModelLoaderFactory unused) { + return new OCFileModelLoader(); + } + + @Override + public void teardown() { + // Do nothing. + } +} diff --git a/src/main/java/com/owncloud/android/utils/glide/StringModelLoaderFactory.java b/src/main/java/com/owncloud/android/utils/glide/StringModelLoaderFactory.java new file mode 100644 index 0000000000..e545abe09f --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/glide/StringModelLoaderFactory.java @@ -0,0 +1,44 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.owncloud.android.utils.glide; + +import android.support.annotation.NonNull; + +import com.bumptech.glide.load.model.ModelLoader; +import com.bumptech.glide.load.model.ModelLoaderFactory; +import com.bumptech.glide.load.model.MultiModelLoaderFactory; + +import java.io.InputStream; + +public class StringModelLoaderFactory implements ModelLoaderFactory<String, InputStream> { + + @NonNull + @Override + public ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory unused) { + return new GlideStringStreamLoader(); + } + + @Override + public void teardown() { + // Do nothing. + } +} diff --git a/src/main/java/com/owncloud/android/utils/svg/SvgDecoder.java b/src/main/java/com/owncloud/android/utils/svg/SvgDecoder.java index 5a4f869f45..dea6be78f9 100644 --- a/src/main/java/com/owncloud/android/utils/svg/SvgDecoder.java +++ b/src/main/java/com/owncloud/android/utils/svg/SvgDecoder.java @@ -11,6 +11,9 @@ package com.owncloud.android.utils.svg; +import android.support.annotation.NonNull; + +import com.bumptech.glide.load.Options; import com.bumptech.glide.load.ResourceDecoder; import com.bumptech.glide.load.engine.Resource; import com.bumptech.glide.load.resource.SimpleResource; @@ -25,19 +28,13 @@ import java.io.InputStream; * Decodes an SVG internal representation from an {@link InputStream}. */ public class SvgDecoder implements ResourceDecoder<InputStream, SVG> { - private int height = -1; - private int width = -1; - - public SvgDecoder(){ - - } - - public SvgDecoder(int height, int width) { - this.height = height; - this.width = width; + @Override + public boolean handles(@NonNull InputStream source, @NonNull Options options) { + return true; } - public Resource<SVG> decode(InputStream source, int w, int h) throws IOException { + public Resource<SVG> decode(@NonNull InputStream source, int width, int height, @NonNull Options options) + throws IOException { try { SVG svg = SVG.getFromInputStream(source); @@ -54,9 +51,4 @@ public class SvgDecoder implements ResourceDecoder<InputStream, SVG> { throw new IOException("Cannot load SVG from stream", ex); } } - - @Override - public String getId() { - return "SvgDecoder.com.owncloud.android"; - } } diff --git a/src/main/java/com/owncloud/android/utils/svg/SvgDrawableTranscoder.java b/src/main/java/com/owncloud/android/utils/svg/SvgDrawableTranscoder.java index 38cb2fb06d..80f39fe3b2 100644 --- a/src/main/java/com/owncloud/android/utils/svg/SvgDrawableTranscoder.java +++ b/src/main/java/com/owncloud/android/utils/svg/SvgDrawableTranscoder.java @@ -12,7 +12,10 @@ package com.owncloud.android.utils.svg; import android.graphics.Picture; import android.graphics.drawable.PictureDrawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import com.bumptech.glide.load.Options; import com.bumptech.glide.load.engine.Resource; import com.bumptech.glide.load.resource.SimpleResource; import com.bumptech.glide.load.resource.transcode.ResourceTranscoder; @@ -22,16 +25,12 @@ import com.caverock.androidsvg.SVG; * Convert the {@link SVG}'s internal representation to an Android-compatible one ({@link Picture}). */ public class SvgDrawableTranscoder implements ResourceTranscoder<SVG, PictureDrawable> { + @Nullable @Override - public Resource<PictureDrawable> transcode(Resource<SVG> toTranscode) { + public Resource<PictureDrawable> transcode(@NonNull Resource<SVG> toTranscode, @NonNull Options options) { SVG svg = toTranscode.get(); Picture picture = svg.renderToPicture(); PictureDrawable drawable = new PictureDrawable(picture); - return new SimpleResource<PictureDrawable>(drawable); - } - - @Override - public String getId() { - return ""; + return new SimpleResource<>(drawable); } } diff --git a/src/main/java/com/owncloud/android/utils/svg/SvgSoftwareLayerSetter.java b/src/main/java/com/owncloud/android/utils/svg/SvgSoftwareLayerSetter.java index ab6f05d323..e8ca8acaaf 100644 --- a/src/main/java/com/owncloud/android/utils/svg/SvgSoftwareLayerSetter.java +++ b/src/main/java/com/owncloud/android/utils/svg/SvgSoftwareLayerSetter.java @@ -10,34 +10,45 @@ */ package com.owncloud.android.utils.svg; -import android.annotation.TargetApi; import android.graphics.drawable.PictureDrawable; -import android.os.Build; +import android.support.annotation.Nullable; import android.widget.ImageView; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.engine.GlideException; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.ImageViewTarget; import com.bumptech.glide.request.target.Target; -@TargetApi(Build.VERSION_CODES.HONEYCOMB) -public class SvgSoftwareLayerSetter<T> implements RequestListener<T, PictureDrawable> { - +/** + * Listener which updates the {@link ImageView} to be software rendered, because + * {@link com.caverock.androidsvg.SVG SVG}/{@link android.graphics.Picture Picture} can't render on + * a hardware backed {@link android.graphics.Canvas Canvas}. + */ +public class SvgSoftwareLayerSetter implements RequestListener<PictureDrawable> { @Override - public boolean onException(Exception e, T model, Target<PictureDrawable> target, boolean isFirstResource) { - ImageView view = ((ImageViewTarget<?>) target).getView(); - if (Build.VERSION_CODES.HONEYCOMB <= Build.VERSION.SDK_INT) { + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<PictureDrawable> target, + boolean isFirstResource) { + try { + ImageView view = ((ImageViewTarget<?>) target).getView(); view.setLayerType(ImageView.LAYER_TYPE_NONE, null); + } catch (Exception e1) { + // ignore } + return false; } @Override - public boolean onResourceReady(PictureDrawable resource, T model, Target<PictureDrawable> target, - boolean isFromMemoryCache, boolean isFirstResource) { - ImageView view = ((ImageViewTarget<?>) target).getView(); - if (Build.VERSION_CODES.HONEYCOMB <= Build.VERSION.SDK_INT) { + public boolean onResourceReady(PictureDrawable resource, Object model, Target<PictureDrawable> target, + DataSource dataSource, boolean isFirstResource) { + try { + ImageView view = ((ImageViewTarget<?>) target).getView(); view.setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null); + } catch (Exception e) { + // ignore } + return false; } -}
\ No newline at end of file +} diff --git a/src/main/res/layout/grid_image.xml b/src/main/res/layout/grid_image.xml index 545bccd683..fb3c5134c4 100644 --- a/src/main/res/layout/grid_image.xml +++ b/src/main/res/layout/grid_image.xml @@ -38,6 +38,14 @@ android:src="@drawable/folder" android:contentDescription="@null"/> + <ImageView + android:id="@+id/play_icon" + android:layout_width="36dp" + android:layout_height="36dp" + android:layout_gravity="center" + android:visibility="gone" + android:contentDescription="@string/thumbnail" + android:src="@drawable/ic_play_arrow"/> <ImageView android:id="@+id/favorite_action" diff --git a/src/main/res/layout/grid_item.xml b/src/main/res/layout/grid_item.xml index 29f8afc9fb..3ca160ee4a 100644 --- a/src/main/res/layout/grid_item.xml +++ b/src/main/res/layout/grid_item.xml @@ -38,6 +38,15 @@ android:contentDescription="@null"/>
<ImageView
+ android:id="@+id/play_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_gravity="center"
+ android:visibility="gone"
+ android:contentDescription="@string/thumbnail"
+ android:src="@drawable/ic_play_arrow"/>
+
+ <ImageView
android:id="@+id/favorite_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -105,4 +114,4 @@ android:textColor="@color/textColor"
android:textSize="@dimen/grid_item_text_size" />
-</com.owncloud.android.ui.SquareLinearLayout>
\ No newline at end of file +</com.owncloud.android.ui.SquareLinearLayout>
diff --git a/src/main/res/layout/list_item.xml b/src/main/res/layout/list_item.xml index f19f82a5c3..35d1852b84 100644 --- a/src/main/res/layout/list_item.xml +++ b/src/main/res/layout/list_item.xml @@ -1,203 +1,212 @@ -<?xml version="1.0" encoding="UTF-8"?><!--
- ownCloud Android client application
-
- Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/ListItemLayout"
- android:layout_width="match_parent"
- android:layout_height="@dimen/standard_list_item_size"
- android:background="@drawable/list_selector"
- android:descendantFocusability="blocksDescendants"
- android:foreground="?android:attr/selectableItemBackground"
- android:baselineAligned="false"
- android:orientation="horizontal">
-
- <RelativeLayout
- android:layout_width="@dimen/standard_list_item_size"
- android:layout_height="@dimen/standard_list_item_size"
- android:paddingBottom="@dimen/standard_padding"
- android:paddingEnd="@dimen/standard_quarter_padding"
- android:paddingLeft="@dimen/zero"
- android:paddingRight="@dimen/standard_quarter_padding"
- android:paddingStart="@dimen/zero"
- android:paddingTop="@dimen/standard_padding">
-
- <ImageView
- android:id="@+id/thumbnail"
- android:layout_width="@dimen/file_icon_size"
- android:layout_height="@dimen/file_icon_size"
- android:layout_centerInParent="true"
- android:layout_marginLeft="@dimen/standard_half_margin"
- android:layout_marginStart="@dimen/standard_half_margin"
- android:contentDescription="@null"
- android:src="@drawable/folder" />
-
- <ImageView
- android:id="@+id/favorite_action"
- android:layout_width="@dimen/list_item_favorite_action_layout_width"
- android:layout_height="@dimen/list_item_favorite_action_layout_height"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:layout_marginEnd="@dimen/standard_quarter_margin"
- android:layout_marginRight="@dimen/standard_quarter_margin"
- android:contentDescription="@string/favorite"
- android:src="@drawable/badge_favorite" />
-
- <ImageView
- android:id="@+id/keptOfflineIcon"
- android:layout_width="@dimen/list_item_kept_offline_icon_layout_width"
- android:layout_height="@dimen/list_item_kept_offline_icon_layout_height"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- android:layout_marginEnd="@dimen/list_item_kept_offline_icon_layout_right_end_margin"
- android:layout_marginRight="@dimen/list_item_kept_offline_icon_layout_right_end_margin"
- android:contentDescription="@string/available_offline_icon"
- android:src="@drawable/ic_available_offline" />
-
- <ImageView
- android:id="@+id/localFileIndicator"
- android:layout_width="@dimen/list_item_local_file_indicator_layout_width"
- android:layout_height="@dimen/list_item_local_file_indicator_layout_height"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- android:layout_marginEnd="@dimen/standard_quarter_margin"
- android:layout_marginRight="@dimen/standard_quarter_margin"
- android:contentDescription="@string/downloader_download_succeeded_ticker"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_synced" />
-
- </RelativeLayout>
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="top"
- android:orientation="vertical"
- android:paddingTop="@dimen/standard_padding">
-
- <TextView
- android:id="@+id/Filename"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:ellipsize="middle"
- android:singleLine="true"
- android:text="@string/placeholder_filename"
- android:textColor="@color/textColor"
- android:textSize="@dimen/two_line_primary_text_size" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <TextView
- android:id="@+id/file_size"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/placeholder_fileSize"
- android:textColor="@color/list_item_lastmod_and_filesize_text"
- android:textSize="@dimen/two_line_secondary_text_size" />
-
- <TextView
- android:id="@+id/file_separator"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="end"
- android:paddingEnd="@dimen/standard_quarter_padding"
- android:paddingLeft="@dimen/zero"
- android:paddingRight="@dimen/standard_quarter_padding"
- android:paddingStart="@dimen/zero"
- android:text="@string/info_separator"
- android:textColor="@color/list_item_lastmod_and_filesize_text"
- android:textSize="@dimen/two_line_secondary_text_size" />
-
- <TextView
- android:id="@+id/last_mod"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="end"
- android:text="@string/placeholder_media_time"
- android:textColor="@color/list_item_lastmod_and_filesize_text"
- android:textSize="@dimen/two_line_secondary_text_size" />
-
- </LinearLayout>
-
- </LinearLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingEnd="@dimen/zero"
- android:paddingLeft="@dimen/standard_half_padding"
- android:paddingRight="@dimen/zero"
- android:paddingStart="@dimen/standard_half_padding">
-
- <ImageView
- android:id="@+id/sharedIcon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_centerVertical="true"
- android:clickable="true"
- android:contentDescription="@string/shared_icon_share"
- android:focusable="true"
- android:paddingEnd="@dimen/list_item_share_right_margin"
- android:paddingLeft="@dimen/standard_half_padding"
- android:paddingRight="@dimen/list_item_share_right_margin"
- android:paddingStart="@dimen/standard_half_padding"
- android:src="@drawable/ic_unshared" />
-
- <ImageView
- android:id="@+id/custom_checkbox"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_centerVertical="true"
- android:layout_toEndOf="@id/sharedIcon"
- android:layout_toRightOf="@id/sharedIcon"
- android:clickable="false"
- android:contentDescription="@string/checkbox"
- android:focusable="false"
- android:paddingEnd="@dimen/alternate_padding"
- android:paddingLeft="@dimen/standard_half_padding"
- android:paddingRight="@dimen/alternate_padding"
- android:paddingStart="@dimen/standard_half_padding"
- android:src="@drawable/ic_checkbox_blank_outline" />
-
- <ImageView
- android:id="@+id/overflow_menu"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_centerVertical="true"
- android:layout_toEndOf="@id/custom_checkbox"
- android:layout_toRightOf="@id/custom_checkbox"
- android:clickable="true"
- android:contentDescription="@string/overflow_menu"
- android:focusable="true"
- android:paddingEnd="@dimen/alternate_padding"
- android:paddingLeft="@dimen/standard_half_padding"
- android:paddingRight="@dimen/alternate_padding"
- android:paddingStart="@dimen/standard_half_padding"
- android:src="@drawable/ic_dots_vertical" />
-
- </RelativeLayout>
-
-</LinearLayout>
+<?xml version="1.0" encoding="UTF-8"?><!-- + ownCloud Android client application + + Copyright (C) 2012 Bartek Przybylski + Copyright (C) 2015 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 <http://www.gnu.org/licenses/>. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/ListItemLayout" + android:layout_width="match_parent" + android:layout_height="@dimen/standard_list_item_size" + android:background="@drawable/list_selector" + android:descendantFocusability="blocksDescendants" + android:foreground="?android:attr/selectableItemBackground" + android:baselineAligned="false" + android:orientation="horizontal"> + + <RelativeLayout + android:layout_width="@dimen/standard_list_item_size" + android:layout_height="@dimen/standard_list_item_size" + android:paddingBottom="@dimen/standard_padding" + android:paddingEnd="@dimen/standard_quarter_padding" + android:paddingLeft="@dimen/zero" + android:paddingRight="@dimen/standard_quarter_padding" + android:paddingStart="@dimen/zero" + android:paddingTop="@dimen/standard_padding"> + + <ImageView + android:id="@+id/thumbnail" + android:layout_width="@dimen/file_icon_size" + android:layout_height="@dimen/file_icon_size" + android:layout_centerInParent="true" + android:layout_marginLeft="@dimen/standard_half_margin" + android:layout_marginStart="@dimen/standard_half_margin" + android:contentDescription="@null" + android:src="@drawable/file_image"/> + + <ImageView + android:id="@+id/play_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:visibility="gone" + android:contentDescription="@string/thumbnail" + android:src="@drawable/ic_play_arrow"/> + + <ImageView + android:id="@+id/favorite_action" + android:layout_width="@dimen/list_item_favorite_action_layout_width" + android:layout_height="@dimen/list_item_favorite_action_layout_height" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_alignParentTop="true" + android:layout_marginEnd="@dimen/standard_quarter_margin" + android:layout_marginRight="@dimen/standard_quarter_margin" + android:contentDescription="@string/favorite" + android:src="@drawable/badge_favorite" /> + + <ImageView + android:id="@+id/keptOfflineIcon" + android:layout_width="@dimen/list_item_kept_offline_icon_layout_width" + android:layout_height="@dimen/list_item_kept_offline_icon_layout_height" + android:layout_alignParentBottom="true" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_marginEnd="@dimen/list_item_kept_offline_icon_layout_right_end_margin" + android:layout_marginRight="@dimen/list_item_kept_offline_icon_layout_right_end_margin" + android:contentDescription="@string/available_offline_icon" + android:src="@drawable/ic_available_offline" /> + + <ImageView + android:id="@+id/localFileIndicator" + android:layout_width="@dimen/list_item_local_file_indicator_layout_width" + android:layout_height="@dimen/list_item_local_file_indicator_layout_height" + android:layout_alignParentBottom="true" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_marginEnd="@dimen/standard_quarter_margin" + android:layout_marginRight="@dimen/standard_quarter_margin" + android:contentDescription="@string/downloader_download_succeeded_ticker" + android:scaleType="fitCenter" + android:src="@drawable/ic_synced" /> + + </RelativeLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:gravity="top" + android:orientation="vertical" + android:paddingTop="@dimen/standard_padding"> + + <TextView + android:id="@+id/Filename" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:ellipsize="middle" + android:singleLine="true" + android:text="@string/placeholder_filename" + android:textColor="@color/textColor" + android:textSize="@dimen/two_line_primary_text_size" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <TextView + android:id="@+id/file_size" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/placeholder_fileSize" + android:textColor="@color/list_item_lastmod_and_filesize_text" + android:textSize="@dimen/two_line_secondary_text_size" /> + + <TextView + android:id="@+id/file_separator" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="end" + android:paddingEnd="@dimen/standard_quarter_padding" + android:paddingLeft="@dimen/zero" + android:paddingRight="@dimen/standard_quarter_padding" + android:paddingStart="@dimen/zero" + android:text="@string/info_separator" + android:textColor="@color/list_item_lastmod_and_filesize_text" + android:textSize="@dimen/two_line_secondary_text_size" /> + + <TextView + android:id="@+id/last_mod" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="end" + android:text="@string/placeholder_media_time" + android:textColor="@color/list_item_lastmod_and_filesize_text" + android:textSize="@dimen/two_line_secondary_text_size" /> + + </LinearLayout> + + </LinearLayout> + + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:paddingEnd="@dimen/zero" + android:paddingLeft="@dimen/standard_half_padding" + android:paddingRight="@dimen/zero" + android:paddingStart="@dimen/standard_half_padding"> + + <ImageView + android:id="@+id/sharedIcon" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_centerVertical="true" + android:clickable="true" + android:contentDescription="@string/shared_icon_share" + android:focusable="true" + android:paddingEnd="@dimen/list_item_share_right_margin" + android:paddingLeft="@dimen/standard_half_padding" + android:paddingRight="@dimen/list_item_share_right_margin" + android:paddingStart="@dimen/standard_half_padding" + android:src="@drawable/ic_unshared" /> + + <ImageView + android:id="@+id/custom_checkbox" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_centerVertical="true" + android:layout_toEndOf="@id/sharedIcon" + android:layout_toRightOf="@id/sharedIcon" + android:clickable="false" + android:contentDescription="@string/checkbox" + android:focusable="false" + android:paddingEnd="@dimen/alternate_padding" + android:paddingLeft="@dimen/standard_half_padding" + android:paddingRight="@dimen/alternate_padding" + android:paddingStart="@dimen/standard_half_padding" + android:src="@drawable/ic_checkbox_blank_outline" /> + + <ImageView + android:id="@+id/overflow_menu" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_centerVertical="true" + android:layout_toEndOf="@id/custom_checkbox" + android:layout_toRightOf="@id/custom_checkbox" + android:clickable="true" + android:contentDescription="@string/overflow_menu" + android:focusable="true" + android:paddingEnd="@dimen/alternate_padding" + android:paddingLeft="@dimen/standard_half_padding" + android:paddingRight="@dimen/alternate_padding" + android:paddingStart="@dimen/standard_half_padding" + android:src="@drawable/ic_dots_vertical" /> + + </RelativeLayout> + +</LinearLayout> diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 43d64a0541..c781e4b0d9 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -818,9 +818,8 @@ <string name="file_rename">Rename</string> <string name="fab_label">Add or upload</string> <string name="account_creation_failed">Account creation failed</string> - <string name="single_sign_on_request_token" formatted="true">Allow %1$s to access your Nextcloud account %2$s?</string> <string name="permission_deny">Deny</string> <string name="permission_allow">Allow</string> - + <string name="thumbnail">Thumbnail</string> </resources> diff --git a/src/main/res/xml/exposed_filepaths.xml b/src/main/res/xml/exposed_filepaths.xml index 154694811d..fa1b6209aa 100644 --- a/src/main/res/xml/exposed_filepaths.xml +++ b/src/main/res/xml/exposed_filepaths.xml @@ -1,11 +1,16 @@ <?xml version="1.0" encoding="utf-8"?> -<paths> +<paths xmlns:tools="http://schemas.android.com/tools"> + <cache-path + name="share" + path="image_manager_disk_cache" + tools:path="DiskCache.Factory.DEFAULT_DISK_CACHE_DIR"/> <files-path name="user_files_internal" path="nextcloud/"/> <files-path path="log/" name="log"/> <external-path name="external_files" path="."/> <root-path name="external_files" path="/storage/" /> + <!-- yes, valid for ALL external storage and not only our app folder, since we can't use @string/data_folder as a value for 'path' attribute; in practice, we will only generate URIs in our folders, of course --> </paths> diff --git a/src/main/res/xml/filepaths.xml b/src/main/res/xml/filepaths.xml new file mode 100644 index 0000000000..9e8de1ed2e --- /dev/null +++ b/src/main/res/xml/filepaths.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Nextcloud Android client application + + Copyright (C) 2018 Tobias Kaminsky + Copyright (C) 2018 Nextcloud + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + License as published by the Free Software Foundation; either + version 3 of the License, or any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU AFFERO GENERAL PUBLIC LICENSE for more details. + + You should have received a copy of the GNU Affero General Public + License along with this program. If not, see <http://www.gnu.org/licenses/>. +--> +<paths xmlns:tools="http://schemas.android.com/tools"> + <cache-path + name="share" + path="image_manager_disk_cache" + tools:path="DiskCache.Factory.DEFAULT_DISK_CACHE_DIR"/> +</paths> |