Browse Source

Add trashbin support

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
tobiasKaminsky 6 years ago
parent
commit
f8801115f2
46 changed files with 1936 additions and 81 deletions
  1. 4 0
      drawable_resources/history.svg
  2. 53 0
      drawable_resources/ic_delete.svg
  3. 1 1
      src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java
  4. 3 0
      src/main/AndroidManifest.xml
  5. 4 4
      src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java
  6. 8 6
      src/main/java/com/owncloud/android/datamodel/OCFile.java
  7. 42 27
      src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java
  8. 1 1
      src/main/java/com/owncloud/android/operations/DownloadFileOperation.java
  9. 83 0
      src/main/java/com/owncloud/android/operations/EmptyTrashbinFileOperation.java
  10. 93 0
      src/main/java/com/owncloud/android/operations/RestoreTrashbinFileOperation.java
  11. 7 7
      src/main/java/com/owncloud/android/operations/UploadFileOperation.java
  12. 1 1
      src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java
  13. 15 0
      src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java
  14. 3 3
      src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java
  15. 1 1
      src/main/java/com/owncloud/android/ui/adapter/ActivityListAdapter.java
  16. 2 2
      src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
  17. 326 0
      src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java
  18. 1 1
      src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java
  19. 1 1
      src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java
  20. 1 1
      src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java
  21. 49 3
      src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
  22. 39 0
      src/main/java/com/owncloud/android/ui/interfaces/TrashbinActivityInterface.java
  23. 7 7
      src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java
  24. 1 1
      src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java
  25. 217 0
      src/main/java/com/owncloud/android/ui/trashbin/RemoteTrashbinRepository.java
  26. 280 0
      src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java
  27. 64 0
      src/main/java/com/owncloud/android/ui/trashbin/TrashbinContract.java
  28. 114 0
      src/main/java/com/owncloud/android/ui/trashbin/TrashbinPresenter.java
  29. 48 0
      src/main/java/com/owncloud/android/ui/trashbin/TrashbinRepository.java
  30. 1 1
      src/main/java/com/owncloud/android/utils/DisplayUtils.java
  31. 5 0
      src/main/java/com/owncloud/android/utils/FileSortOrder.java
  32. 20 0
      src/main/java/com/owncloud/android/utils/FileSortOrderByDate.java
  33. 26 0
      src/main/java/com/owncloud/android/utils/FileSortOrderByName.java
  34. 30 0
      src/main/java/com/owncloud/android/utils/FileSortOrderBySize.java
  35. 1 1
      src/main/java/com/owncloud/android/utils/FileStorageUtils.java
  36. 9 8
      src/main/java/com/owncloud/android/utils/MimeTypeUtil.java
  37. 2 2
      src/main/java/org/nextcloud/providers/cursors/FileCursor.java
  38. 2 2
      src/main/java/third_parties/daveKoeller/AlphanumComparator.java
  39. 11 0
      src/main/res/drawable/ic_delete.xml
  40. 11 0
      src/main/res/drawable/nav_trashbin.xml
  41. 84 0
      src/main/res/layout/trashbin_activity.xml
  42. 175 0
      src/main/res/layout/trashbin_item.xml
  43. 5 0
      src/main/res/menu/drawer_menu.xml
  44. 34 0
      src/main/res/menu/trashbin_actions_menu.xml
  45. 41 0
      src/main/res/menu/trashbin_options_menu.xml
  46. 10 0
      src/main/res/values/strings.xml

+ 4 - 0
drawable_resources/history.svg

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

+ 53 - 0
drawable_resources/ic_delete.svg

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

+ 1 - 1
src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java

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

+ 3 - 0
src/main/AndroidManifest.xml

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

+ 4 - 4
src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java

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

+ 8 - 6
src/main/java/com/owncloud/android/datamodel/OCFile.java

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

+ 42 - 27
src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java

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

+ 1 - 1
src/main/java/com/owncloud/android/operations/DownloadFileOperation.java

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

+ 83 - 0
src/main/java/com/owncloud/android/operations/EmptyTrashbinFileOperation.java

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

+ 93 - 0
src/main/java/com/owncloud/android/operations/RestoreTrashbinFileOperation.java

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

+ 7 - 7
src/main/java/com/owncloud/android/operations/UploadFileOperation.java

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

+ 1 - 1
src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java

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

+ 15 - 0
src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java

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

+ 3 - 3
src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java

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

+ 1 - 1
src/main/java/com/owncloud/android/ui/adapter/ActivityListAdapter.java

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

+ 2 - 2
src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java

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

+ 326 - 0
src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java

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

+ 1 - 1
src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java

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

+ 1 - 1
src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java

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

+ 1 - 1
src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java

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

+ 49 - 3
src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java

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

+ 39 - 0
src/main/java/com/owncloud/android/ui/interfaces/TrashbinActivityInterface.java

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

+ 7 - 7
src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java

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

+ 1 - 1
src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java

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

+ 217 - 0
src/main/java/com/owncloud/android/ui/trashbin/RemoteTrashbinRepository.java

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

+ 280 - 0
src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java

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

+ 64 - 0
src/main/java/com/owncloud/android/ui/trashbin/TrashbinContract.java

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

+ 114 - 0
src/main/java/com/owncloud/android/ui/trashbin/TrashbinPresenter.java

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

+ 48 - 0
src/main/java/com/owncloud/android/ui/trashbin/TrashbinRepository.java

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

+ 1 - 1
src/main/java/com/owncloud/android/utils/DisplayUtils.java

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

+ 5 - 0
src/main/java/com/owncloud/android/utils/FileSortOrder.java

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

+ 20 - 0
src/main/java/com/owncloud/android/utils/FileSortOrderByDate.java

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

+ 26 - 0
src/main/java/com/owncloud/android/utils/FileSortOrderByName.java

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

+ 30 - 0
src/main/java/com/owncloud/android/utils/FileSortOrderBySize.java

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

+ 1 - 1
src/main/java/com/owncloud/android/utils/FileStorageUtils.java

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

+ 9 - 8
src/main/java/com/owncloud/android/utils/MimeTypeUtil.java

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

+ 2 - 2
src/main/java/org/nextcloud/providers/cursors/FileCursor.java

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

+ 2 - 2
src/main/java/third_parties/daveKoeller/AlphanumComparator.java

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

+ 11 - 0
src/main/res/drawable/ic_delete.xml

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

+ 11 - 0
src/main/res/drawable/nav_trashbin.xml

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

+ 84 - 0
src/main/res/layout/trashbin_activity.xml

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

+ 175 - 0
src/main/res/layout/trashbin_item.xml

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

+ 5 - 0
src/main/res/menu/drawer_menu.xml

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

+ 34 - 0
src/main/res/menu/trashbin_actions_menu.xml

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

+ 41 - 0
src/main/res/menu/trashbin_options_menu.xml

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

+ 10 - 0
src/main/res/values/strings.xml

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