Add trashbin support

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
tobiasKaminsky 2018-05-02 12:49:25 +02:00 committed by AndyScherzinger
parent fd9e468e93
commit f8801115f2
No known key found for this signature in database
GPG Key ID: 6CADC7E3523C308B
46 changed files with 1936 additions and 81 deletions

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 16 16" width="16" height="16">
<path
d="m9.025 1.08c-3.95 0-6.535 3.447-6.364 6.72h-2.161l3.904 3.92 4.08-3.874h-2.147c-0.237-1.7 1.163-3.114 2.689-3.092 1.595 0.024 2.8 1.23 2.8 2.734 0.09 1.594-1.63 3.428-3.966 2.53 0 1.23 0.003 2.545 0 3.765 4.19 0.83 7.64-2.51 7.64-6.25 0-3.563-2.92-6.453-6.475-6.453z"/>
</svg>

After

Width:  |  Height:  |  Size: 396 B

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
viewbox="0 0 16 16"
width="16"
height="16"
id="svg4"
sodipodi:docname="ic_delete.svg"
inkscape:version="0.92.2 2405546, 2018-03-11">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8"/>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1600"
inkscape:window-height="835"
id="namedview6"
showgrid="false"
inkscape:zoom="14.75"
inkscape:cx="-3.8305085"
inkscape:cy="8"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4"/>
<path
d="M6.5 1L6 2H3c-.554 0-1 .446-1 1v1h12V3c0-.554-.446-1-1-1h-3l-.5-1zM3 5l.875 9c.06.55.573 1 1.125 1h6c.552 0 1.064-.45 1.125-1L13 5z"
id="path2"
style="fill:#c4c4c4;fill-opacity:1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -109,7 +109,7 @@ public class OCFileUnitTest {
assertThat(fileReadFromParcel.getFileId(), is(ID));
assertThat(fileReadFromParcel.getParentId(), is(PARENT_ID));
assertThat(fileReadFromParcel.getStoragePath(), is(STORAGE_PATH));
assertThat(fileReadFromParcel.getMimetype(), is(MIME_TYPE));
assertThat(fileReadFromParcel.getMimeType(), is(MIME_TYPE));
assertThat(fileReadFromParcel.getFileLength(), is(FILE_LENGTH));
assertThat(fileReadFromParcel.getCreationTimestamp(), is(CREATION_TIMESTAMP));
assertThat(fileReadFromParcel.getModificationTimestamp(), is(MODIFICATION_TIMESTAMP));

View File

@ -250,6 +250,9 @@
<activity android:name=".ui.errorhandling.ErrorShowActivity" />
<activity android:name=".ui.activity.UploadListActivity" />
<activity
android:name=".ui.trashbin.TrashbinActivity"
android:configChanges="orientation|screenSize|keyboardHidden"/>
<activity android:name=".ui.activity.WhatsNewActivity"
android:theme="@style/Theme.ownCloud.noActionBar.Login" />

View File

@ -185,7 +185,7 @@ public class FileDataStorageManager {
);
cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimeType());
cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
cv.put(ProviderTableMeta.FILE_ENCRYPTED_NAME, file.getEncryptedFileName());
cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
@ -430,7 +430,7 @@ public class FileDataStorageManager {
);
cv.put(ProviderTableMeta.FILE_CREATION, folder.getCreationTimestamp());
cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, 0);
cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, folder.getMimetype());
cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, folder.getMimeType());
cv.put(ProviderTableMeta.FILE_NAME, folder.getFileName());
cv.put(ProviderTableMeta.FILE_PARENT, folder.getParentId());
cv.put(ProviderTableMeta.FILE_PATH, folder.getRemotePath());
@ -455,7 +455,7 @@ public class FileDataStorageManager {
cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData());
cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimeType());
cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
cv.put(ProviderTableMeta.FILE_ENCRYPTED_NAME, file.getEncryptedFileName());
cv.put(ProviderTableMeta.FILE_PARENT, folder.getFileId());
@ -1367,7 +1367,7 @@ public class FileDataStorageManager {
);
cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimeType());
cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());

View File

@ -35,13 +35,14 @@ import com.owncloud.android.R;
import com.owncloud.android.lib.common.network.WebdavEntry;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.files.ServerFileInterface;
import com.owncloud.android.utils.MimeType;
import java.io.File;
import third_parties.daveKoeller.AlphanumComparator;
public class OCFile implements Parcelable, Comparable<OCFile> {
public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterface {
public static final Parcelable.Creator<OCFile> CREATOR = new Parcelable.Creator<OCFile>() {
@ -466,7 +467,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
Log_OC.d(TAG, "OCFile name changing from " + mRemotePath);
if (name != null && name.length() > 0 && !name.contains(PATH_SEPARATOR) &&
!mRemotePath.equals(ROOT_PATH)) {
String parent = (new File(getRemotePath())).getParent();
String parent = (new File(this.getRemotePath())).getParent();
parent = (parent.endsWith(PATH_SEPARATOR)) ? parent : parent + PATH_SEPARATOR;
mRemotePath = parent + name;
if (isFolder()) {
@ -485,11 +486,12 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
}
/**
* Can be used to get the Mimetype
* Can be used to get the MimeType
*
* @return the Mimetype as a String
* @return the MimeType as a String
*/
public String getMimetype() {
@Override
public String getMimeType() {
return mMimeType;
}
@ -585,7 +587,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
* @return remote path
*/
public String getParentRemotePath() {
String parentPath = new File(getRemotePath()).getParent();
String parentPath = new File(this.getRemotePath()).getParent();
return (parentPath.endsWith("/")) ? parentPath : (parentPath + "/");
}

View File

@ -52,6 +52,8 @@ import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.files.ServerFileInterface;
import com.owncloud.android.lib.resources.files.TrashbinFile;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import com.owncloud.android.ui.TextDrawable;
import com.owncloud.android.ui.adapter.DiskLruImageCache;
@ -272,7 +274,7 @@ public class ThumbnailsCacheManager {
if (bitmap != null) {
// Handle PNG
if (file.getMimetype().equalsIgnoreCase(PNG_MIMETYPE)) {
if (file.getMimeType().equalsIgnoreCase(PNG_MIMETYPE)) {
bitmap = handlePNG(bitmap, pxW, pxH);
}
@ -304,7 +306,7 @@ public class ThumbnailsCacheManager {
}
// Handle PNG
if (thumbnail != null && file.getMimetype().equalsIgnoreCase(PNG_MIMETYPE)) {
if (thumbnail != null && file.getMimeType().equalsIgnoreCase(PNG_MIMETYPE)) {
thumbnail = handlePNG(thumbnail, thumbnail.getWidth(), thumbnail.getHeight());
}
@ -446,10 +448,10 @@ public class ThumbnailsCacheManager {
mFile = object.getFile();
mImageKey = object.getImageKey();
if (mFile instanceof OCFile) {
if (mFile instanceof ServerFileInterface) {
thumbnail = doThumbnailFromOCFileInBackground();
if (MimeTypeUtil.isVideo((OCFile) mFile) && thumbnail != null) {
if (MimeTypeUtil.isVideo((ServerFileInterface) mFile) && thumbnail != null) {
thumbnail = addVideoOverlay(thumbnail);
}
} else if (mFile instanceof File) {
@ -484,6 +486,8 @@ public class ThumbnailsCacheManager {
tagId = String.valueOf(((OCFile)mFile).getFileId());
} else if (mFile instanceof File) {
tagId = String.valueOf(mFile.hashCode());
} else if (mFile instanceof TrashbinFile) {
tagId = String.valueOf(((TrashbinFile) mFile).getRemoteId());
}
if (String.valueOf(imageView.getTag()).equals(tagId)) {
imageView.setImageBitmap(bitmap);
@ -498,40 +502,44 @@ public class ThumbnailsCacheManager {
private Bitmap doThumbnailFromOCFileInBackground() {
Bitmap thumbnail;
OCFile file = (OCFile) mFile;
ServerFileInterface file = (ServerFileInterface) mFile;
String imageKey = PREFIX_THUMBNAIL + String.valueOf(file.getRemoteId());
// Check disk cache in background thread
thumbnail = getBitmapFromDiskCache(imageKey);
// Not found in disk cache
if (thumbnail == null || file.needsUpdateThumbnail()) {
if (thumbnail == null || (file instanceof OCFile && ((OCFile) file).needsUpdateThumbnail())) {
int pxW;
int pxH;
pxW = pxH = getThumbnailDimension();
if (file.isDown()) {
Bitmap bitmap;
if (MimeTypeUtil.isVideo(file)) {
bitmap = ThumbnailUtils.createVideoThumbnail(file.getStoragePath(),
MediaStore.Images.Thumbnails.MINI_KIND);
} else {
bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getStoragePath(), pxW, pxH);
}
if (bitmap != null) {
// Handle PNG
if (file.getMimetype().equalsIgnoreCase(PNG_MIMETYPE)) {
bitmap = handlePNG(bitmap, pxW, pxH);
if (file instanceof OCFile) {
OCFile ocFile = (OCFile) file;
if (ocFile.isDown()) {
Bitmap bitmap;
if (MimeTypeUtil.isVideo(ocFile)) {
bitmap = ThumbnailUtils.createVideoThumbnail(ocFile.getStoragePath(),
MediaStore.Images.Thumbnails.MINI_KIND);
} else {
bitmap = BitmapUtils.decodeSampledBitmapFromFile(ocFile.getStoragePath(), pxW, pxH);
}
thumbnail = addThumbnailToCache(imageKey, bitmap, file.getStoragePath(), pxW, pxH);
if (bitmap != null) {
// Handle PNG
if (ocFile.getMimeType().equalsIgnoreCase(PNG_MIMETYPE)) {
bitmap = handlePNG(bitmap, pxW, pxH);
}
file.setNeedsUpdateThumbnail(false);
mStorageManager.saveFile(file);
thumbnail = addThumbnailToCache(imageKey, bitmap, ocFile.getStoragePath(), pxW, pxH);
ocFile.setNeedsUpdateThumbnail(false);
mStorageManager.saveFile(ocFile);
}
}
}
} else {
if (thumbnail == null) {
// check if resized version is available
String resizedImageKey = PREFIX_RESIZED_IMAGE + String.valueOf(file.getRemoteId());
Bitmap resizedImage = getBitmapFromDiskCache(resizedImageKey);
@ -544,8 +552,15 @@ public class ThumbnailsCacheManager {
getMethod = null;
try {
// thumbnail
String uri = mClient.getBaseUri() + "/index.php/apps/files/api/v1/thumbnail/" +
pxW + "/" + pxH + Uri.encode(file.getRemotePath(), "/");
String uri;
if (file instanceof OCFile) {
uri = mClient.getBaseUri() + "/index.php/apps/files/api/v1/thumbnail/" +
pxW + "/" + pxH + Uri.encode(file.getRemotePath(), "/");
} else {
uri = mClient.getBaseUri() + "/index.php/apps/files_trashbin/ajax/preview.php?x="
+ pxW + "&y=" + pxH + "&file=" + Uri.encode(file.getRemotePath());
}
Log_OC.d(TAG, "generate thumbnail: " + file.getFileName() +
" URI: " + uri);
getMethod = new GetMethod(uri);
@ -565,7 +580,7 @@ public class ThumbnailsCacheManager {
}
// Handle PNG
if (file.getMimetype().equalsIgnoreCase(PNG_MIMETYPE)) {
if (file.getMimeType().equalsIgnoreCase(PNG_MIMETYPE)) {
thumbnail = handlePNG(thumbnail, pxW, pxH);
}
} catch (Exception e) {
@ -1154,7 +1169,7 @@ public class ThumbnailsCacheManager {
if (bitmap != null) {
// Handle PNG
if (file.getMimetype().equalsIgnoreCase(PNG_MIMETYPE)) {
if (file.getMimeType().equalsIgnoreCase(PNG_MIMETYPE)) {
bitmap = handlePNG(bitmap, pxW, pxH);
}

View File

@ -121,7 +121,7 @@ public class DownloadFileOperation extends RemoteOperation {
}
public String getMimeType() {
String mimeType = mFile.getMimetype();
String mimeType = mFile.getMimeType();
if (mimeType == null || mimeType.length() <= 0) {
try {
mimeType = MimeTypeMap.getSingleton()

View File

@ -0,0 +1,83 @@
/*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2018 Tobias Kaminsky
* Copyright (C) 2018 Nextcloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 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 <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.operations;
import android.util.Log;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.operations.common.SyncOperation;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
import java.io.IOException;
/**
* Empty trashbin.
*/
public class EmptyTrashbinFileOperation extends SyncOperation {
private static final String TAG = EmptyTrashbinFileOperation.class.getSimpleName();
private static final int RESTORE_READ_TIMEOUT = 30000;
private static final int RESTORE_CONNECTION_TIMEOUT = 5000;
private String userId;
/**
* Constructor
*
* @param userId to access correct trashbin
*/
public EmptyTrashbinFileOperation(String userId) {
this.userId = userId;
}
/**
* Performs the operation.
*
* @param client Client object to communicate with the remote Nextcloud server.
*/
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result;
try {
DeleteMethod delete = new DeleteMethod(client.getNewWebdavUri(false) + "/trashbin/" + userId + "/trash");
int status = client.executeMethod(delete, RESTORE_READ_TIMEOUT, RESTORE_CONNECTION_TIMEOUT);
result = new RemoteOperationResult(isSuccess(status), delete);
client.exhaustResponse(delete.getResponseBodyAsStream());
} catch (IOException e) {
result = new RemoteOperationResult(e);
Log.e(TAG, "Empty trashbin failed: " + result.getLogMessage(), e);
}
return result;
}
private boolean isSuccess(int status) {
return status == HttpStatus.SC_NO_CONTENT;
}
}

View File

@ -0,0 +1,93 @@
/*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2018 Tobias Kaminsky
* Copyright (C) 2018 Nextcloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 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 <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.operations;
import android.util.Log;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.operations.common.SyncOperation;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.jackrabbit.webdav.client.methods.MoveMethod;
import java.io.IOException;
/**
* Restore a {@link com.owncloud.android.lib.resources.files.TrashbinFile}.
*/
public class RestoreTrashbinFileOperation extends SyncOperation {
private static final String TAG = RestoreTrashbinFileOperation.class.getSimpleName();
private static final int RESTORE_READ_TIMEOUT = 30000;
private static final int RESTORE_CONNECTION_TIMEOUT = 5000;
private String sourcePath;
private String fileName;
private String userId;
/**
* Constructor
*
* @param sourcePath Remote path of the {@link com.owncloud.android.lib.resources.files.TrashbinFile} to restore
* @param fileName original filename
* @param userId userId to access correct trashbin
*/
public RestoreTrashbinFileOperation(String sourcePath, String fileName, String userId) {
this.sourcePath = sourcePath;
this.fileName = fileName;
this.userId = userId;
}
/**
* Performs the operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result;
try {
String source = client.getNewWebdavUri(false) + WebdavUtils.encodePath(sourcePath);
String target = client.getNewWebdavUri(false) + "/trashbin/" + userId + "/restore/" + fileName;
MoveMethod move = new MoveMethod(source, target, true);
int status = client.executeMethod(move, RESTORE_READ_TIMEOUT, RESTORE_CONNECTION_TIMEOUT);
result = new RemoteOperationResult(isSuccess(status), move);
client.exhaustResponse(move.getResponseBodyAsStream());
} catch (IOException e) {
result = new RemoteOperationResult(e);
Log.e(TAG, "Restore trashbin file " + sourcePath + " failed: " + result.getLogMessage(), e);
}
return result;
}
private boolean isSuccess(int status) {
return status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT;
}
}

View File

@ -268,7 +268,7 @@ public class UploadFileOperation extends SyncOperation {
}
public String getMimeType() {
return mFile.getMimetype();
return mFile.getMimeType();
}
public int getLocalBehaviour() {
@ -581,11 +581,11 @@ public class UploadFileOperation extends SyncOperation {
/// perform the upload
if (mChunked && (size > ChunkedUploadRemoteFileOperation.CHUNK_SIZE)) {
mUploadOperation = new ChunkedUploadRemoteFileOperation(mContext, encryptedTempFile.getAbsolutePath(),
mFile.getParentRemotePath() + encryptedFileName, mFile.getMimetype(),
mFile.getParentRemotePath() + encryptedFileName, mFile.getMimeType(),
mFile.getEtagInConflict(), timeStamp);
} else {
mUploadOperation = new UploadRemoteFileOperation(encryptedTempFile.getAbsolutePath(),
mFile.getParentRemotePath() + encryptedFileName, mFile.getMimetype(),
mFile.getParentRemotePath() + encryptedFileName, mFile.getMimeType(),
mFile.getEtagInConflict(), timeStamp);
}
@ -611,7 +611,7 @@ public class UploadFileOperation extends SyncOperation {
DecryptedFolderMetadata.DecryptedFile decryptedFile = new DecryptedFolderMetadata.DecryptedFile();
DecryptedFolderMetadata.Data data = new DecryptedFolderMetadata.Data();
data.setFilename(mFile.getFileName());
data.setMimetype(mFile.getMimetype());
data.setMimetype(mFile.getMimeType());
data.setKey(EncryptionUtils.encodeBytesToBase64String(key));
decryptedFile.setEncrypted(data);
@ -822,10 +822,10 @@ public class UploadFileOperation extends SyncOperation {
// perform the upload
if (mChunked && (size > ChunkedUploadRemoteFileOperation.CHUNK_SIZE)) {
mUploadOperation = new ChunkedUploadRemoteFileOperation(mContext, mFile.getStoragePath(),
mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict(), timeStamp);
mFile.getRemotePath(), mFile.getMimeType(), mFile.getEtagInConflict(), timeStamp);
} else {
mUploadOperation = new UploadRemoteFileOperation(mFile.getStoragePath(),
mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict(), timeStamp);
mFile.getRemotePath(), mFile.getMimeType(), mFile.getEtagInConflict(), timeStamp);
}
Iterator<OnDatatransferProgressListener> listener = mDataTransferListeners.iterator();
@ -1055,7 +1055,7 @@ public class UploadFileOperation extends SyncOperation {
OCFile newFile = new OCFile(newRemotePath);
newFile.setCreationTimestamp(mFile.getCreationTimestamp());
newFile.setFileLength(mFile.getFileLength());
newFile.setMimetype(mFile.getMimetype());
newFile.setMimetype(mFile.getMimeType());
newFile.setModificationTimestamp(mFile.getModificationTimestamp());
newFile.setModificationTimestampAtLastSyncForData(
mFile.getModificationTimestampAtLastSyncForData()

View File

@ -105,7 +105,7 @@ public class DiskLruImageCacheFileProvider extends ContentProvider {
@Override
public String getType(@NonNull Uri uri) {
OCFile ocFile = getFile(uri);
return ocFile.getMimetype();
return ocFile.getMimeType();
}
@Override

View File

@ -63,6 +63,7 @@ import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.authentication.PassCodeManager;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ExternalLinksProvider;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.ExternalLink;
import com.owncloud.android.lib.common.ExternalLinkType;
@ -86,6 +87,7 @@ import com.owncloud.android.ui.events.ChangeMenuEvent;
import com.owncloud.android.ui.events.DummyDrawerEvent;
import com.owncloud.android.ui.events.MenuItemClickEvent;
import com.owncloud.android.ui.events.SearchEvent;
import com.owncloud.android.ui.trashbin.TrashbinActivity;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.FilesSyncHelper;
import com.owncloud.android.utils.ThemeUtils;
@ -349,6 +351,14 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
navigationView.getMenu().removeItem(R.id.nav_videos);
}
if (getAccount() != null) {
FileDataStorageManager storageManager = new FileDataStorageManager(getAccount(), getContentResolver());
OCCapability capability = storageManager.getCapability(getAccount().name);
if (capability.getFilesUndelete().isFalse() || capability.getFilesUndelete().isUnknown()) {
navigationView.getMenu().removeItem(R.id.nav_trashbin);
}
}
if (getResources().getBoolean(R.bool.use_home) && navigationView.getMenu().findItem(R.id.nav_all_files) !=
null) {
navigationView.getMenu().findItem(R.id.nav_all_files).setTitle(getResources().
@ -445,6 +455,11 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
uploadListIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(uploadListIntent);
break;
case R.id.nav_trashbin:
Intent trashbinIntent = new Intent(getApplicationContext(), TrashbinActivity.class);
trashbinIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(trashbinIntent);
break;
case R.id.nav_activity:
Intent activityIntent = new Intent(getApplicationContext(), ActivitiesActivity.class);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

View File

@ -508,7 +508,7 @@ public class FileDisplayActivity extends HookActivity
updateActionBarTitleAndHomeButton(file);
} else {
cleanSecondFragment();
if (file.isDown() && MimeTypeUtil.isVCard(file.getMimetype())) {
if (file.isDown() && MimeTypeUtil.isVCard(file.getMimeType())) {
startContactListFragment(file);
} else if (file.isDown() && PreviewTextFragment.canBePreviewed(file)) {
startTextPreview(file, false);
@ -738,7 +738,7 @@ public class FileDisplayActivity extends HookActivity
if (PreviewMediaFragment.canBePreviewed(mWaitingToPreview)) {
startMediaPreview(mWaitingToPreview, 0, true, true);
detailsFragmentChanged = true;
} else if (MimeTypeUtil.isVCard(mWaitingToPreview.getMimetype())) {
} else if (MimeTypeUtil.isVCard(mWaitingToPreview.getMimeType())) {
startContactListFragment(mWaitingToPreview);
detailsFragmentChanged = true;
} else if (PreviewTextFragment.canBePreviewed(mWaitingToPreview)) {
@ -2235,7 +2235,7 @@ public class FileDisplayActivity extends HookActivity
private void sendDownloadedFile(String packageName, String activityName) {
if (mWaitingToSend != null) {
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType(mWaitingToSend.getMimetype());
sendIntent.setType(mWaitingToSend.getMimeType());
sendIntent.putExtra(Intent.EXTRA_STREAM, mWaitingToSend.getExposedFileUri(this));
sendIntent.putExtra(Intent.ACTION_SEND, true);

View File

@ -253,7 +253,7 @@ public class ActivityListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
.error(placeholder).into(fileIcon); // using custom fetcher
} else {
fileIcon.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimetype(), file.getFileName(), context));
fileIcon.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(), file.getFileName(), context));
}
} else {
// Folder

View File

@ -398,11 +398,11 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
}
}
if (file.getMimetype().equalsIgnoreCase("image/png")) {
if (file.getMimeType().equalsIgnoreCase("image/png")) {
thumbnailView.setBackgroundColor(mContext.getResources().getColor(R.color.background_color));
}
} else {
thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimetype(), file.getFileName(),
thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(), file.getFileName(),
mAccount, mContext));
}
}

View File

@ -0,0 +1,326 @@
/*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2018 Tobias Kaminsky
* Copyright (C) 2018 Nextcloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 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 <https://www.gnu.org/licenses/>.
*/
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;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
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.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 java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Adapter for the trashbin view
*/
public class TrashbinListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TRASHBIN_ITEM = 100;
private static final int TRASHBIN_FOOTER = 101;
private static final String TAG = TrashbinListAdapter.class.getSimpleName();
private TrashbinActivityInterface trashbinActivityInterface;
private List<TrashbinFile> files;
private Context context;
private Account account;
private FileDataStorageManager storageManager;
private ArrayList<ThumbnailsCacheManager.ThumbnailGenerationTask> asyncTasks = new ArrayList<>();
public TrashbinListAdapter(TrashbinActivityInterface trashbinActivityInterface,
FileDataStorageManager storageManager, Context context) {
this.files = new ArrayList<>();
this.trashbinActivityInterface = trashbinActivityInterface;
this.account = AccountUtils.getCurrentOwnCloudAccount(context);
this.storageManager = storageManager;
this.context = context;
}
public void setTrashbinFiles(List<Object> trashbinFiles, boolean clear) {
if (clear) {
files.clear();
}
for (Object file : trashbinFiles) {
files.add((TrashbinFile) file);
}
files = PreferenceManager.getSortOrder(context, null).sortTrashbinFiles(files);
notifyDataSetChanged();
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == TRASHBIN_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.trashbin_item, parent, false);
return new TrashbinFileViewHolder(v);
} else {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_footer, parent, false);
return new TrashbinFooterViewHolder(v);
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (holder instanceof TrashbinFileViewHolder) {
final TrashbinFileViewHolder trashbinFileViewHolder = (TrashbinFileViewHolder) holder;
TrashbinFile file = files.get(position);
// layout
trashbinFileViewHolder.itemLayout.setOnClickListener(v -> trashbinActivityInterface.onItemClicked(file));
// thumbnail
trashbinFileViewHolder.thumbnail.setTag(file.getRemoteId());
setThumbnail(file, trashbinFileViewHolder.thumbnail);
// fileName
trashbinFileViewHolder.fileName.setText(file.getFileName());
// fileSize
trashbinFileViewHolder.fileSize.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
// originalLocation
String location;
int lastIndex = file.getOriginalLocation().lastIndexOf('/');
if (lastIndex != -1) {
location = "/" + file.getOriginalLocation().substring(0, lastIndex) + "/";
} else {
location = "/";
}
trashbinFileViewHolder.originalLocation.setText(location);
// deletion time
trashbinFileViewHolder.deletionTimestamp.setText(DisplayUtils.getRelativeTimestamp(context,
file.getDeletionTimestamp() * 1000));
// checkbox
trashbinFileViewHolder.checkbox.setVisibility(View.GONE);
// overflow menu
trashbinFileViewHolder.overflowMenu.setOnClickListener(v ->
trashbinActivityInterface.onOverflowIconClicked(file, v));
// restore button
trashbinFileViewHolder.restoreButton.setOnClickListener(v ->
trashbinActivityInterface.onRestoreIconClicked(file, v));
} else {
TrashbinFooterViewHolder trashbinFooterViewHolder = (TrashbinFooterViewHolder) holder;
trashbinFooterViewHolder.title.setText(getFooterText());
}
}
public void removeFile(TrashbinFile file) {
int index = files.indexOf(file);
if (index != -1) {
files.remove(index);
notifyItemRemoved(index);
}
}
public void removeAllFiles() {
files.clear();
notifyDataSetChanged();
}
private String getFooterText() {
int filesCount = 0;
int foldersCount = 0;
int count = files.size();
TrashbinFile file;
for (int i = 0; i < count; i++) {
file = files.get(i);
if (file.isFolder()) {
foldersCount++;
} else {
if (!file.isHidden()) {
filesCount++;
}
}
}
return generateFooterText(filesCount, foldersCount);
}
private String generateFooterText(int filesCount, int foldersCount) {
String output;
Resources resources = context.getResources();
if (filesCount + foldersCount <= 0) {
output = "";
} else if (foldersCount <= 0) {
output = resources.getQuantityString(R.plurals.file_list__footer__file, filesCount, filesCount);
} else if (filesCount <= 0) {
output = resources.getQuantityString(R.plurals.file_list__footer__folder, foldersCount, foldersCount);
} else {
output = resources.getQuantityString(R.plurals.file_list__footer__file, filesCount, filesCount) + ", " +
resources.getQuantityString(R.plurals.file_list__footer__folder, foldersCount, foldersCount);
}
return output;
}
private void setThumbnail(TrashbinFile file, ImageView thumbnailView) {
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 + String.valueOf(file.getRemoteId())
);
if (thumbnail != null) {
if (MimeTypeUtil.isVideo(file)) {
Bitmap withOverlay = ThumbnailsCacheManager.addVideoOverlay(thumbnail);
thumbnailView.setImageBitmap(withOverlay);
} else {
thumbnailView.setImageBitmap(thumbnail);
}
} else {
// generate new thumbnail
if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailView)) {
try {
final ThumbnailsCacheManager.ThumbnailGenerationTask task =
new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView, storageManager,
account, asyncTasks);
final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
new ThumbnailsCacheManager.AsyncThumbnailDrawable(context.getResources(),
thumbnail, task);
thumbnailView.setImageDrawable(asyncDrawable);
asyncTasks.add(task);
task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file,
file.getRemoteId()));
} catch (IllegalArgumentException e) {
Log_OC.d(TAG, "ThumbnailGenerationTask : " + e.getMessage());
}
}
}
if (file.getMimeType().equalsIgnoreCase("image/png")) {
thumbnailView.setBackgroundColor(context.getResources().getColor(R.color.background_color));
}
} else {
thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(), file.getFileName(),
account, context));
}
}
}
@Override
public int getItemViewType(int position) {
if (position == files.size()) {
return TRASHBIN_FOOTER;
} else {
return TRASHBIN_ITEM;
}
}
@Override
public int getItemCount() {
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);
notifyDataSetChanged();
}
public class TrashbinFileViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.thumbnail)
public ImageView thumbnail;
@BindView(R.id.Filename)
public TextView fileName;
@BindView(R.id.fileSize)
public TextView fileSize;
@BindView(R.id.deletionTimestamp)
public TextView deletionTimestamp;
@BindView(R.id.originalLocation)
public TextView originalLocation;
@BindView(R.id.restore)
public ImageView restoreButton;
@BindView(R.id.customCheckbox)
public ImageView checkbox;
@BindView(R.id.overflowMenu)
public ImageView overflowMenu;
@BindView(R.id.ListItemLayout)
public LinearLayout itemLayout;
private TrashbinFileViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
// todo action mode
}
}
public class TrashbinFooterViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.footerText)
public TextView title;
private TrashbinFooterViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}

View File

@ -128,7 +128,7 @@ public class UploaderAdapter extends SimpleAdapter {
}
} else {
fileIcon.setImageDrawable(
MimeTypeUtil.getFileTypeIcon(file.getMimetype(), file.getFileName(), mAccount, mContext)
MimeTypeUtil.getFileTypeIcon(file.getMimeType(), file.getFileName(), mAccount, mContext)
);
}
}

View File

@ -223,7 +223,7 @@ public class SendShareDialog extends BottomSheetDialogFragment {
@NonNull
private Intent createSendIntent() {
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType(file.getMimetype());
sendIntent.setType(file.getMimeType());
sendIntent.putExtra(Intent.EXTRA_STREAM, file.getExposedFileUri(getActivity()));
sendIntent.putExtra(Intent.ACTION_SEND, true);
return sendIntent;

View File

@ -191,7 +191,7 @@ public class ShareFileFragment extends Fragment implements ShareUserListAdapter.
// Image
ImageView icon = view.findViewById(R.id.shareFileIcon);
icon.setImageDrawable(
MimeTypeUtil.getFileTypeIcon(mFile.getMimetype(), mFile.getFileName(), mAccount, getContext())
MimeTypeUtil.getFileTypeIcon(mFile.getMimeType(), mFile.getFileName(), mAccount, getContext())
);
if (MimeTypeUtil.isImage(mFile)) {
String remoteId = String.valueOf(mFile.getRemoteId());

View File

@ -335,6 +335,52 @@ public class FileOperationsHelper {
openFileWithIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
return openFileWithIntent;
String storagePath = file.getStoragePath();
String[] officeExtensions = MainApp.getAppContext().getResources().getStringArray(R.array
.ms_office_extensions);
Uri fileUri;
if (file.getFileName().contains(".") &&
Arrays.asList(officeExtensions).contains(file.getFileName().substring(file.getFileName().
lastIndexOf(".") + 1, file.getFileName().length())) &&
!file.getStoragePath().startsWith(MainApp.getAppContext().getFilesDir().getAbsolutePath())) {
fileUri = file.getLegacyExposedFileUri(mFileActivity);
} else {
fileUri = file.getExposedFileUri(mFileActivity);
}
Intent openFileWithIntent = null;
int lastIndexOfDot = storagePath.lastIndexOf('.');
if (lastIndexOfDot >= 0) {
String fileExt = storagePath.substring(lastIndexOfDot + 1);
String guessedMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExt);
if (guessedMimeType != null) {
openFileWithIntent = new Intent(Intent.ACTION_VIEW);
openFileWithIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
openFileWithIntent.setDataAndType(
fileUri,
guessedMimeType
);
}
}
if (openFileWithIntent == null) {
openFileWithIntent = createIntentFromFile(storagePath);
}
if (openFileWithIntent == null) {
openFileWithIntent = new Intent(Intent.ACTION_VIEW);
openFileWithIntent.setDataAndType(
fileUri,
file.getMimeType()
);
}
openFileWithIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
private Uri getFileUri(OCFile file, String[] officeExtensions) {
@ -709,7 +755,7 @@ public class FileOperationsHelper {
Context context = MainApp.getAppContext();
Intent sendIntent = new Intent(Intent.ACTION_SEND);
// set MimeType
sendIntent.setType(file.getMimetype());
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) +
@ -746,11 +792,11 @@ public class FileOperationsHelper {
file.getRemotePath());
}
intent.setDataAndType(uri, file.getMimetype());
intent.setDataAndType(uri, file.getMimeType());
mFileActivity.startActivityForResult(Intent.createChooser(intent,
mFileActivity.getString(R.string.set_as)), 200);
intent.setDataAndType(uri, file.getMimetype());
intent.setDataAndType(uri, file.getMimeType());
} catch (ActivityNotFoundException exception) {
DisplayUtils.showSnackMessage(view, R.string.picture_set_as_no_app);
}

View File

@ -0,0 +1,39 @@
/*
* Nextcloud Android client application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic
* 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.ui.interfaces;
import android.view.View;
import com.owncloud.android.lib.resources.files.TrashbinFile;
import com.owncloud.android.ui.adapter.OCFileListAdapter;
/**
* Interface for communication between {@link com.owncloud.android.ui.fragment.OCFileListFragment}
* and {@link OCFileListAdapter}
*/
public interface TrashbinActivityInterface {
void onOverflowIconClicked(TrashbinFile file, View view);
void onItemClicked(TrashbinFile file);
void onRestoreIconClicked(TrashbinFile file, View view);
}

View File

@ -467,7 +467,7 @@ public class PreviewImageFragment extends FileFragment {
int minHeight = screenSize.y;
for (int i = 0; i < maxDownScale && bitmapResult == null && drawableResult == null; i++) {
if (ocFile.getMimetype().equalsIgnoreCase(MIME_TYPE_SVG)) {
if (ocFile.getMimeType().equalsIgnoreCase(MIME_TYPE_SVG)) {
if (isCancelled()) {
return null;
}
@ -504,7 +504,7 @@ public class PreviewImageFragment extends FileFragment {
Log_OC.e(TAG, "File could not be loaded as a bitmap: " + storagePath);
break;
} else {
if (ocFile.getMimetype().equalsIgnoreCase("image/jpeg")) {
if (ocFile.getMimeType().equalsIgnoreCase("image/jpeg")) {
// Rotate image, obeying exif tag.
bitmapResult = BitmapUtils.rotateImage(bitmapResult, storagePath);
}
@ -570,9 +570,9 @@ public class PreviewImageFragment extends FileFragment {
Log_OC.d(TAG, "Showing image with resolution " + bitmap.getWidth() + "x" +
bitmap.getHeight());
if (result.ocFile.getMimetype().equalsIgnoreCase(MIME_TYPE_PNG) ||
result.ocFile.getMimetype().equalsIgnoreCase(MIME_TYPE_SVG) ||
result.ocFile.getMimetype().equalsIgnoreCase(MIME_TYPE_GIF)) {
if (result.ocFile.getMimeType().equalsIgnoreCase(MIME_TYPE_PNG) ||
result.ocFile.getMimeType().equalsIgnoreCase(MIME_TYPE_SVG) ||
result.ocFile.getMimeType().equalsIgnoreCase(MIME_TYPE_GIF)) {
if (getResources() != null) {
imageView.setImageDrawable(generateCheckerboardLayeredDrawable(result, bitmap));
} else {
@ -728,8 +728,8 @@ public class PreviewImageFragment extends FileFragment {
private void toggleImageBackground() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && getFile() != null
&& (getFile().getMimetype().equalsIgnoreCase(MIME_TYPE_PNG) ||
getFile().getMimetype().equalsIgnoreCase(MIME_TYPE_SVG)) && getActivity() != null
&& (getFile().getMimeType().equalsIgnoreCase(MIME_TYPE_PNG) ||
getFile().getMimeType().equalsIgnoreCase(MIME_TYPE_SVG)) && getActivity() != null
&& getActivity() instanceof PreviewImageActivity && getResources() != null) {
PreviewImageActivity previewImageActivity = (PreviewImageActivity) getActivity();

View File

@ -403,7 +403,7 @@ public class PreviewTextFragment extends FileFragment {
unsupportedTypes.add("text/vnd.wap.wml");
unsupportedTypes.add("text/vnd.wap.wmlscript");
return (file != null && file.isDown() && MimeTypeUtil.isText(file) &&
!unsupportedTypes.contains(file.getMimetype()) &&
!unsupportedTypes.contains(file.getMimeType()) &&
!unsupportedTypes.contains(MimeTypeUtil.getMimeTypeFromPath(file.getRemotePath()))
);
}

View File

@ -0,0 +1,217 @@
/*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2018 Tobias Kaminsky
* Copyright (C) 2018 Nextcloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 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 <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.ui.trashbin;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import com.owncloud.android.R;
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.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.files.ReadRemoteTrashbinFolderOperation;
import com.owncloud.android.lib.resources.files.RemoveTrashbinFileOperation;
import com.owncloud.android.lib.resources.files.TrashbinFile;
import com.owncloud.android.operations.EmptyTrashbinFileOperation;
import com.owncloud.android.operations.RestoreTrashbinFileOperation;
import java.util.List;
public class RemoteTrashbinRepository implements TrashbinRepository {
private static final String TAG = RemoteTrashbinRepository.class.getSimpleName();
private String userId;
private OwnCloudClient client;
public RemoteTrashbinRepository(Context context) {
AccountManager accountManager = AccountManager.get(context);
Account account = AccountUtils.getCurrentOwnCloudAccount(context);
try {
OwnCloudAccount ocAccount = new OwnCloudAccount(account, context);
client = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, context);
} catch (Exception e) {
Log_OC.e(TAG, e.getMessage());
}
userId = accountManager.getUserData(account,
com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID);
}
public void removeTrashbinFile(TrashbinFile file, OperationCallback callback) {
new RemoveTrashbinFileTask(client, file, callback).execute();
}
private static class RemoveTrashbinFileTask extends AsyncTask<Void, Void, Boolean> {
private OwnCloudClient client;
private TrashbinFile file;
private OperationCallback callback;
private RemoveTrashbinFileTask(OwnCloudClient client, TrashbinFile file, OperationCallback callback) {
this.client = client;
this.file = file;
this.callback = callback;
}
@Override
protected Boolean doInBackground(Void... voids) {
RemoveTrashbinFileOperation removeTrashbinFileOperation = new RemoveTrashbinFileOperation(
file.getFullRemotePath());
RemoteOperationResult result = removeTrashbinFileOperation.execute(client);
return result.isSuccess();
}
@Override
protected void onPostExecute(Boolean success) {
super.onPostExecute(success);
callback.onResult(success);
}
}
public void emptyTrashbin(OperationCallback callback) {
new EmptyTrashbinTask(client, userId, callback).execute();
}
private static class EmptyTrashbinTask extends AsyncTask<Void, Void, Boolean> {
private OwnCloudClient client;
private String userId;
private OperationCallback callback;
private EmptyTrashbinTask(OwnCloudClient client, String userId, OperationCallback callback) {
this.client = client;
this.userId = userId;
this.callback = callback;
}
@Override
protected Boolean doInBackground(Void... voids) {
EmptyTrashbinFileOperation emptyTrashbinFileOperation = new EmptyTrashbinFileOperation(userId);
RemoteOperationResult result = emptyTrashbinFileOperation.execute(client);
return result.isSuccess();
}
@Override
protected void onPostExecute(Boolean success) {
super.onPostExecute(success);
callback.onResult(success);
}
}
@Override
public void restoreFile(TrashbinFile file, OperationCallback callback) {
new RestoreTrashbinFileTask(file, userId, client, callback).execute();
}
private static class RestoreTrashbinFileTask extends AsyncTask<Void, Void, Boolean> {
private TrashbinFile file;
private String userId;
private OwnCloudClient client;
private TrashbinRepository.OperationCallback callback;
private RestoreTrashbinFileTask(TrashbinFile file, String userId, OwnCloudClient client,
TrashbinRepository.OperationCallback callback) {
this.file = file;
this.userId = userId;
this.client = client;
this.callback = callback;
}
@Override
protected Boolean doInBackground(Void... voids) {
RestoreTrashbinFileOperation restoreTrashbinFileOperation = new RestoreTrashbinFileOperation(
file.getFullRemotePath(), file.getFileName(), userId);
RemoteOperationResult result = restoreTrashbinFileOperation.execute(client);
return result.isSuccess();
}
@Override
protected void onPostExecute(Boolean success) {
super.onPostExecute(success);
callback.onResult(success);
}
}
@Override
public void getFolder(String remotePath, @NonNull LoadFolderCallback callback) {
new ReadRemoteTrashbinFolderTask(remotePath, userId, client, callback).execute();
}
private static class ReadRemoteTrashbinFolderTask extends AsyncTask<Void, Void, Boolean> {
private String remotePath;
private String userId;
private OwnCloudClient client;
private List<Object> trashbinFiles;
private LoadFolderCallback callback;
private ReadRemoteTrashbinFolderTask(String remotePath, String userId, OwnCloudClient client,
LoadFolderCallback callback) {
this.remotePath = remotePath;
this.userId = userId;
this.client = client;
this.callback = callback;
}
@Override
protected Boolean doInBackground(Void... voids) {
ReadRemoteTrashbinFolderOperation readRemoteTrashbinFolderOperation =
new ReadRemoteTrashbinFolderOperation(remotePath, userId);
RemoteOperationResult result = readRemoteTrashbinFolderOperation.execute(client);
if (result.isSuccess()) {
trashbinFiles = result.getData();
return true;
} else {
return false;
}
}
@Override
protected void onPostExecute(Boolean success) {
super.onPostExecute(success);
if (success) {
callback.onSuccess(trashbinFiles);
} else {
callback.onError(R.string.trashbin_loading_failed);
}
}
}
}

View File

@ -0,0 +1,280 @@
/*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2018 Tobias Kaminsky
* Copyright (C) 2018 Nextcloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 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 <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.ui.trashbin;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import com.owncloud.android.R;
import com.owncloud.android.lib.resources.files.TrashbinFile;
import com.owncloud.android.ui.EmptyRecyclerView;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.adapter.TrashbinListAdapter;
import com.owncloud.android.ui.dialog.SortingOrderDialogFragment;
import com.owncloud.android.ui.interfaces.TrashbinActivityInterface;
import com.owncloud.android.utils.FileSortOrder;
import com.owncloud.android.utils.ThemeUtils;
import java.util.List;
import butterknife.BindString;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import static com.owncloud.android.db.PreferenceManager.getSortOrder;
/**
* Presenting trashbin data, received from presenter
*/
public class TrashbinActivity extends FileActivity implements TrashbinActivityInterface,
SortingOrderDialogFragment.OnSortingOrderListener, TrashbinContract.View {
@BindView(R.id.empty_list_view_text)
public TextView emptyContentMessage;
@BindView(R.id.empty_list_view_headline)
public TextView emptyContentHeadline;
@BindView(R.id.empty_list_icon)
public ImageView emptyContentIcon;
@BindView(android.R.id.list)
public EmptyRecyclerView recyclerView;
@BindView(R.id.swipe_containing_list)
public SwipeRefreshLayout swipeListRefreshLayout;
@BindString(R.string.trashbin_empty_headline)
public String noResultsHeadline;
@BindString(R.string.trashbin_empty_message)
public String noResultsMessage;
private Unbinder unbinder;
private TrashbinListAdapter trashbinListAdapter;
private TrashbinPresenter trashbinPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
trashbinPresenter = new TrashbinPresenter(new RemoteTrashbinRepository(this), this);
setContentView(R.layout.trashbin_activity);
unbinder = ButterKnife.bind(this);
// setup toolbar
setupToolbar();
// setup drawer
setupDrawer(R.id.nav_trashbin);
ThemeUtils.setColoredTitle(getSupportActionBar(), R.string.trashbin_activity_title, this);
}
@Override
protected void onStart() {
super.onStart();
setupContent();
}
private void setupContent() {
recyclerView = findViewById(android.R.id.list);
recyclerView.setEmptyView(findViewById(R.id.empty_list_view));
findViewById(R.id.empty_list_progress).setVisibility(View.GONE);
emptyContentIcon.setImageResource(R.drawable.ic_delete);
emptyContentIcon.setVisibility(View.VISIBLE);
emptyContentHeadline.setText(noResultsHeadline);
emptyContentMessage.setText(noResultsMessage);
emptyContentMessage.setVisibility(View.VISIBLE);
trashbinListAdapter = new TrashbinListAdapter(this, getStorageManager(), this);
recyclerView.setAdapter(trashbinListAdapter);
recyclerView.setHasFixedSize(true);
recyclerView.setHasFooter(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
swipeListRefreshLayout.setOnRefreshListener(this::loadFolder);
loadFolder();
}
private void loadFolder() {
swipeListRefreshLayout.setRefreshing(true);
trashbinPresenter.loadFolder();
}
@Override
public void showFiles(boolean onDeviceOnly) {
super.showFiles(onDeviceOnly);
Intent i = new Intent(getApplicationContext(), FileDisplayActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean retval = true;
switch (item.getItemId()) {
case android.R.id.home:
if (isDrawerOpen()) {
closeDrawer();
} else if (trashbinPresenter.isRoot()) {
onBackPressed();
} else {
openDrawer();
}
break;
case R.id.action_sort: {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.addToBackStack(null);
SortingOrderDialogFragment mSortingOrderDialogFragment = SortingOrderDialogFragment.newInstance(
getSortOrder(this, null));
mSortingOrderDialogFragment.show(ft, SortingOrderDialogFragment.SORTING_ORDER_FRAGMENT);
break;
}
case R.id.action_empty_trashbin:
trashbinPresenter.emptyTrashbin();
break;
default:
retval = super.onOptionsItemSelected(item);
break;
}
return retval;
}
public void onDestroy() {
super.onDestroy();
unbinder.unbind();
}
@Override
public void onOverflowIconClicked(TrashbinFile file, View view) {
PopupMenu popup = new PopupMenu(this, view);
popup.inflate(R.menu.trashbin_actions_menu);
popup.setOnMenuItemClickListener(item -> {
trashbinPresenter.removeTrashbinFile(file);
return true;
});
popup.show();
}
@Override
public void onItemClicked(TrashbinFile file) {
if (file.isFolder()) {
trashbinPresenter.enterFolder(file.getRemotePath());
mDrawerToggle.setDrawerIndicatorEnabled(false);
Toolbar toolbar = findViewById(R.id.toolbar);
if (toolbar != null && toolbar.getNavigationIcon() != null) {
ThemeUtils.tintDrawable(toolbar.getNavigationIcon(), ThemeUtils.fontColor(this));
}
}
}
@Override
public void onRestoreIconClicked(TrashbinFile file, View view) {
trashbinPresenter.restoreTrashbinFile(file);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.trashbin_options_menu, menu);
return true;
}
@Override
protected void onPause() {
super.onPause();
trashbinListAdapter.cancelAllPendingTasks();
}
@Override
public void onBackPressed() {
trashbinPresenter.navigateUp();
}
public void close() {
super.onBackPressed();
}
public void setDrawerIndicatorEnabled(boolean bool) {
mDrawerToggle.setDrawerIndicatorEnabled(bool);
}
@Override
public void onSortingOrderChosen(FileSortOrder sortOrder) {
trashbinListAdapter.setSortOrder(sortOrder);
}
@Override
public void showTrashbinFolder(List<Object> trashbinFiles) {
trashbinListAdapter.setTrashbinFiles(trashbinFiles, true);
swipeListRefreshLayout.setRefreshing(false);
}
@Override
public void removeFile(TrashbinFile file) {
trashbinListAdapter.removeFile(file);
}
@Override
public void removeAllFiles() {
trashbinListAdapter.removeAllFiles();
}
@Override
public void showError(int message) {
swipeListRefreshLayout.setRefreshing(false);
Snackbar.make(recyclerView, getString(message), Snackbar.LENGTH_LONG).show();
}
@Override
public void showError(int message, TrashbinFile file) {
swipeListRefreshLayout.setRefreshing(false);
Snackbar.make(recyclerView, String.format(getString(message), file.getFileName()), Snackbar.LENGTH_LONG).show();
}
}

View File

@ -0,0 +1,64 @@
/*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2018 Tobias Kaminsky
* Copyright (C) 2018 Nextcloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 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 <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.ui.trashbin;
import com.owncloud.android.lib.resources.files.TrashbinFile;
import java.util.List;
/**
* Contract between view (TrashbinActivity) and presenter (TrashbinPresenter)
*/
public interface TrashbinContract {
interface View {
void showTrashbinFolder(List<Object> trashbinFiles);
void showError(int message, TrashbinFile file);
void showError(int message);
void removeFile(TrashbinFile file);
void removeAllFiles();
void close();
void setDrawerIndicatorEnabled(boolean bool);
}
interface Presenter {
boolean isRoot();
void loadFolder();
void navigateUp();
void enterFolder(String folder);
void restoreTrashbinFile(TrashbinFile file);
void removeTrashbinFile(TrashbinFile file);
void emptyTrashbin();
}
}

View File

@ -0,0 +1,114 @@
/*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2018 Tobias Kaminsky
* Copyright (C) 2018 Nextcloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 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 <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.ui.trashbin;
import com.owncloud.android.R;
import com.owncloud.android.lib.resources.files.TrashbinFile;
import java.io.File;
import java.util.List;
/**
* Coordinates between model and view: querying model, updating view, react to UI input
*/
public class TrashbinPresenter implements TrashbinContract.Presenter {
private TrashbinContract.View trashbinView;
private TrashbinRepository trashbinRepository;
private String currentPath = "/";
public TrashbinPresenter(TrashbinRepository trashbinRepository, TrashbinContract.View trashbinView) {
this.trashbinRepository = trashbinRepository;
this.trashbinView = trashbinView;
}
@Override
public void enterFolder(String folder) {
currentPath = folder;
loadFolder();
}
@Override
public boolean isRoot() {
return !"/".equals(currentPath);
}
@Override
public void navigateUp() {
if ("/".equals(currentPath)) {
trashbinView.close();
} else {
currentPath = new File(currentPath).getParent();
loadFolder();
}
trashbinView.setDrawerIndicatorEnabled("/".equals(currentPath));
}
@Override
public void loadFolder() {
trashbinRepository.getFolder(currentPath, new TrashbinRepository.LoadFolderCallback() {
@Override
public void onSuccess(List<Object> files) {
trashbinView.showTrashbinFolder(files);
}
@Override
public void onError(int error) {
trashbinView.showError(error);
}
});
}
@Override
public void restoreTrashbinFile(TrashbinFile file) {
trashbinRepository.restoreFile(file, success -> {
if (success) {
trashbinView.removeFile(file);
} else {
trashbinView.showError(R.string.trashbin_file_not_restored, file);
}
});
}
@Override
public void removeTrashbinFile(TrashbinFile file) {
trashbinRepository.removeTrashbinFile(file, success -> {
if (success) {
trashbinView.removeFile(file);
} else {
trashbinView.showError(R.string.trashbin_file_not_deleted, file);
}
});
}
@Override
public void emptyTrashbin() {
trashbinRepository.emptyTrashbin(success -> {
if (success) {
trashbinView.removeAllFiles();
} else {
trashbinView.showError(R.string.trashbin_not_emptied);
}
});
}
}

View File

@ -0,0 +1,48 @@
/*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2018 Tobias Kaminsky
* Copyright (C) 2018 Nextcloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 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 <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.ui.trashbin;
import com.owncloud.android.lib.resources.files.TrashbinFile;
import java.util.List;
/**
* Contract between presenter and model
*/
public interface TrashbinRepository {
interface LoadFolderCallback {
void onSuccess(List<Object> files);
void onError(int error);
}
interface OperationCallback {
void onResult(boolean success);
}
void getFolder(String remotePath, LoadFolderCallback callback);
void restoreFile(TrashbinFile file, OperationCallback callback);
void emptyTrashbin(OperationCallback callback);
void removeTrashbinFile(TrashbinFile file, OperationCallback callback);
}

View File

@ -310,7 +310,7 @@ public class DisplayUtils {
* calculates the relative time string based on the given modification timestamp.
*
* @param context the app's context
* @param modificationTimestamp the UNIX timestamp of the file modification time.
* @param modificationTimestamp the UNIX timestamp of the file modification time in milliseconds.
* @return a relative time string
*/
public static CharSequence getRelativeTimestamp(Context context, long modificationTimestamp) {

View File

@ -21,6 +21,7 @@
package com.owncloud.android.utils;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.resources.files.TrashbinFile;
import java.io.File;
import java.util.Collections;
@ -67,6 +68,10 @@ public class FileSortOrder {
return files;
}
public List<TrashbinFile> sortTrashbinFiles(List<TrashbinFile> files) {
return files;
}
/**
* Sorts list by Favourites.
*

View File

@ -21,6 +21,7 @@
package com.owncloud.android.utils;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.resources.files.TrashbinFile;
import java.io.File;
import java.util.Collections;
@ -49,6 +50,25 @@ public class FileSortOrderByDate extends FileSortOrder {
return super.sortCloudFiles(files);
}
/**
* Sorts list by Date.
*
* @param files list of files to sort
*/
public List<TrashbinFile> sortTrashbinFiles(List<TrashbinFile> files) {
final int multiplier = mAscending ? 1 : -1;
Collections.sort(files, new Comparator<TrashbinFile>() {
@SuppressFBWarnings(value = "Bx", justification = "Would require stepping up API level")
public int compare(TrashbinFile o1, TrashbinFile o2) {
Long obj1 = o1.getDeletionTimestamp();
return multiplier * obj1.compareTo(o2.getDeletionTimestamp());
}
});
return super.sortTrashbinFiles(files);
}
/**
* Sorts list by Date.
*

View File

@ -21,6 +21,7 @@
package com.owncloud.android.utils;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.resources.files.TrashbinFile;
import java.io.File;
import java.util.Collections;
@ -62,6 +63,31 @@ public class FileSortOrderByName extends FileSortOrder {
return super.sortCloudFiles(files);
}
/**
* Sorts list by Name.
*
* @param files files to sort
*/
@SuppressFBWarnings(value = "Bx")
public List<TrashbinFile> sortTrashbinFiles(List<TrashbinFile> files) {
final int multiplier = mAscending ? 1 : -1;
Collections.sort(files, new Comparator<TrashbinFile>() {
public int compare(TrashbinFile o1, TrashbinFile o2) {
if (o1.isFolder() && o2.isFolder()) {
return multiplier * new AlphanumComparator().compare(o1, o2);
} else if (o1.isFolder()) {
return -1;
} else if (o2.isFolder()) {
return 1;
}
return multiplier * new AlphanumComparator().compare(o1, o2);
}
});
return super.sortTrashbinFiles(files);
}
/**
* Sorts list by Name.
*

View File

@ -21,6 +21,7 @@
package com.owncloud.android.utils;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.resources.files.TrashbinFile;
import java.io.File;
import java.util.Collections;
@ -62,6 +63,35 @@ public class FileSortOrderBySize extends FileSortOrder {
return super.sortCloudFiles(files);
}
/**
* Sorts list by Size.
*
* @param files list of files to sort
*/
public List<TrashbinFile> sortTrashbinFiles(List<TrashbinFile> files) {
final int multiplier = mAscending ? 1 : -1;
Collections.sort(files, new Comparator<TrashbinFile>() {
@SuppressFBWarnings(value = "Bx")
public int compare(TrashbinFile o1, TrashbinFile o2) {
if (o1.isFolder() && o2.isFolder()) {
Long obj1 = o1.getFileLength();
return multiplier * obj1.compareTo(o2.getFileLength());
} else if (o1.isFolder()) {
return -1;
} else if (o2.isFolder()) {
return 1;
} else {
Long obj1 = o1.getFileLength();
return multiplier * obj1.compareTo(o2.getFileLength());
}
}
});
return super.sortTrashbinFiles(files);
}
/**
* Sorts list by Size.
*

View File

@ -187,7 +187,7 @@ public class FileStorageUtils {
RemoteFile file = new RemoteFile(ocFile.getRemotePath());
file.setCreationTimestamp(ocFile.getCreationTimestamp());
file.setLength(ocFile.getFileLength());
file.setMimeType(ocFile.getMimetype());
file.setMimeType(ocFile.getMimeType());
file.setModifiedTimestamp(ocFile.getModificationTimestamp());
file.setEtag(ocFile.getEtag());
file.setPermissions(ocFile.getPermissions());

View File

@ -28,6 +28,7 @@ import android.webkit.MimeTypeMap;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.network.WebdavEntry;
import com.owncloud.android.lib.resources.files.ServerFileInterface;
import java.io.File;
import java.util.ArrayList;
@ -248,7 +249,7 @@ public class MimeTypeUtil {
}
public static boolean isSVG(OCFile file) {
return "image/svg+xml".equalsIgnoreCase(file.getMimetype());
return "image/svg+xml".equalsIgnoreCase(file.getMimeType());
}
/**
@ -256,23 +257,23 @@ public class MimeTypeUtil {
* @return 'True' if the file contains audio
*/
public static boolean isAudio(OCFile file) {
return MimeTypeUtil.isAudio(file.getMimetype());
return MimeTypeUtil.isAudio(file.getMimeType());
}
/**
* @param file the file to be analyzed
* @return 'True' if the file contains video
*/
public static boolean isVideo(OCFile file) {
return MimeTypeUtil.isVideo(file.getMimetype());
public static boolean isVideo(ServerFileInterface file) {
return MimeTypeUtil.isVideo(file.getMimeType());
}
/**
* @param file the file to be analyzed
* @return 'True' if the file contains an image
*/
public static boolean isImage(OCFile file) {
return (MimeTypeUtil.isImage(file.getMimetype())
public static boolean isImage(ServerFileInterface file) {
return (MimeTypeUtil.isImage(file.getMimeType())
|| MimeTypeUtil.isImage(getMimeTypeFromPath(file.getRemotePath())));
}
@ -281,7 +282,7 @@ public class MimeTypeUtil {
* @return 'True' if the file is simple text (e.g. not application-dependent, like .doc or .docx)
*/
public static boolean isText(OCFile file) {
return (MimeTypeUtil.isText(file.getMimetype())
return (MimeTypeUtil.isText(file.getMimeType())
|| MimeTypeUtil.isText(getMimeTypeFromPath(file.getRemotePath())));
}
@ -291,7 +292,7 @@ public class MimeTypeUtil {
* @return 'True' if the file is a vcard
*/
public static boolean isVCard(OCFile file) {
return isVCard(file.getMimetype()) || isVCard(getMimeTypeFromPath(file.getRemotePath()));
return isVCard(file.getMimeType()) || isVCard(getMimeTypeFromPath(file.getRemotePath()));
}
/**

View File

@ -46,8 +46,8 @@ public class FileCursor extends MatrixCursor {
return;
}
final int iconRes = MimeTypeUtil.getFileTypeIconId(file.getMimetype(), file.getFileName());
final String mimeType = file.isFolder() ? Document.MIME_TYPE_DIR : file.getMimetype();
final int iconRes = MimeTypeUtil.getFileTypeIconId(file.getMimeType(), file.getFileName());
final String mimeType = file.isFolder() ? Document.MIME_TYPE_DIR : file.getMimeType();
final String imagePath = MimeTypeUtil.isImage(file) && file.isDown() ? file.getStoragePath() : null;
int flags = imagePath != null ? Document.FLAG_SUPPORTS_THUMBNAIL : 0;

View File

@ -24,7 +24,7 @@
package third_parties.daveKoeller;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.resources.files.ServerFileInterface;
import java.io.File;
import java.io.Serializable;
@ -86,7 +86,7 @@ public class AlphanumComparator<T> implements Comparator<T>, Serializable {
return chunk.toString();
}
public int compare(OCFile o1, OCFile o2) {
public int compare(ServerFileInterface o1, ServerFileInterface o2) {
String s1 = o1.getFileName();
String s2 = o2.getFileName();

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:fillColor="#c4c4c4"
android:pathData="M6.5,1 L6,2 L3,2 C2.446,2,2,2.446,2,3 L2,4 L14,4 L14,3 C14,2.446,13.554,2,13,2 L10,2 L9.5,1 Z M3,5 L3.875,14 C3.935,14.55,4.448,15,5,15 L11,15 C11.552,15,12.064,14.55,12.125,14 L13,5 Z"/>
</vector>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:fillColor="#6f6f6f"
android:pathData="M6.5,1 L6,2 L3,2 C2.446,2,2,2.446,2,3 L2,4 L14,4 L14,3 C14,2.446,13.554,2,13,2 L10,2 L9.5,1 Z M3,5 L3.875,14 C3.935,14.55,4.448,15,5,15 L11,15 C11.552,15,12.064,14.55,12.125,14 L13,5 Z"/>
</vector>

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Nextcloud Android client application
@author Tobias Kaminsky
Copyright (C) 2018 Tobias Kaminsky
Copyright (C) 2018 Nextcloud GmbH.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU 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 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 <https://www.gnu.org/licenses/>.
-->
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:fitsSystemWindows="true"
android:focusable="true">
<!-- The main content view -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/navigation_bar"
layout="@layout/toolbar_standard"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/bottom_navigation_view"
android:layout_below="@+id/navigation_bar">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_containing_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:footerDividersEnabled="false"
android:visibility="visible">
<com.owncloud.android.ui.EmptyRecyclerView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>
<include layout="@layout/empty_list"/>
</FrameLayout>
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="gone"
app:itemBackground="@color/primary_button_background_color"
app:itemIconTint="@color/primary_button_text_color"
app:itemTextColor="@color/primary_button_text_color"
app:menu="@menu/navigation_bar_menu"/>
</RelativeLayout>
<include
layout="@layout/drawer"
android:layout_width="@dimen/drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start"/>
</android.support.v4.widget.DrawerLayout>

View File

@ -0,0 +1,175 @@
<?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:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:orientation="horizontal">
<ImageView
android:id="@+id/thumbnail"
android:layout_width="@dimen/file_icon_size"
android:layout_height="@dimen/file_icon_size"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/standard_margin"
android:layout_marginRight="@dimen/standard_margin"
android:contentDescription="@string/thumbnail"
android:src="@drawable/folder"/>
<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/fileSize"
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/deletionTimestamp"
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>
<TextView
android:id="@+id/originalLocation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:text="@string/placeholder_filename"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:textSize="@dimen/two_line_secondary_text_size"/>
</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/restore"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:clickable="false"
android:contentDescription="@string/restore"
android:focusable="false"
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_history"/>
<ImageView
android:id="@+id/customCheckbox"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/restore"
android:layout_toRightOf="@id/restore"
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/overflowMenu"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/customCheckbox"
android:layout_toRightOf="@id/customCheckbox"
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/standard_half_padding"
android:paddingStart="@dimen/standard_half_padding"
android:src="@drawable/ic_dots_vertical"/>
</RelativeLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/list_divider_background"/>
</LinearLayout>

View File

@ -88,6 +88,11 @@
android:icon="@drawable/nav_uploads"
android:orderInCategory="2"
android:title="@string/drawer_item_uploads_list"/>
<item
android:id="@+id/nav_trashbin"
android:icon="@drawable/nav_trashbin"
android:orderInCategory="2"
android:title="@string/drawer_item_trashbin"/>
</group>
<!--

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Nextcloud Android client application
@author Tobias Kaminsky
Copyright (C) 2018 Tobias Kaminsky
Copyright (C) 2018 Nextcloud GmbH.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU 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 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 <https://www.gnu.org/licenses/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="AppCompatResource">
<item
android:id="@+id/action_delete"
android:orderInCategory="1"
android:showAsAction="never"
android:title="@string/common_remove"
app:showAsAction="never"/>
</menu>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Nextcloud Android client application
@author Tobias Kaminsky
Copyright (C) 2018 Tobias Kaminsky
Copyright (C) 2018 Nextcloud GmbH.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU 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 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 <https://www.gnu.org/licenses/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="AppCompatResource">
<item
android:id="@+id/action_sort"
android:contentDescription="@string/actionbar_sort"
android:icon="@drawable/ic_sort_variant"
android:orderInCategory="1"
android:title="@string/actionbar_sort"
app:showAsAction="never"/>
<item
android:id="@+id/action_empty_trashbin"
android:contentDescription="@string/action_empty_trashbin"
android:orderInCategory="1"
android:title="@string/action_empty_trashbin"
app:showAsAction="never"/>
</menu>

View File

@ -114,6 +114,8 @@
<string name="file_list_empty_text_photos_filter">No photos.</string>
<string name="file_list_empty_text_videos">Upload some videos or activate auto upload.</string>
<string name="file_list_empty_text_videos_filter">No videos.</string>
<string name="trashbin_empty_headline">No deleted files</string>
<string name="trashbin_empty_message">You will be able to recover deleted files from here</string>
<string name="upload_list_empty_headline">No uploads available</string>
<string name="upload_list_empty_text_auto_upload">Upload some content or activate auto upload.</string>
<string name="file_list_folder">folder</string>
@ -807,4 +809,12 @@
<string name="outdated_server">The server has reached end of life, please upgrade!</string>
<string name="dismiss">Dismiss</string>
<string name="feedback_no_mail_app">No app available to send mails!</string>
<string name="drawer_item_trashbin">Deleted files</string>
<string name="trashbin_activity_title">Deleted files</string>
<string name="restore_deleted_file">Restore deleted file</string>
<string name="action_empty_trashbin">Empty trashbin</string>
<string name="trashbin_loading_failed">Loading trashbin failed!</string>
<string name="trashbin_file_not_deleted">File %1$s could not be deleted!</string>
<string name="trashbin_file_not_restored">File %1$s could not be restored!</string>
<string name="trashbin_not_emptied">Trashbin could not emptied!</string>
</resources>