Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
tobiasKaminsky 2018-07-06 10:16:50 +02:00
parent df3bb79365
commit b80bba82c1
No known key found for this signature in database
GPG Key ID: 0E00D4D47D0C5AF7
73 changed files with 2264 additions and 2470 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,16 +1395,13 @@ 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());
cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,53 +378,43 @@ 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);
}
} 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());
}
}
if (client == null) {
client = AccountUtils.getClientForCurrentAccount(mContext);
}
CustomViewTarget thumbnailTarget = new CustomViewTarget<ImageView, Drawable>(thumbnailView) {
@Override
protected void onResourceCleared(@Nullable Drawable placeholder) {
thumbnailView.setImageDrawable(placeholder);
}
@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);
}
}

View File

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

View File

@ -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 (MimeTypeUtil.isImageOrVideo(file)) {
try {
int placeholder = MimeTypeUtil.isImage(file) ? R.drawable.file_image : R.drawable.file_movie;
if (thumbnail != null) {
if (MimeTypeUtil.isVideo(file)) {
Bitmap withOverlay = ThumbnailsCacheManager.addVideoOverlay(thumbnail);
thumbnailView.setImageBitmap(withOverlay);
} else {
thumbnailView.setImageBitmap(thumbnail);
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));
}
} 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 ("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);

View File

@ -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);
if (MimeTypeUtil.isImageOrVideo(new File(item.getLocalPath()))) {
if (item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED) {
/*
* 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());
OCFile file = mParentActivity.getStorageManager().getFileByPath(item.getRemotePath());
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 (file != null) {
DisplayUtils.downloadThumbnail(file, itemViewHolder.thumbnail, client, mParentActivity);
}
}
if ("image/png".equals(item.getMimeType())) {
itemViewHolder.thumbnail.setBackgroundColor(mParentActivity.getResources()
.getColor(R.color.background_color));
}
} 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));

View File

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

View File

@ -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;
}
// Thumbnail
GlideContainer container = new GlideContainer();
int placeholder = MimeTypeUtil.isVideo(getFile()) ? R.drawable.file_movie : R.drawable.file_image;
// 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());
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());
if (resizedImage == null) {
resizedImage = thumbnail;
}
// resized image
GlideContainer containerResizedImage = new GlideContainer();
final ThumbnailsCacheManager.AsyncResizedImageDrawable asyncDrawable =
new ThumbnailsCacheManager.AsyncResizedImageDrawable(
MainApp.getAppContext().getResources(),
resizedImage,
task
);
Point p = DisplayUtils.getScreenDimension();
int pxW = p.x;
int pxH = p.y;
activity.setPreviewImageDrawable(asyncDrawable);
activatePreviewImage();
previewLoaded = true;
task.execute(getFile());
}
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
*/

View File

@ -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()
);
String userId = mAccountMgr.getUserData(currentAccount,
com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID);
OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
getClientFor(ocAccount, MainApp.getAppContext());
if (TextUtils.isEmpty(userId)) {
userId = client.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(client);
if (TextUtils.isEmpty(userId)) {
userId = mClient.getCredentials().getUsername();
}
ToggleFavoriteOperation toggleFavoriteOperation = new ToggleFavoriteOperation(event.shouldFavorite,
event.remotePath, userId);
RemoteOperationResult remoteOperationResult = toggleFavoriteOperation.execute(mClient);
if (remoteOperationResult.isSuccess()) {
mAdapter.setFavoriteAttributeForItemID(event.remoteId, event.shouldFavorite);
}
} 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());
ToggleEncryptionOperation toggleEncryptionOperation = new ToggleEncryptionOperation(event.localId,
event.remotePath, event.shouldBeEncrypted);
RemoteOperationResult remoteOperationResult = toggleEncryptionOperation.execute(client, true);
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);
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();
}
}

View File

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

View File

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

View File

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

View File

@ -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()));
try {
Account account = AccountUtils.getCurrentOwnCloudAccount(getContext());
OwnCloudAccount ocAccount = new OwnCloudAccount(account, MainApp.getAppContext());
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()));
OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
getClientFor(ocAccount, MainApp.getAppContext());
if (thumbnail != null) {
mImageView.setImageBitmap(thumbnail);
mImageView.setVisibility(View.VISIBLE);
mBitmap = thumbnail;
} else {
thumbnail = ThumbnailsCacheManager.mDefaultImg;
}
int thumbnailW = DisplayUtils.getThumbnailDimension();
int thumbnailH = DisplayUtils.getThumbnailDimension();
// 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());
}
// 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);

View File

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

View File

@ -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);
@ -226,12 +226,6 @@ public class TrashbinActivity extends FileActivity implements TrashbinActivityIn
return true;
}
@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);
}
}
}
}

View File

@ -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;
}
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver());
// show avatar immediately, might be outdated
if (view instanceof ImageView) {
ImageView imageView = (ImageView) view;
imageView.setContentDescription(account.name);
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;
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>() {
// first show old one
Drawable avatar = BitmapUtils.bitmapToCircularBitmapDrawable(resources,
ThumbnailsCacheManager.getBitmapFromDiskCache(avatarKey));
GetMethod get;
ArbitraryDataProvider arbitraryDataProvider;
String accountName;
// 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);
@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);
}
int status = client.executeMethod(get);
Log_OC.d(TAG, "glide: status: " + status);
// 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 (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);
final ThumbnailsCacheManager.AsyncAvatarDrawable asyncDrawable =
new ThumbnailsCacheManager.AsyncAvatarDrawable(resources, avatar, task);
listener.avatarGenerated(asyncDrawable, callContext);
task.execute(userId);
}
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
}
}
};
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));
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 class CustomGlideStreamLoader implements StreamModelLoader<String> {
@Override
public DataFetcher<InputStream> getResourceFetcher(String url, int width, int height) {
return new HttpStreamFetcher(url);
public GlideAvatar(ObjectKey key, InputStream avatar) {
this.key = key;
this.inputStream = avatar;
}
public ObjectKey getKey() {
return key;
}
public InputStream getInputStream() {
return inputStream;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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(){
@Override
public boolean handles(@NonNull InputStream source, @NonNull Options options) {
return true;
}
public SvgDecoder(int height, int width) {
this.height = height;
this.width = width;
}
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";
}
}

View File

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

View File

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

View File

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

View File

@ -37,6 +37,15 @@
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"
android:layout_width="wrap_content"
@ -105,4 +114,4 @@
android:textColor="@color/textColor"
android:textSize="@dimen/grid_item_text_size" />
</com.owncloud.android.ui.SquareLinearLayout>
</com.owncloud.android.ui.SquareLinearLayout>

View File

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

View File

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

View File

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

View File

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