Browse Source

Replace ListView with RecyclerView

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
tobiasKaminsky 7 years ago
parent
commit
6d7cc6b869
83 changed files with 3485 additions and 2353 deletions
  1. 1 0
      build.gradle
  2. 0 183
      src/main/java/com/afollestad/sectionedrecyclerview/SectionedRecyclerViewAdapter.java
  3. 25 28
      src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java
  4. 1 1
      src/main/java/com/owncloud/android/datamodel/OCFile.java
  5. 1 2
      src/main/java/com/owncloud/android/jobs/ContactsBackupJob.java
  6. 100 0
      src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java
  7. 3 3
      src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java
  8. 134 120
      src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java
  9. 778 778
      src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java
  10. 406 211
      src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java
  11. 813 0
      src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
  12. 9 4
      src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java
  13. 672 0
      src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java
  14. 2 2
      src/main/java/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java
  15. 0 89
      src/main/java/com/owncloud/android/ui/fragment/ExpandableListFragment.java
  16. 72 210
      src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java
  17. 83 107
      src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java
  18. 177 178
      src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
  19. 0 163
      src/main/java/com/owncloud/android/ui/fragment/UploadListFragment.java
  20. 8 7
      src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java
  21. 36 0
      src/main/java/com/owncloud/android/ui/interfaces/LocalFileListFragmentInterface.java
  22. 7 4
      src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java
  23. 3 3
      src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java
  24. 11 14
      src/main/java/com/owncloud/android/utils/FileSortOrder.java
  25. 1 2
      src/main/java/com/owncloud/android/utils/FileSortOrderByDate.java
  26. 1 2
      src/main/java/com/owncloud/android/utils/FileSortOrderByName.java
  27. 1 2
      src/main/java/com/owncloud/android/utils/FileSortOrderBySize.java
  28. 5 8
      src/main/java/com/owncloud/android/utils/FileStorageUtils.java
  29. 0 96
      src/main/res/layout/grid_image.xml
  30. 28 65
      src/main/res/layout/list_fragment.xml
  31. 16 0
      src/main/res/layout/upload_list_header.xml
  32. 7 10
      src/main/res/layout/upload_list_item.xml
  33. 34 10
      src/main/res/layout/upload_list_layout.xml
  34. 1 1
      src/main/res/values-ast/strings.xml
  35. 1 1
      src/main/res/values-b+en+001/strings.xml
  36. 1 1
      src/main/res/values-b+es+419/strings.xml
  37. 1 1
      src/main/res/values-bg-rBG/strings.xml
  38. 1 1
      src/main/res/values-cs-rCZ/strings.xml
  39. 1 1
      src/main/res/values-de-rDE/strings.xml
  40. 1 1
      src/main/res/values-de/strings.xml
  41. 1 1
      src/main/res/values-el/strings.xml
  42. 1 1
      src/main/res/values-es-rAR/strings.xml
  43. 1 1
      src/main/res/values-es-rCL/strings.xml
  44. 1 1
      src/main/res/values-es-rCO/strings.xml
  45. 1 1
      src/main/res/values-es-rCR/strings.xml
  46. 1 1
      src/main/res/values-es-rDO/strings.xml
  47. 1 1
      src/main/res/values-es-rEC/strings.xml
  48. 1 1
      src/main/res/values-es-rGT/strings.xml
  49. 1 1
      src/main/res/values-es-rHN/strings.xml
  50. 1 1
      src/main/res/values-es-rMX/strings.xml
  51. 1 1
      src/main/res/values-es-rNI/strings.xml
  52. 1 1
      src/main/res/values-es-rPA/strings.xml
  53. 1 1
      src/main/res/values-es-rPE/strings.xml
  54. 1 1
      src/main/res/values-es-rPR/strings.xml
  55. 1 1
      src/main/res/values-es-rPY/strings.xml
  56. 1 1
      src/main/res/values-es-rSV/strings.xml
  57. 1 1
      src/main/res/values-es-rUY/strings.xml
  58. 1 1
      src/main/res/values-es/strings.xml
  59. 1 1
      src/main/res/values-eu/strings.xml
  60. 1 1
      src/main/res/values-fi-rFI/strings.xml
  61. 1 1
      src/main/res/values-fr/strings.xml
  62. 1 1
      src/main/res/values-hu-rHU/strings.xml
  63. 1 1
      src/main/res/values-id/strings.xml
  64. 1 1
      src/main/res/values-is/strings.xml
  65. 1 1
      src/main/res/values-it/strings.xml
  66. 1 1
      src/main/res/values-ka-rGE/strings.xml
  67. 1 1
      src/main/res/values-ko/strings.xml
  68. 1 1
      src/main/res/values-lt-rLT/strings.xml
  69. 1 1
      src/main/res/values-nb-rNO/strings.xml
  70. 1 1
      src/main/res/values-nl/strings.xml
  71. 1 1
      src/main/res/values-pl/strings.xml
  72. 1 1
      src/main/res/values-pt-rBR/strings.xml
  73. 1 1
      src/main/res/values-ru/strings.xml
  74. 1 1
      src/main/res/values-sl/strings.xml
  75. 1 1
      src/main/res/values-sq/strings.xml
  76. 1 1
      src/main/res/values-sr/strings.xml
  77. 1 1
      src/main/res/values-sv/strings.xml
  78. 1 1
      src/main/res/values-tr/strings.xml
  79. 1 1
      src/main/res/values-zh-rCN/strings.xml
  80. 1 1
      src/main/res/values-zh-rTW/strings.xml
  81. 0 2
      src/main/res/values/dims.xml
  82. 1 1
      src/main/res/values/setup.xml
  83. 2 1
      src/main/res/values/strings.xml

+ 1 - 0
build.gradle

@@ -217,6 +217,7 @@ dependencies {
     implementation 'org.lukhnos:nnio:0.2'
     implementation 'com.madgag.spongycastle:pkix:1.54.0.0'
     implementation 'com.google.code.gson:gson:2.8.2'
+    implementation 'com.afollestad:sectioned-recyclerview:0.5.0'
 
     // uncomment for gplay
     // implementation "com.google.firebase:firebase-messaging:${googleLibraryVersion}"

+ 0 - 183
src/main/java/com/afollestad/sectionedrecyclerview/SectionedRecyclerViewAdapter.java

@@ -1,183 +0,0 @@
-/**
- * Copyright 2016 Aidan Follestad
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific language
- * governing permissions and limitations under the License.
- */
-package com.afollestad.sectionedrecyclerview;
-
-import android.support.annotation.IntRange;
-import android.support.annotation.Nullable;
-import android.support.v4.util.ArrayMap;
-import android.support.v7.widget.GridLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.StaggeredGridLayoutManager;
-import android.view.ViewGroup;
-
-import java.util.List;
-
-/**
- * @author Aidan Follestad (afollestad)
- */
-public abstract class SectionedRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
-
-    protected final static int VIEW_TYPE_HEADER = -2;
-    protected final static int VIEW_TYPE_ITEM = -1;
-
-    private final ArrayMap<Integer, Integer> mHeaderLocationMap;
-    private GridLayoutManager mLayoutManager;
-    private boolean mShowHeadersForEmptySections;
-
-    public SectionedRecyclerViewAdapter() {
-        mHeaderLocationMap = new ArrayMap<>();
-    }
-
-    public abstract int getSectionCount();
-
-    public abstract int getItemCount(int section);
-
-    public abstract void onBindHeaderViewHolder(VH holder, int section);
-
-    public abstract void onBindViewHolder(VH holder, int section, int relativePosition, int absolutePosition);
-
-    public final boolean isHeader(int position) {
-        return mHeaderLocationMap.get(position) != null;
-    }
-
-    /**
-     * Instructs the list view adapter to whether show headers for empty sections or not.
-     *
-     * @param show flag indicating whether headers for empty sections ought to be shown.
-     */
-    public final void shouldShowHeadersForEmptySections(boolean show) {
-        mShowHeadersForEmptySections = show;
-    }
-
-    public final void setLayoutManager(@Nullable GridLayoutManager lm) {
-        mLayoutManager = lm;
-        if (lm == null) return;
-        lm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
-            @Override
-            public int getSpanSize(int position) {
-                if (isHeader(position))
-                    return mLayoutManager.getSpanCount();
-                final int[] sectionAndPos = getSectionIndexAndRelativePosition(position);
-                final int absPos = position - (sectionAndPos[0] + 1);
-                return getRowSpan(mLayoutManager.getSpanCount(),
-                        sectionAndPos[0], sectionAndPos[1], absPos);
-            }
-        });
-    }
-
-    @SuppressWarnings("UnusedParameters")
-    protected int getRowSpan(int fullSpanSize, int section, int relativePosition, int absolutePosition) {
-        return 1;
-    }
-
-    // returns section along with offsetted position
-    private int[] getSectionIndexAndRelativePosition(int itemPosition) {
-        synchronized (mHeaderLocationMap) {
-            Integer lastSectionIndex = -1;
-            for (final Integer sectionIndex : mHeaderLocationMap.keySet()) {
-                if (itemPosition > sectionIndex) {
-                    lastSectionIndex = sectionIndex;
-                } else {
-                    break;
-                }
-            }
-            return new int[]{mHeaderLocationMap.get(lastSectionIndex), itemPosition - lastSectionIndex - 1};
-        }
-    }
-
-    @Override
-    public final int getItemCount() {
-        int count = 0;
-        mHeaderLocationMap.clear();
-        for (int s = 0; s < getSectionCount(); s++) {
-            int itemCount = getItemCount(s);
-            if (mShowHeadersForEmptySections || (itemCount > 0)) {
-                mHeaderLocationMap.put(count, s);
-                count += itemCount + 1;
-            }
-        }
-        return count;
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    @Override
-    @Deprecated
-    public final int getItemViewType(int position) {
-        if (isHeader(position)) {
-            return getHeaderViewType(mHeaderLocationMap.get(position));
-        } else {
-            final int[] sectionAndPos = getSectionIndexAndRelativePosition(position);
-            return getItemViewType(sectionAndPos[0],
-                    // offset section view positions
-                    sectionAndPos[1],
-                    position - (sectionAndPos[0] + 1));
-        }
-    }
-
-    @SuppressWarnings("UnusedParameters")
-    @IntRange(from = 0, to = Integer.MAX_VALUE)
-    public int getHeaderViewType(int section) {
-        //noinspection ResourceType
-        return VIEW_TYPE_HEADER;
-    }
-
-    @SuppressWarnings("UnusedParameters")
-    @IntRange(from = 0, to = Integer.MAX_VALUE)
-    public int getItemViewType(int section, int relativePosition, int absolutePosition) {
-        //noinspection ResourceType
-        return VIEW_TYPE_ITEM;
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    @Override
-    @Deprecated
-    public final void onBindViewHolder(VH holder, int position) {
-        StaggeredGridLayoutManager.LayoutParams layoutParams = null;
-        if (holder.itemView.getLayoutParams() instanceof GridLayoutManager.LayoutParams)
-            layoutParams = new StaggeredGridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-        else if (holder.itemView.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams)
-            layoutParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
-        if (isHeader(position)) {
-            if (layoutParams != null) layoutParams.setFullSpan(true);
-            onBindHeaderViewHolder(holder, mHeaderLocationMap.get(position));
-        } else {
-            if (layoutParams != null) layoutParams.setFullSpan(false);
-            final int[] sectionAndPos = getSectionIndexAndRelativePosition(position);
-            final int absPos = position - (sectionAndPos[0] + 1);
-            onBindViewHolder(holder, sectionAndPos[0],
-                    // offset section view positions
-                    sectionAndPos[1], absPos);
-        }
-        if (layoutParams != null)
-            holder.itemView.setLayoutParams(layoutParams);
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    @Deprecated
-    @Override
-    public final void onBindViewHolder(VH holder, int position, List<Object> payloads) {
-        super.onBindViewHolder(holder, position, payloads);
-    }
-}

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

@@ -60,7 +60,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Set;
-import java.util.Vector;
 
 public class FileDataStorageManager {
 
@@ -151,26 +150,25 @@ public class FileDataStorageManager {
     }
 
 
-    public Vector<OCFile> getFolderContent(OCFile f, boolean onlyOnDevice) {
+    public ArrayList<OCFile> getFolderContent(OCFile f, boolean onlyOnDevice) {
         if (f != null && f.isFolder() && f.getFileId() != -1) {
             return getFolderContent(f.getFileId(), onlyOnDevice);
-
         } else {
-            return new Vector<OCFile>();
+            return new ArrayList<>();
         }
     }
 
 
-    public Vector<OCFile> getFolderImages(OCFile folder, boolean onlyOnDevice) {
-        Vector<OCFile> ret = new Vector<OCFile>();
+    public ArrayList<OCFile> getFolderImages(OCFile folder, boolean onlyOnDevice) {
+        ArrayList<OCFile> ret = new ArrayList<>();
+        
         if (folder != null) {
             // TODO better implementation, filtering in the access to database instead of here
-            Vector<OCFile> tmp = getFolderContent(folder, onlyOnDevice);
-            OCFile current = null;
-            for (int i = 0; i < tmp.size(); i++) {
-                current = tmp.get(i);
-                if (MimeTypeUtil.isImage(current)) {
-                    ret.add(current);
+            ArrayList<OCFile> tmp = getFolderContent(folder, onlyOnDevice);
+
+            for (OCFile file : tmp) {
+                if (MimeTypeUtil.isImage(file)) {
+                    ret.add(file);
                 }
             }
         }
@@ -178,7 +176,7 @@ public class FileDataStorageManager {
     }
 
     public boolean saveFile(OCFile file) {
-        boolean overriden = false;
+        boolean overridden = false;
         ContentValues cv = new ContentValues();
         cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
         cv.put(
@@ -222,7 +220,7 @@ public class FileDataStorageManager {
                 oldFile = getFileById(file.getFileId());
             }
 
-            overriden = true;
+            overridden = true;
             if (getContentResolver() != null) {
                 getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv,
                         ProviderTableMeta._ID + "=?",
@@ -253,7 +251,7 @@ public class FileDataStorageManager {
             }
         }
 
-        return overriden;
+        return overridden;
     }
 
     /**
@@ -571,7 +569,7 @@ public class FileDataStorageManager {
         File localFolder = new File(localFolderPath);
         if (localFolder.exists()) {
             // stage 1: remove the local files already registered in the files database
-            Vector<OCFile> files = getFolderContent(folder.getFileId(), false);
+            ArrayList<OCFile> files = getFolderContent(folder.getFileId(), false);
             if (files != null) {
                 for (OCFile file : files) {
                     if (file.isFolder()) {
@@ -810,12 +808,12 @@ public class FileDataStorageManager {
         }
     }
 
-    private Vector<OCFile> getFolderContent(long parentId, boolean onlyOnDevice) {
+    private ArrayList<OCFile> getFolderContent(long parentId, boolean onlyOnDevice) {
 
-        Vector<OCFile> ret = new Vector<>();
+        ArrayList<OCFile> ret = new ArrayList<>();
 
         Uri req_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, String.valueOf(parentId));
-        Cursor c = null;
+        Cursor c;
 
         if (getContentProviderClient() != null) {
             try {
@@ -1610,7 +1608,7 @@ public class FileDataStorageManager {
                     + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
             String[] whereArgs = new String[]{"", mAccount.name};
 
-            Vector<OCFile> files = getFolderContent(folder, false);
+            ArrayList<OCFile> files = getFolderContent(folder, false);
 
             for (OCFile file : files) {
                 whereArgs[0] = file.getRemotePath();
@@ -2135,8 +2133,8 @@ public class FileDataStorageManager {
         }
     }
 
-    public Vector<OCFile> getVirtualFolderContent(VirtualFolderType type, boolean onlyImages) {
-        Vector<OCFile> ocFiles = new Vector<>();
+    public ArrayList<OCFile> getVirtualFolderContent(VirtualFolderType type, boolean onlyImages) {
+        ArrayList<OCFile> ocFiles = new ArrayList<>();
         Uri req_uri = ProviderTableMeta.CONTENT_URI_VIRTUAL;
         Cursor c;
 
@@ -2175,12 +2173,11 @@ public class FileDataStorageManager {
         }
 
         if (onlyImages) {
-            OCFile current;
-            Vector<OCFile> temp = new Vector<>();
-            for (int i=0; i < ocFiles.size(); i++) {
-                current = ocFiles.get(i);
-                if (MimeTypeUtil.isImage(current)) {
-                    temp.add(current);
+            ArrayList<OCFile> temp = new ArrayList<>();
+
+            for (OCFile file : temp) {
+                if (MimeTypeUtil.isImage(file)) {
+                    temp.add(file);
                 }
             }
             ocFiles = temp;

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

@@ -463,7 +463,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
      * directory
      */
     public void setFileName(String name) {
-        Log_OC.d(TAG, "OCFile name changin from " + mRemotePath);
+        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();

+ 1 - 2
src/main/java/com/owncloud/android/jobs/ContactsBackupJob.java

@@ -54,7 +54,6 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Calendar;
-import java.util.Vector;
 
 /**
  * Job that backup contacts to /Contacts-Backup and deletes files older than x days
@@ -184,7 +183,7 @@ public class ContactsBackupJob extends Job {
                 Log_OC.d(TAG, "expire: " + daysToExpire + " " + backupFolder.getFileName());
             }
 
-            Vector<OCFile> backups = storageManager.getFolderContent(backupFolder, false);
+            ArrayList<OCFile> backups = storageManager.getFolderContent(backupFolder, false);
 
             for (OCFile backup : backups) {
                 if (timestampToExpire > backup.getModificationTimestamp()) {

+ 100 - 0
src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java

@@ -0,0 +1,100 @@
+/*
+ * 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 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;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Extends RecyclerView to show a custom view if no data is available
+ * Inspired by http://alexzh.com/tutorials/how-to-setemptyview-to-recyclerview
+ */
+
+public class EmptyRecyclerView extends RecyclerView {
+    private View mEmptyView;
+    private boolean hasFooter = false;
+
+    public EmptyRecyclerView(Context context) {
+        super(context);
+    }
+
+    public EmptyRecyclerView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public EmptyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    public void setAdapter(Adapter adapter) {
+        Adapter oldAdapter = getAdapter();
+        super.setAdapter(adapter);
+        if (oldAdapter != null) {
+            oldAdapter.unregisterAdapterDataObserver(observer);
+        }
+        if (adapter != null) {
+            adapter.registerAdapterDataObserver(observer);
+        }
+    }
+
+    public void setEmptyView(View view) {
+        this.mEmptyView = view;
+        initEmptyView();
+    }
+
+    private void initEmptyView() {
+        if (mEmptyView != null) {
+            int emptyCount = hasFooter ? 1 : 0;
+            boolean empty = getAdapter() == null || getAdapter().getItemCount() == emptyCount;
+            mEmptyView.setVisibility(empty ? VISIBLE : GONE);
+            EmptyRecyclerView.this.setVisibility(empty ? GONE : VISIBLE);
+        }
+    }
+
+    final AdapterDataObserver observer = new AdapterDataObserver() {
+        @Override
+        public void onChanged() {
+            super.onChanged();
+            initEmptyView();
+        }
+
+        @Override
+        public void onItemRangeInserted(int positionStart, int itemCount) {
+            super.onItemRangeInserted(positionStart, itemCount);
+            initEmptyView();
+        }
+
+        @Override
+        public void onItemRangeRemoved(int positionStart, int itemCount) {
+            super.onItemRangeRemoved(positionStart, itemCount);
+            initEmptyView();
+        }
+    };
+
+    public void setHasFooter(boolean bool) {
+        hasFooter = bool;
+    }
+}

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

@@ -628,7 +628,7 @@ public class ReceiveExternalFilesActivity extends FileActivity
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         // click on folder in the list
         Log_OC.d(TAG, "on item click");
-        Vector<OCFile> tmpFiles = getStorageManager().getFolderContent(mFile, false);
+        ArrayList<OCFile> tmpFiles = getStorageManager().getFolderContent(mFile, false);
         tmpFiles = sortFileList(tmpFiles);
 
         if (tmpFiles.size() <= 0) {
@@ -738,7 +738,7 @@ public class ReceiveExternalFilesActivity extends FileActivity
 
         mFile = getStorageManager().getFileByPath(full_path);
         if (mFile != null) {
-            Vector<OCFile> files = getStorageManager().getFolderContent(mFile, false);
+            ArrayList<OCFile> files = getStorageManager().getFolderContent(mFile, false);
 
             if (files.size() == 0) {
                 setMessageForEmptyList(R.string.file_list_empty_headline, R.string.empty,
@@ -834,7 +834,7 @@ public class ReceiveExternalFilesActivity extends FileActivity
         syncFolderOp.execute(getAccount(), this, null, null);
     }
 
-    private Vector<OCFile> sortFileList(Vector<OCFile> files) {
+    private ArrayList<OCFile> sortFileList(ArrayList<OCFile> files) {
         FileSortOrder sortOrder = getSortOrder(this, mFile);
         return sortOrder.sortCloudFiles(files);
     }

+ 134 - 120
src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java

@@ -1,28 +1,27 @@
-/**
- *   ownCloud Android client application
- *
- *   @author LukeOwncloud
- *   @author David A. Velasco
- *   @author masensio
- *   Copyright (C) 2016 ownCloud Inc.
+/*
+ * Nextcloud Android client application
  *
- *   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.
+ * @author Tobias Kaminsky
+ * Copyright (C) 2018 Tobias Kaminsky
+ * Copyright (C) 2018 Nextcloud
  *
- *   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.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
  *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * 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.activity;
 
 import android.accounts.Account;
-import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -30,22 +29,23 @@ import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
-import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.preference.PreferenceManager;
 import android.support.design.widget.BottomNavigationView;
-import android.support.v4.app.FragmentTransaction;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.GridLayoutManager;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 import com.evernote.android.job.JobRequest;
 import com.evernote.android.job.util.support.PersistableBundleCompat;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.UploadsStorageManager;
-import com.owncloud.android.db.OCUpload;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
 import com.owncloud.android.jobs.FilesSyncJob;
@@ -53,13 +53,17 @@ import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.operations.CheckCurrentCredentialsOperation;
-import com.owncloud.android.ui.fragment.UploadListFragment;
+import com.owncloud.android.ui.EmptyRecyclerView;
+import com.owncloud.android.ui.adapter.UploadListAdapter;
+import com.owncloud.android.ui.decoration.MediaGridItemDecoration;
 import com.owncloud.android.utils.AnalyticsUtils;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.FilesSyncHelper;
-import com.owncloud.android.utils.MimeTypeUtil;
 
-import java.io.File;
+import butterknife.BindString;
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.Unbinder;
 
 import static com.owncloud.android.ui.activity.Preferences.PREFERENCE_EXPERT_MODE;
 
@@ -67,19 +71,42 @@ import static com.owncloud.android.ui.activity.Preferences.PREFERENCE_EXPERT_MOD
  * Activity listing pending, active, and completed uploads. User can delete
  * completed uploads from view. Content of this list of coming from
  * {@link UploadsStorageManager}.
- *
  */
-public class UploadListActivity extends FileActivity implements UploadListFragment.ContainerActivity {
+public class UploadListActivity extends FileActivity {
 
     private static final String TAG = UploadListActivity.class.getSimpleName();
 
-    private static final String TAG_UPLOAD_LIST_FRAGMENT = "UPLOAD_LIST_FRAGMENT";
-
     private static final String SCREEN_NAME = "Uploads";
 
     private UploadMessagesReceiver mUploadMessagesReceiver;
 
-    private Menu mMenu;
+    private Menu menu;
+
+    private UploadListAdapter uploadListAdapter;
+
+    private UploadsStorageManager uploadStorageManager;
+
+    public SwipeRefreshLayout swipeListRefreshLayout;
+
+    @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;
+
+    @BindString(R.string.upload_list_empty_headline)
+    public String noResultsHeadline;
+
+    @BindString(R.string.upload_list_empty_text_auto_upload)
+    public String noResultsMessage;
+
+    private Unbinder unbinder;
 
     @Override
     public void showFiles(boolean onDeviceOnly) {
@@ -93,7 +120,12 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        uploadStorageManager = new UploadsStorageManager(getContentResolver(), getApplicationContext());
+
         setContentView(R.layout.upload_list_layout);
+        unbinder = ButterKnife.bind(this);
+
+        swipeListRefreshLayout = findViewById(R.id.swipe_containing_list);
 
         // this activity has no file really bound, it's for multiple accounts at the same time; should no inherit
         // from FileActivity; moreover, some behaviours inherited from FileActivity should be delegated to Fragments;
@@ -106,12 +138,11 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
         // setup drawer
         setupDrawer(R.id.nav_uploads);
 
-        // Add fragment with a transaction for setting a tag
-        if(savedInstanceState == null) {
-            createUploadListFragment();
-        } // else, the Fragment Manager makes the job on configuration changes
+        setupContent();
 
-        getSupportActionBar().setTitle(getString(R.string.uploads_view_title));
+        if (getSupportActionBar() != null) {
+            getSupportActionBar().setTitle(getString(R.string.uploads_view_title));
+        }
 
         BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation_view);
 
@@ -121,13 +152,46 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
         }
     }
 
-    private void createUploadListFragment(){
-        UploadListFragment uploadList = new UploadListFragment();
-        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-        transaction.add(R.id.upload_list_fragment, uploadList, TAG_UPLOAD_LIST_FRAGMENT);
-        transaction.commit();
+    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_list_empty_upload);
+        emptyContentIcon.setVisibility(View.VISIBLE);
+        emptyContentHeadline.setText(noResultsHeadline);
+        emptyContentMessage.setText(noResultsMessage);
+
+        uploadListAdapter = new UploadListAdapter(this);
+
+        final GridLayoutManager lm = new GridLayoutManager(this, 1);
+        uploadListAdapter.setLayoutManager(lm);
+
+        int spacing = getResources().getDimensionPixelSize(R.dimen.media_grid_spacing);
+        recyclerView.addItemDecoration(new MediaGridItemDecoration(spacing));
+        recyclerView.setLayoutManager(lm);
+        recyclerView.setAdapter(uploadListAdapter);
+
+
+        swipeListRefreshLayout.setOnRefreshListener(this::refresh);
+
+        loadItems();
+    }
+
+    private void loadItems() {
+        uploadListAdapter.loadUploadItemsFromDb();
+
+        if (uploadListAdapter.getItemCount() > 0) {
+            return;
+        }
+
+        swipeListRefreshLayout.setVisibility(View.VISIBLE);
+        swipeListRefreshLayout.setRefreshing(false);
     }
 
+    private void refresh() {
+        uploadListAdapter.loadUploadItemsFromDb();
+        swipeListRefreshLayout.setRefreshing(false);
+    }
 
     @Override
     protected void onResume() {
@@ -159,46 +223,9 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
         Log_OC.v(TAG, "onPause() end");
     }
 
-    // ////////////////////////////////////////
-    // UploadListFragment.ContainerActivity
-    // ////////////////////////////////////////
-    @Override
-    public boolean onUploadItemClick(OCUpload file) {
-        /// TODO is this path still active?
-        File f = new File(file.getLocalPath());
-        if(!f.exists()) {
-            DisplayUtils.showSnackMessage(this, R.string.local_file_not_found_toast);
-        } else {
-            openFileWithDefault(file.getLocalPath());
-        }
-        return true;
-    }
-
-    /**
-     * Open file with app associates with its MIME type. If MIME type unknown, show list with all apps.
-     */
-    private void openFileWithDefault(String localPath) {
-        Intent myIntent = new Intent(Intent.ACTION_VIEW);
-        File file = new File(localPath);
-        String mimetype = MimeTypeUtil.getBestMimeTypeByFilename(localPath);
-        if ("application/octet-stream".equals(mimetype)) {
-            mimetype = "*/*";
-        }
-        myIntent.setDataAndType(Uri.fromFile(file), mimetype);
-        try {
-            startActivity(myIntent);
-        } catch (ActivityNotFoundException e) {
-            DisplayUtils.showSnackMessage(this, R.string.file_list_no_app_for_file_type);
-            Log_OC.i(TAG, "Could not find app for sending log history.");
-        }        
-    }
-
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         boolean retval = true;
-        UploadsStorageManager storageManager = null;
-        UploadListFragment uploadListFragment =
-                (UploadListFragment) getSupportFragmentManager().findFragmentByTag(TAG_UPLOAD_LIST_FRAGMENT);
         switch (item.getItemId()) {
             case android.R.id.home:
                 if (isDrawerOpen()) {
@@ -211,25 +238,21 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
             case R.id.action_retry_uploads:
                 FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
 
-                new Thread(() -> {
-                    requester.retryFailedUploads(this, null, null);
-                }).start();
-                
-                if (mMenu != null) {
-                    mMenu.removeItem(R.id.action_retry_uploads);
+                new Thread(() -> requester.retryFailedUploads(this, null, null)).start();
+
+                if (menu != null) {
+                    menu.removeItem(R.id.action_retry_uploads);
                 }
                 break;
 
             case R.id.action_clear_failed_uploads:
-                storageManager = new UploadsStorageManager(getContentResolver(), getApplicationContext());
-                storageManager.clearFailedButNotDelayedUploads();
-                uploadListFragment.updateUploads();
+                uploadStorageManager.clearFailedButNotDelayedUploads();
+                uploadListAdapter.loadUploadItemsFromDb();
                 break;
 
             case R.id.action_clear_successfull_uploads:
-                storageManager = new UploadsStorageManager(getContentResolver(), getApplicationContext());
-                storageManager.clearSuccessfulUploads();
-                uploadListFragment.updateUploads();
+                uploadStorageManager.clearSuccessfulUploads();
+                uploadListAdapter.loadUploadItemsFromDb();
                 break;
 
             case R.id.action_force_rescan:
@@ -241,9 +264,9 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
                         .setExtras(persistableBundleCompat)
                         .build()
                         .schedule();
-                
-                if (mMenu != null) {
-                    mMenu.removeItem(R.id.action_force_rescan);
+
+                if (menu != null) {
+                    menu.removeItem(R.id.action_force_rescan);
                 }
 
                 break;
@@ -257,12 +280,11 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
-        SharedPreferences appPrefs =
-                PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+        SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
         if (appPrefs.getBoolean(PREFERENCE_EXPERT_MODE, false)) {
             MenuInflater inflater = getMenuInflater();
             inflater.inflate(R.menu.upload_list_menu, menu);
-            mMenu = menu;
+            this.menu = menu;
         }
 
         return true;
@@ -277,14 +299,13 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
     }
 
     /**
-     *
-     * @param operation     Operation performed.
-     * @param result        Result of the removal.
+     * @param operation Operation performed.
+     * @param result    Result of the removal.
      */
     @Override
     public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
         if (operation instanceof CheckCurrentCredentialsOperation) {
-            // Do not call super in this case; more refactoring needed around onRemoteOeprationFinish :'(
+            // Do not call super in this case; more refactoring needed around onRemoteOperationFinish :'(
             getFileOperationsHelper().setOpIdWaitingFor(Long.MAX_VALUE);
             dismissLoadingDialog();
             Account account = (Account) result.getData().get(0);
@@ -307,24 +328,18 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
         return new UploadListServiceConnection();
     }
 
-    /** Defines callbacks for service binding, passed to bindService() */
+    /**
+     * Defines callbacks for service binding, passed to bindService()
+     */
     private class UploadListServiceConnection implements ServiceConnection {
 
         @Override
         public void onServiceConnected(ComponentName component, IBinder service) {
             if (service instanceof FileUploaderBinder) {
-                if(mUploaderBinder == null)
-                {
+                if (mUploaderBinder == null) {
                     mUploaderBinder = (FileUploaderBinder) service;
                     Log_OC.d(TAG, "UploadListActivity connected to Upload service. component: " +
-                            component + " service: "
-                            + service);
-                    // Say to UploadListFragment that the Binder is READY in the Activity
-                    UploadListFragment uploadListFragment =
-                            (UploadListFragment) getSupportFragmentManager().findFragmentByTag(TAG_UPLOAD_LIST_FRAGMENT);
-                    if (uploadListFragment != null) {
-                        uploadListFragment.binderReady();
-                    }
+                            component + " service: " + service);
                 } else {
                     Log_OC.d(TAG, "mUploaderBinder already set. mUploaderBinder: " +
                             mUploaderBinder + " service:" + service);
@@ -332,8 +347,7 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
             } else {
                 Log_OC.d(TAG, "UploadListActivity not connected to Upload service. component: " +
                         component + " service: " + service);
-                return;
-            }            
+            }
         }
 
         @Override
@@ -355,10 +369,7 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
         @Override
         public void onReceive(Context context, Intent intent) {
             try {
-                UploadListFragment uploadListFragment =
-                    (UploadListFragment) getSupportFragmentManager().findFragmentByTag(TAG_UPLOAD_LIST_FRAGMENT);
-
-                uploadListFragment.updateUploads();
+                uploadListAdapter.loadUploadItemsFromDb();
             } finally {
                 if (intent != null) {
                     removeStickyBroadcast(intent);
@@ -368,21 +379,24 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
         }
     }
 
-    protected String getDefaultTitle() {
-        return getString(R.string.uploads_view_title);
-    }
-
-
     /**
      * Called when the ownCloud {@link Account} associated to the Activity was just updated.
      */
     @Override
     protected void onAccountSet(boolean stateWasRecovered) {
         super.onAccountSet(stateWasRecovered);
-        getSupportActionBar().setTitle(getString(R.string.uploads_view_title));
+
+        if (getSupportActionBar() != null) {
+            getSupportActionBar().setTitle(getString(R.string.uploads_view_title));
+        }
+
         if (mAccountWasSet) {
             setAccountInDrawer(getAccount());
         }
     }
 
+    public void onDestroy() {
+        super.onDestroy();
+        unbinder.unbind();
+    }
 }

+ 778 - 778
src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java

@@ -1,778 +1,778 @@
-/*
- * ownCloud Android client application
- *
- * @author Bartek Przybylski
- * @author Tobias Kaminsky
- * @author David A. Velasco
- * @author masensio
- * Copyright (C) 2011  Bartek Przybylski
- * Copyright (C) 2016 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package com.owncloud.android.ui.adapter;
-
-
-import android.accounts.Account;
-import android.content.ContentValues;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.os.Handler;
-import android.os.Looper;
-import android.text.TextUtils;
-import android.util.SparseBooleanArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.BaseAdapter;
-import android.widget.Filter;
-import android.widget.GridView;
-import android.widget.ImageView;
-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.OCFile;
-import com.owncloud.android.datamodel.ThumbnailsCacheManager;
-import com.owncloud.android.datamodel.VirtualFolderType;
-import com.owncloud.android.db.PreferenceManager;
-import com.owncloud.android.db.ProviderMeta;
-import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
-import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult;
-import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
-import com.owncloud.android.lib.resources.files.RemoteFile;
-import com.owncloud.android.lib.resources.shares.OCShare;
-import com.owncloud.android.lib.resources.shares.ShareType;
-import com.owncloud.android.operations.RemoteOperationFailedException;
-import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
-import com.owncloud.android.ui.activity.ComponentsGetter;
-import com.owncloud.android.ui.fragment.ExtendedListFragment;
-import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface;
-import com.owncloud.android.utils.DisplayUtils;
-import com.owncloud.android.utils.FileSortOrder;
-import com.owncloud.android.utils.FileStorageUtils;
-import com.owncloud.android.utils.MimeTypeUtil;
-import com.owncloud.android.utils.ThemeUtils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Locale;
-import java.util.Vector;
-
-
-/**
- * This Adapter populates a ListView with all files and folders in an ownCloud
- * instance.
- */
-public class FileListListAdapter extends BaseAdapter {
-
-    public static final int showFilenameColumnThreshold = 4;
-    private Context mContext;
-    private Vector<OCFile> mFilesAll = new Vector<>();
-    private Vector<OCFile> mFiles = new Vector<>();
-    private boolean mJustFolders;
-    private boolean mHideItemOptions;
-
-    private FileDataStorageManager mStorageManager;
-    private Account mAccount;
-    private ComponentsGetter mTransferServiceGetter;
-    private OCFileListFragmentInterface OCFileListFragmentInterface;
-
-    private FilesFilter mFilesFilter;
-    private OCFile currentDirectory;
-    private static final String TAG = FileListListAdapter.class.getSimpleName();
-
-    private ArrayList<ThumbnailsCacheManager.ThumbnailGenerationTask> asyncTasks = new ArrayList<>();
-
-    public FileListListAdapter(Context context, ComponentsGetter transferServiceGetter,
-                               OCFileListFragmentInterface OCFileListFragmentInterface, boolean argHideItemOptions) {
-
-        this.OCFileListFragmentInterface = OCFileListFragmentInterface;
-        mContext = context;
-        mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
-        mHideItemOptions = argHideItemOptions;
-
-        mTransferServiceGetter = transferServiceGetter;
-
-        // initialise thumbnails cache on background thread
-        new ThumbnailsCacheManager.InitDiskCacheTask().execute();
-    }
-
-    @Override
-    public boolean areAllItemsEnabled() {
-        return true;
-    }
-
-    @Override
-    public boolean isEnabled(int position) {
-        return true;
-    }
-
-    @Override
-    public int getCount() {
-        return mFiles != null ? mFiles.size() : 0;
-    }
-
-    @Override
-    public Object getItem(int position) {
-        if (mFiles == null || mFiles.size() <= position) {
-            return null;
-        }
-        return mFiles.get(position);
-    }
-
-    public void setFavoriteAttributeForItemID(String fileId, boolean favorite) {
-        for (int i = 0; i < mFiles.size(); i++) {
-            if (mFiles.get(i).getRemoteId().equals(fileId)) {
-                mFiles.get(i).setFavorite(favorite);
-                break;
-            }
-        }
-
-        for (int i = 0; i < mFilesAll.size(); i++) {
-            if (mFilesAll.get(i).getRemoteId().equals(fileId)) {
-                mFilesAll.get(i).setFavorite(favorite);
-                break;
-            }
-        }
-
-        new Handler(Looper.getMainLooper()).post(new Runnable() {
-            @Override
-            public void run() {
-                notifyDataSetChanged();
-            }
-        });
-    }
-
-    public void setEncryptionAttributeForItemID(String fileId, boolean encrypted) {
-        for (int i = 0; i < mFiles.size(); i++) {
-            if (mFiles.get(i).getRemoteId().equals(fileId)) {
-                mFiles.get(i).setEncrypted(encrypted);
-                break;
-            }
-        }
-
-        for (int i = 0; i < mFilesAll.size(); i++) {
-            if (mFilesAll.get(i).getRemoteId().equals(fileId)) {
-                mFilesAll.get(i).setEncrypted(encrypted);
-                break;
-            }
-        }
-
-        new Handler(Looper.getMainLooper()).post(new Runnable() {
-            @Override
-            public void run() {
-                notifyDataSetChanged();
-            }
-        });
-    }
-
-    @Override
-    public long getItemId(int position) {
-        if (mFiles == null || mFiles.size() <= position) {
-            return 0;
-        }
-        return mFiles.get(position).getFileId();
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        return 0;
-    }
-
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-
-        View view = convertView;
-        OCFile file = null;
-        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
-        if (mFiles != null && mFiles.size() > position) {
-            file = mFiles.get(position);
-        }
-
-        // Find out which layout should be displayed
-        ViewType viewType;
-        if (parent instanceof GridView) {
-            if (file != null && (MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file))) {
-                viewType = ViewType.GRID_IMAGE;
-            } else {
-                viewType = ViewType.GRID_ITEM;
-            }
-        } else {
-            viewType = ViewType.LIST_ITEM;
-        }
-
-        // create view only if differs, otherwise reuse
-        if (convertView == null || convertView.getTag() != viewType) {
-            switch (viewType) {
-                case GRID_IMAGE:
-                    view = inflater.inflate(R.layout.grid_image, parent, false);
-                    view.setTag(ViewType.GRID_IMAGE);
-                    break;
-                case GRID_ITEM:
-                    view = inflater.inflate(R.layout.grid_item, parent, false);
-                    view.setTag(ViewType.GRID_ITEM);
-                    break;
-                case LIST_ITEM:
-                    view = inflater.inflate(R.layout.list_item, parent, false);
-                    view.setTag(ViewType.LIST_ITEM);
-                    break;
-            }
-        }
-
-        if (file != null) {
-            ImageView fileIcon = view.findViewById(R.id.thumbnail);
-
-            fileIcon.setTag(file.getFileId());
-            TextView fileName;
-            String name = file.getFileName();
-
-            switch (viewType) {
-                case LIST_ITEM:
-                    TextView fileSizeV = view.findViewById(R.id.file_size);
-                    TextView fileSizeSeparatorV = view.findViewById(R.id.file_separator);
-                    TextView lastModV = view.findViewById(R.id.last_mod);
-
-
-                    lastModV.setVisibility(View.VISIBLE);
-                    lastModV.setText(DisplayUtils.getRelativeTimestamp(mContext, file.getModificationTimestamp()));
-
-
-                    fileSizeSeparatorV.setVisibility(View.VISIBLE);
-                    fileSizeV.setVisibility(View.VISIBLE);
-                    fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
-
-                case GRID_ITEM:
-                    // filename
-                    fileName = view.findViewById(R.id.Filename);
-                    fileName.setText(name);
-
-                    if (OCFileListFragmentInterface.getColumnSize() > showFilenameColumnThreshold
-                            && viewType == ViewType.GRID_ITEM) {
-                        fileName.setVisibility(View.GONE);
-                    }
-
-                case GRID_IMAGE:
-
-                    // local state
-                    ImageView localStateView = view.findViewById(R.id.localFileIndicator);
-                    localStateView.bringToFront();
-                    FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();
-                    FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();
-                    OperationsServiceBinder opsBinder = mTransferServiceGetter.getOperationsServiceBinder();
-
-                    localStateView.setVisibility(View.INVISIBLE);   // default first
-
-                    if (opsBinder != null && opsBinder.isSynchronizing(mAccount, file)) {
-                        //synchronizing
-                        localStateView.setImageResource(R.drawable.ic_synchronizing);
-                        localStateView.setVisibility(View.VISIBLE);
-
-                    } else if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {
-                        // downloading
-                        localStateView.setImageResource(R.drawable.ic_synchronizing);
-                        localStateView.setVisibility(View.VISIBLE);
-
-                    } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {
-                        //uploading
-                        localStateView.setImageResource(R.drawable.ic_synchronizing);
-                        localStateView.setVisibility(View.VISIBLE);
-
-                    } else if (file.getEtagInConflict() != null) {
-                        // conflict
-                        localStateView.setImageResource(R.drawable.ic_synchronizing_error);
-                        localStateView.setVisibility(View.VISIBLE);
-
-                    } else if (file.isDown()) {
-                        localStateView.setImageResource(R.drawable.ic_synced);
-                        localStateView.setVisibility(View.VISIBLE);
-                    }
-
-                    break;
-            }
-
-            // For all Views
-            if (file.getIsFavorite()) {
-                view.findViewById(R.id.favorite_action).setVisibility(View.VISIBLE);
-            } else {
-                view.findViewById(R.id.favorite_action).setVisibility(View.GONE);
-            }
-
-            ImageView checkBoxV = view.findViewById(R.id.custom_checkbox);
-            view.setBackgroundColor(Color.WHITE);
-
-            AbsListView parentList = (AbsListView) parent;
-
-            if (parentList.getChoiceMode() != AbsListView.CHOICE_MODE_NONE && parentList.getCheckedItemCount() > 0) {
-                if (parentList.isItemChecked(position)) {
-                    view.setBackgroundColor(mContext.getResources().getColor(R.color.selected_item_background));
-                    checkBoxV.setImageDrawable(ThemeUtils.tintDrawable(R.drawable.ic_checkbox_marked,
-                            ThemeUtils.primaryColor()));
-                } else {
-                    view.setBackgroundColor(Color.WHITE);
-                    checkBoxV.setImageResource(R.drawable.ic_checkbox_blank_outline);
-                }
-                checkBoxV.setVisibility(View.VISIBLE);
-                hideShareIcon(view);
-                hideOverflowMenuIcon(view, viewType);
-            } else {
-                checkBoxV.setVisibility(View.GONE);
-
-                if (mHideItemOptions) {
-                    ImageView sharedIconView = view.findViewById(R.id.sharedIcon);
-                    sharedIconView.setVisibility(View.GONE);
-
-                    ImageView overflowIndicatorView = view.findViewById(R.id.overflow_menu);
-                    overflowIndicatorView.setVisibility(View.GONE);
-                } else {
-                    showShareIcon(view, file);
-                    showOverflowMenuIcon(view, file, viewType);
-                }
-            }
-
-            // this if-else is needed even though kept-in-sync icon is visible by default
-            // because android reuses views in listview
-            if (!file.isAvailableOffline()) {
-                view.findViewById(R.id.keptOfflineIcon).setVisibility(View.GONE);
-            } else {
-                view.findViewById(R.id.keptOfflineIcon).setVisibility(View.VISIBLE);
-            }
-
-
-            // No Folder
-            if (!file.isFolder()) {
-                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 && !file.needsUpdateThumbnail()) {
-
-                        if (MimeTypeUtil.isVideo(file)) {
-                            Bitmap withOverlay = ThumbnailsCacheManager.addVideoOverlay(thumbnail);
-                            fileIcon.setImageBitmap(withOverlay);
-                        } else {
-                            fileIcon.setImageBitmap(thumbnail);
-                        }
-                    } else {
-                        // generate new Thumbnail
-                        if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon)) {
-                            try {
-                                final ThumbnailsCacheManager.ThumbnailGenerationTask task =
-                                        new ThumbnailsCacheManager.ThumbnailGenerationTask(
-                                                fileIcon, mStorageManager, mAccount, asyncTasks);
-
-                                if (thumbnail == null) {
-                                    if (MimeTypeUtil.isVideo(file)) {
-                                        thumbnail = ThumbnailsCacheManager.mDefaultVideo;
-                                    } else {
-                                        thumbnail = ThumbnailsCacheManager.mDefaultImg;
-                                    }
-                                }
-                                final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
-                                        new ThumbnailsCacheManager.AsyncThumbnailDrawable(
-                                                mContext.getResources(),
-                                                thumbnail,
-                                                task
-                                        );
-                                fileIcon.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")) {
-                        fileIcon.setBackgroundColor(mContext.getResources().getColor(R.color.background_color));
-                    }
-
-
-                } else {
-                    fileIcon.setImageDrawable(
-                            MimeTypeUtil.getFileTypeIcon(file.getMimetype(), file.getFileName(), mAccount)
-                    );
-                }
-
-
-            } else {
-                // Folder
-                fileIcon.setImageDrawable(MimeTypeUtil.getFolderTypeIcon(file.isSharedWithMe() ||
-                        file.isSharedWithSharee(), file.isSharedViaLink(), file.isEncrypted(), file.getMountType()));
-            }
-        }
-        return view;
-    }
-
-    private void showShareIcon(View view, OCFile file) {
-        ImageView sharedIconV = view.findViewById(R.id.sharedIcon);
-        sharedIconV.setVisibility(View.VISIBLE);
-        if (file.isSharedWithSharee() || file.isSharedWithMe()) {
-            sharedIconV.setImageResource(R.drawable.shared_via_users);
-        } else if (file.isSharedViaLink()) {
-            sharedIconV.setImageResource(R.drawable.shared_via_link);
-        } else {
-            sharedIconV.setImageResource(R.drawable.ic_unshared);
-        }
-        sharedIconV.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                OCFileListFragmentInterface.onShareIconClick(file);
-            }
-        });
-    }
-
-    private void hideShareIcon(View view) {
-        view.findViewById(R.id.sharedIcon).setVisibility(View.GONE);
-    }
-
-    private void showOverflowMenuIcon(View view, OCFile file, ViewType viewType) {
-        if (ViewType.LIST_ITEM.equals(viewType)) {
-            ImageView overflowIndicatorV = view.findViewById(R.id.overflow_menu);
-            overflowIndicatorV.setVisibility(View.VISIBLE);
-            overflowIndicatorV.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    OCFileListFragmentInterface.onOverflowIconClick(v, file);
-                }
-            });
-        }
-    }
-
-    private void hideOverflowMenuIcon(View view, ViewType viewType) {
-        if (ViewType.LIST_ITEM.equals(viewType)) {
-            ImageView overflowIndicatorV = view.findViewById(R.id.overflow_menu);
-            overflowIndicatorV.setVisibility(View.GONE);
-        }
-    }
-
-    @Override
-    public int getViewTypeCount() {
-        return 1;
-    }
-
-    @Override
-    public boolean hasStableIds() {
-        return true;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return (mFiles == null || mFiles.isEmpty());
-    }
-
-    /**
-     * Change the adapted directory for a new one
-     *
-     * @param directory             New folder to adapt. Can be NULL, meaning
-     *                              "no content to adapt".
-     * @param updatedStorageManager Optional updated storage manager; used to replace
-     *                              mStorageManager if is different (and not NULL)
-     */
-    public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager
-            , boolean onlyOnDevice) {
-        if (updatedStorageManager != null && !updatedStorageManager.equals(mStorageManager)) {
-            mStorageManager = updatedStorageManager;
-            mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
-        }
-        if (mStorageManager != null) {
-            mFiles = mStorageManager.getFolderContent(directory, onlyOnDevice);
-
-            if (mJustFolders) {
-                mFiles = getFolders(mFiles);
-            }
-            if (!PreferenceManager.showHiddenFilesEnabled(mContext)) {
-                mFiles = filterHiddenFiles(mFiles);
-            }
-            FileSortOrder sortOrder = PreferenceManager.getSortOrder(mContext, directory);
-            mFiles = sortOrder.sortCloudFiles(mFiles);
-            mFilesAll.clear();
-            mFilesAll.addAll(mFiles);
-
-            currentDirectory = directory;
-        } else {
-            mFiles.clear();
-            mFilesAll.clear();
-        }
-
-        notifyDataSetChanged();
-    }
-
-    private void searchForLocalFileInDefaultPath(OCFile file) {
-        if (file.getStoragePath() == null && !file.isFolder()) {
-            File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
-            if (f.exists()) {
-                file.setStoragePath(f.getAbsolutePath());
-                file.setLastSyncDateForData(f.lastModified());
-            }
-        }
-    }
-
-    public void setData(ArrayList<Object> objects, ExtendedListFragment.SearchType searchType,
-                        FileDataStorageManager storageManager, OCFile folder) {
-        if (storageManager != null && mStorageManager == null) {
-            mStorageManager = storageManager;
-        }
-        mFiles.clear();
-
-        // early exit
-        if (objects.size() > 0 && mStorageManager != null) {
-            if (searchType.equals(ExtendedListFragment.SearchType.SHARED_FILTER)) {
-                parseShares(objects);
-            } else {
-                parseVirtuals(objects, searchType);
-            }
-        }
-
-        if (!searchType.equals(ExtendedListFragment.SearchType.PHOTO_SEARCH) &&
-                !searchType.equals(ExtendedListFragment.SearchType.PHOTOS_SEARCH_FILTER) &&
-                !searchType.equals(ExtendedListFragment.SearchType.RECENTLY_MODIFIED_SEARCH) &&
-                !searchType.equals(ExtendedListFragment.SearchType.RECENTLY_MODIFIED_SEARCH_FILTER)) {
-            FileSortOrder sortOrder = PreferenceManager.getSortOrder(mContext, folder);
-            mFiles = sortOrder.sortCloudFiles(mFiles);
-        } else {
-            mFiles = FileStorageUtils.sortOcFolderDescDateModified(mFiles);
-        }
-
-        mFilesAll = new Vector<>();
-        mFilesAll.addAll(mFiles);
-
-        new Handler(Looper.getMainLooper()).post(new Runnable() {
-            @Override
-            public void run() {
-                notifyDataSetChanged();
-                OCFileListFragmentInterface.finishedFiltering();
-            }
-        });
-    }
-
-    private void parseShares(ArrayList<Object> objects) {
-        ArrayList<OCShare> shares = new ArrayList<>();
-        for (int i = 0; i < objects.size(); i++) {
-            // check type before cast as of long running data fetch it is possible that old result is filled
-            if (objects.get(i) instanceof OCShare) {
-                OCShare ocShare = (OCShare) objects.get(i);
-
-                shares.add(ocShare);
-
-                // get ocFile from Server to have an up-to-date copy
-                ReadRemoteFileOperation operation = new ReadRemoteFileOperation(ocShare.getPath());
-                RemoteOperationResult result = operation.execute(mAccount, mContext);
-                if (result.isSuccess()) {
-                    OCFile file = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
-                    searchForLocalFileInDefaultPath(file);
-                    file = mStorageManager.saveFileWithParent(file, mContext);
-
-                    ShareType newShareType = ocShare.getShareType();
-                    if (newShareType == ShareType.PUBLIC_LINK) {
-                        file.setShareViaLink(true);
-                    } else if (newShareType == ShareType.USER || newShareType == ShareType.GROUP ||
-                                    newShareType == ShareType.EMAIL || newShareType == ShareType.FEDERATED) {
-                        file.setShareWithSharee(true);
-                    }
-
-                    mStorageManager.saveFile(file);
-
-                    if (!mFiles.contains(file)) {
-                        mFiles.add(file);
-                    }
-                } else {
-                    Log_OC.e(TAG, "Error in getting prop for file: " + ocShare.getPath());
-                }
-            }
-        }
-        mStorageManager.saveShares(shares);
-    }
-
-    private void parseVirtuals(ArrayList<Object> objects, ExtendedListFragment.SearchType searchType) {
-        VirtualFolderType type;
-        boolean onlyImages = false;
-        switch (searchType) {
-            case FAVORITE_SEARCH:
-                type = VirtualFolderType.FAVORITE;
-                break;
-            case PHOTO_SEARCH:
-                type = VirtualFolderType.PHOTOS;
-                onlyImages = true;
-                break;
-            default:
-                type = VirtualFolderType.NONE;
-                break;
-        }
-
-        mStorageManager.deleteVirtuals(type);
-
-        ArrayList<ContentValues> contentValues = new ArrayList<>();
-
-        for (int i = 0; i < objects.size(); i++) {
-            OCFile ocFile = FileStorageUtils.fillOCFile((RemoteFile) objects.get(i));
-            searchForLocalFileInDefaultPath(ocFile);
-
-            try {
-                ocFile = mStorageManager.saveFileWithParent(ocFile, mContext);
-
-                if (!onlyImages || MimeTypeUtil.isImage(ocFile)) {
-                    mFiles.add(ocFile);
-                }
-
-                ContentValues cv = new ContentValues();
-                cv.put(ProviderMeta.ProviderTableMeta.VIRTUAL_TYPE, type.toString());
-                cv.put(ProviderMeta.ProviderTableMeta.VIRTUAL_OCFILE_ID, ocFile.getFileId());
-
-                contentValues.add(cv);
-            } catch (RemoteOperationFailedException e) {
-                Log_OC.e(TAG, "Error saving file with parent" + e.getMessage(),e);
-            }
-        }
-
-        mStorageManager.saveVirtuals(type, contentValues);
-    }
-
-    /**
-     * Filter for getting only the folders
-     *
-     * @param files Collection of files to filter
-     * @return Folders in the input
-     */
-    public Vector<OCFile> getFolders(Vector<OCFile> files) {
-        Vector<OCFile> ret = new Vector<>();
-        OCFile current;
-        for (int i = 0; i < files.size(); i++) {
-            current = files.get(i);
-            if (current.isFolder()) {
-                ret.add(current);
-            }
-        }
-        return ret;
-    }
-
-
-    public void setSortOrder(OCFile folder, FileSortOrder sortOrder) {
-        PreferenceManager.setSortOrder(mContext, folder, sortOrder);
-        mFiles = sortOrder.sortCloudFiles(mFiles);
-        notifyDataSetChanged();
-    }
-
-
-    public ArrayList<OCFile> getCheckedItems(AbsListView parentList) {
-        SparseBooleanArray checkedPositions = parentList.getCheckedItemPositions();
-        ArrayList<OCFile> files = new ArrayList<>();
-        Object item;
-        for (int i = 0; i < checkedPositions.size(); i++) {
-            if (checkedPositions.valueAt(i)) {
-                item = getItem(checkedPositions.keyAt(i));
-                if (item != null) {
-                    files.add((OCFile) item);
-                }
-            }
-        }
-        return files;
-    }
-
-    public Vector<OCFile> getFiles() {
-        return mFiles;
-    }
-
-    public Filter getFilter() {
-        if (mFilesFilter == null) {
-            mFilesFilter = new FilesFilter();
-        }
-        return mFilesFilter;
-    }
-
-    private class FilesFilter extends Filter {
-
-        @Override
-        protected FilterResults performFiltering(CharSequence constraint) {
-            FilterResults results = new FilterResults();
-            Vector<OCFile> filteredFiles = new Vector<>();
-
-            if (!TextUtils.isEmpty(constraint)) {
-                for (int i = 0; i < mFilesAll.size(); i++) {
-                    OCFile currentFile = mFilesAll.get(i);
-                    if (currentFile.getParentRemotePath().equals(currentDirectory.getRemotePath()) &&
-                            currentFile.getFileName().toLowerCase(Locale.getDefault()).contains(
-                                    constraint.toString().toLowerCase(Locale.getDefault())) && 
-                            !filteredFiles.contains(currentFile)) {
-                        filteredFiles.add(currentFile);
-                    }
-                }
-            }
-
-            results.values = filteredFiles;
-            results.count = filteredFiles.size();
-
-            return results;
-        }
-
-        @Override
-        protected void publishResults(CharSequence constraint, Filter.FilterResults results) {
-            Vector<OCFile> ocFiles = (Vector<OCFile>) results.values;
-            mFiles.clear();
-            if (ocFiles != null && ocFiles.size() > 0) {
-                mFiles.addAll(ocFiles);
-                if (!PreferenceManager.showHiddenFilesEnabled(mContext)) {
-                    mFiles = filterHiddenFiles(mFiles);
-                }
-                FileSortOrder sortOrder = PreferenceManager.getSortOrder(mContext, null);
-                mFiles = sortOrder.sortCloudFiles(mFiles);
-            }
-
-            notifyDataSetChanged();
-            OCFileListFragmentInterface.finishedFiltering();
-
-        }
-    }
-
-
-    /**
-     * Filter for hidden files
-     *
-     * @param files Collection of files to filter
-     * @return Non-hidden files
-     */
-    public Vector<OCFile> filterHiddenFiles(Vector<OCFile> files) {
-        Vector<OCFile> ret = new Vector<>();
-        OCFile current;
-        for (int i = 0; i < files.size(); i++) {
-            current = files.get(i);
-            if (!current.isHidden() && !ret.contains(current)) {
-                ret.add(current);
-            }
-        }
-        return ret;
-    }
-
-    public void cancelAllPendingTasks() {
-        for (ThumbnailsCacheManager.ThumbnailGenerationTask task : asyncTasks) {
-            if (task != null) {
-                task.cancel(true);
-                if (task.getGetMethod() != null) {
-                    Log_OC.d(TAG, "cancel: abort get method directly");
-                    task.getGetMethod().abort();
-                }
-            }
-        }
-
-        asyncTasks.clear();
-    }
-
-}
+/*
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author Tobias Kaminsky
+ * @author David A. Velasco
+ * @author masensio
+ * Copyright (C) 2011  Bartek Przybylski
+ * Copyright (C) 2016 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.adapter;
+
+
+import android.accounts.Account;
+import android.content.ContentValues;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.util.SparseBooleanArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.BaseAdapter;
+import android.widget.Filter;
+import android.widget.GridView;
+import android.widget.ImageView;
+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.OCFile;
+import com.owncloud.android.datamodel.ThumbnailsCacheManager;
+import com.owncloud.android.datamodel.VirtualFolderType;
+import com.owncloud.android.db.PreferenceManager;
+import com.owncloud.android.db.ProviderMeta;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
+import com.owncloud.android.lib.resources.files.RemoteFile;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.lib.resources.shares.ShareType;
+import com.owncloud.android.operations.RemoteOperationFailedException;
+import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
+import com.owncloud.android.ui.activity.ComponentsGetter;
+import com.owncloud.android.ui.fragment.ExtendedListFragment;
+import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface;
+import com.owncloud.android.utils.DisplayUtils;
+import com.owncloud.android.utils.FileSortOrder;
+import com.owncloud.android.utils.FileStorageUtils;
+import com.owncloud.android.utils.MimeTypeUtil;
+import com.owncloud.android.utils.ThemeUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.Vector;
+
+
+/**
+ * This Adapter populates a ListView with all files and folders in an ownCloud
+ * instance.
+ */
+public class FileListListAdapter extends BaseAdapter {
+
+    public static final int showFilenameColumnThreshold = 4;
+    private Context mContext;
+    private Vector<OCFile> mFilesAll = new Vector<>();
+    private Vector<OCFile> mFiles = new Vector<>();
+    private boolean mJustFolders;
+    private boolean mHideItemOptions;
+
+    private FileDataStorageManager mStorageManager;
+    private Account mAccount;
+    private ComponentsGetter mTransferServiceGetter;
+    private OCFileListFragmentInterface OCFileListFragmentInterface;
+
+    private FilesFilter mFilesFilter;
+    private OCFile currentDirectory;
+    private static final String TAG = FileListListAdapter.class.getSimpleName();
+
+    private ArrayList<ThumbnailsCacheManager.ThumbnailGenerationTask> asyncTasks = new ArrayList<>();
+
+    public FileListListAdapter(Context context, ComponentsGetter transferServiceGetter,
+                               OCFileListFragmentInterface OCFileListFragmentInterface, boolean argHideItemOptions) {
+
+        this.OCFileListFragmentInterface = OCFileListFragmentInterface;
+        mContext = context;
+        mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
+        mHideItemOptions = argHideItemOptions;
+
+        mTransferServiceGetter = transferServiceGetter;
+
+        // initialise thumbnails cache on background thread
+        new ThumbnailsCacheManager.InitDiskCacheTask().execute();
+    }
+
+    @Override
+    public boolean areAllItemsEnabled() {
+        return true;
+    }
+
+    @Override
+    public boolean isEnabled(int position) {
+        return true;
+    }
+
+    @Override
+    public int getCount() {
+        return mFiles != null ? mFiles.size() : 0;
+    }
+
+    @Override
+    public Object getItem(int position) {
+        if (mFiles == null || mFiles.size() <= position) {
+            return null;
+        }
+        return mFiles.get(position);
+    }
+
+    public void setFavoriteAttributeForItemID(String fileId, boolean favorite) {
+        for (int i = 0; i < mFiles.size(); i++) {
+            if (mFiles.get(i).getRemoteId().equals(fileId)) {
+                mFiles.get(i).setFavorite(favorite);
+                break;
+            }
+        }
+
+        for (int i = 0; i < mFilesAll.size(); i++) {
+            if (mFilesAll.get(i).getRemoteId().equals(fileId)) {
+                mFilesAll.get(i).setFavorite(favorite);
+                break;
+            }
+        }
+
+        new Handler(Looper.getMainLooper()).post(new Runnable() {
+            @Override
+            public void run() {
+                notifyDataSetChanged();
+            }
+        });
+    }
+
+    public void setEncryptionAttributeForItemID(String fileId, boolean encrypted) {
+        for (int i = 0; i < mFiles.size(); i++) {
+            if (mFiles.get(i).getRemoteId().equals(fileId)) {
+                mFiles.get(i).setEncrypted(encrypted);
+                break;
+            }
+        }
+
+        for (int i = 0; i < mFilesAll.size(); i++) {
+            if (mFilesAll.get(i).getRemoteId().equals(fileId)) {
+                mFilesAll.get(i).setEncrypted(encrypted);
+                break;
+            }
+        }
+
+        new Handler(Looper.getMainLooper()).post(new Runnable() {
+            @Override
+            public void run() {
+                notifyDataSetChanged();
+            }
+        });
+    }
+
+    @Override
+    public long getItemId(int position) {
+        if (mFiles == null || mFiles.size() <= position) {
+            return 0;
+        }
+        return mFiles.get(position).getFileId();
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return 0;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+
+        View view = convertView;
+        OCFile file = null;
+        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+        if (mFiles != null && mFiles.size() > position) {
+            file = mFiles.get(position);
+        }
+
+        // Find out which layout should be displayed
+        ViewType viewType;
+        if (parent instanceof GridView) {
+            if (file != null && (MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file))) {
+                viewType = ViewType.GRID_IMAGE;
+            } else {
+                viewType = ViewType.GRID_ITEM;
+            }
+        } else {
+            viewType = ViewType.LIST_ITEM;
+        }
+
+        // create view only if differs, otherwise reuse
+        if (convertView == null || convertView.getTag() != viewType) {
+            switch (viewType) {
+                case GRID_IMAGE:
+                    view = inflater.inflate(R.layout.grid_image, parent, false);
+                    view.setTag(ViewType.GRID_IMAGE);
+                    break;
+                case GRID_ITEM:
+                    view = inflater.inflate(R.layout.grid_item, parent, false);
+                    view.setTag(ViewType.GRID_ITEM);
+                    break;
+                case LIST_ITEM:
+                    view = inflater.inflate(R.layout.list_item, parent, false);
+                    view.setTag(ViewType.LIST_ITEM);
+                    break;
+            }
+        }
+
+        if (file != null) {
+            ImageView fileIcon = view.findViewById(R.id.thumbnail);
+
+            fileIcon.setTag(file.getFileId());
+            TextView fileName;
+            String name = file.getFileName();
+
+            switch (viewType) {
+                case LIST_ITEM:
+                    TextView fileSizeV = view.findViewById(R.id.file_size);
+                    TextView fileSizeSeparatorV = view.findViewById(R.id.file_separator);
+                    TextView lastModV = view.findViewById(R.id.last_mod);
+
+
+                    lastModV.setVisibility(View.VISIBLE);
+                    lastModV.setText(DisplayUtils.getRelativeTimestamp(mContext, file.getModificationTimestamp()));
+
+
+                    fileSizeSeparatorV.setVisibility(View.VISIBLE);
+                    fileSizeV.setVisibility(View.VISIBLE);
+                    fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
+
+                case GRID_ITEM:
+                    // filename
+                    fileName = view.findViewById(R.id.Filename);
+                    fileName.setText(name);
+
+                    if (OCFileListFragmentInterface.getColumnSize() > showFilenameColumnThreshold
+                            && viewType == ViewType.GRID_ITEM) {
+                        fileName.setVisibility(View.GONE);
+                    }
+
+                case GRID_IMAGE:
+
+                    // local state
+                    ImageView localStateView = view.findViewById(R.id.localFileIndicator);
+                    localStateView.bringToFront();
+                    FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();
+                    FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();
+                    OperationsServiceBinder opsBinder = mTransferServiceGetter.getOperationsServiceBinder();
+
+                    localStateView.setVisibility(View.INVISIBLE);   // default first
+
+                    if (opsBinder != null && opsBinder.isSynchronizing(mAccount, file)) {
+                        //synchronizing
+                        localStateView.setImageResource(R.drawable.ic_synchronizing);
+                        localStateView.setVisibility(View.VISIBLE);
+
+                    } else if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {
+                        // downloading
+                        localStateView.setImageResource(R.drawable.ic_synchronizing);
+                        localStateView.setVisibility(View.VISIBLE);
+
+                    } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {
+                        //uploading
+                        localStateView.setImageResource(R.drawable.ic_synchronizing);
+                        localStateView.setVisibility(View.VISIBLE);
+
+                    } else if (file.getEtagInConflict() != null) {
+                        // conflict
+                        localStateView.setImageResource(R.drawable.ic_synchronizing_error);
+                        localStateView.setVisibility(View.VISIBLE);
+
+                    } else if (file.isDown()) {
+                        localStateView.setImageResource(R.drawable.ic_synced);
+                        localStateView.setVisibility(View.VISIBLE);
+                    }
+
+                    break;
+            }
+
+            // For all Views
+            if (file.getIsFavorite()) {
+                view.findViewById(R.id.favorite_action).setVisibility(View.VISIBLE);
+            } else {
+                view.findViewById(R.id.favorite_action).setVisibility(View.GONE);
+            }
+
+            ImageView checkBoxV = view.findViewById(R.id.custom_checkbox);
+            view.setBackgroundColor(Color.WHITE);
+
+            AbsListView parentList = (AbsListView) parent;
+
+            if (parentList.getChoiceMode() != AbsListView.CHOICE_MODE_NONE && parentList.getCheckedItemCount() > 0) {
+                if (parentList.isItemChecked(position)) {
+                    view.setBackgroundColor(mContext.getResources().getColor(R.color.selected_item_background));
+                    checkBoxV.setImageDrawable(ThemeUtils.tintDrawable(R.drawable.ic_checkbox_marked,
+                            ThemeUtils.primaryColor()));
+                } else {
+                    view.setBackgroundColor(Color.WHITE);
+                    checkBoxV.setImageResource(R.drawable.ic_checkbox_blank_outline);
+                }
+                checkBoxV.setVisibility(View.VISIBLE);
+                hideShareIcon(view);
+                hideOverflowMenuIcon(view, viewType);
+            } else {
+                checkBoxV.setVisibility(View.GONE);
+
+                if (mHideItemOptions) {
+                    ImageView sharedIconView = view.findViewById(R.id.sharedIcon);
+                    sharedIconView.setVisibility(View.GONE);
+
+                    ImageView overflowIndicatorView = view.findViewById(R.id.overflow_menu);
+                    overflowIndicatorView.setVisibility(View.GONE);
+                } else {
+                    showShareIcon(view, file);
+                    showOverflowMenuIcon(view, file, viewType);
+                }
+            }
+
+            // this if-else is needed even though kept-in-sync icon is visible by default
+            // because android reuses views in listview
+            if (!file.isAvailableOffline()) {
+                view.findViewById(R.id.keptOfflineIcon).setVisibility(View.GONE);
+            } else {
+                view.findViewById(R.id.keptOfflineIcon).setVisibility(View.VISIBLE);
+            }
+
+
+            // No Folder
+            if (!file.isFolder()) {
+                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 && !file.needsUpdateThumbnail()) {
+
+                        if (MimeTypeUtil.isVideo(file)) {
+                            Bitmap withOverlay = ThumbnailsCacheManager.addVideoOverlay(thumbnail);
+                            fileIcon.setImageBitmap(withOverlay);
+                        } else {
+                            fileIcon.setImageBitmap(thumbnail);
+                        }
+                    } else {
+                        // generate new Thumbnail
+                        if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon)) {
+                            try {
+                                final ThumbnailsCacheManager.ThumbnailGenerationTask task =
+                                        new ThumbnailsCacheManager.ThumbnailGenerationTask(
+                                                fileIcon, mStorageManager, mAccount, asyncTasks);
+
+                                if (thumbnail == null) {
+                                    if (MimeTypeUtil.isVideo(file)) {
+                                        thumbnail = ThumbnailsCacheManager.mDefaultVideo;
+                                    } else {
+                                        thumbnail = ThumbnailsCacheManager.mDefaultImg;
+                                    }
+                                }
+                                final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
+                                        new ThumbnailsCacheManager.AsyncThumbnailDrawable(
+                                                mContext.getResources(),
+                                                thumbnail,
+                                                task
+                                        );
+                                fileIcon.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")) {
+                        fileIcon.setBackgroundColor(mContext.getResources().getColor(R.color.background_color));
+                    }
+
+
+                } else {
+                    fileIcon.setImageDrawable(
+                            MimeTypeUtil.getFileTypeIcon(file.getMimetype(), file.getFileName(), mAccount)
+                    );
+                }
+
+
+            } else {
+                // Folder
+                fileIcon.setImageDrawable(MimeTypeUtil.getFolderTypeIcon(file.isSharedWithMe() ||
+                        file.isSharedWithSharee(), file.isSharedViaLink(), file.isEncrypted(), file.getMountType()));
+            }
+        }
+        return view;
+    }
+
+    private void showShareIcon(View view, OCFile file) {
+        ImageView sharedIconV = view.findViewById(R.id.sharedIcon);
+        sharedIconV.setVisibility(View.VISIBLE);
+        if (file.isSharedWithSharee() || file.isSharedWithMe()) {
+            sharedIconV.setImageResource(R.drawable.shared_via_users);
+        } else if (file.isSharedViaLink()) {
+            sharedIconV.setImageResource(R.drawable.shared_via_link);
+        } else {
+            sharedIconV.setImageResource(R.drawable.ic_unshared);
+        }
+        sharedIconV.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                OCFileListFragmentInterface.onShareIconClick(file);
+            }
+        });
+    }
+
+    private void hideShareIcon(View view) {
+        view.findViewById(R.id.sharedIcon).setVisibility(View.GONE);
+    }
+
+    private void showOverflowMenuIcon(View view, OCFile file, ViewType viewType) {
+        if (ViewType.LIST_ITEM.equals(viewType)) {
+            ImageView overflowIndicatorV = view.findViewById(R.id.overflow_menu);
+            overflowIndicatorV.setVisibility(View.VISIBLE);
+            overflowIndicatorV.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    OCFileListFragmentInterface.onOverflowIconClick(v, file);
+                }
+            });
+        }
+    }
+
+    private void hideOverflowMenuIcon(View view, ViewType viewType) {
+        if (ViewType.LIST_ITEM.equals(viewType)) {
+            ImageView overflowIndicatorV = view.findViewById(R.id.overflow_menu);
+            overflowIndicatorV.setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    public int getViewTypeCount() {
+        return 1;
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return true;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return (mFiles == null || mFiles.isEmpty());
+    }
+
+    /**
+     * Change the adapted directory for a new one
+     *
+     * @param directory             New folder to adapt. Can be NULL, meaning
+     *                              "no content to adapt".
+     * @param updatedStorageManager Optional updated storage manager; used to replace
+     *                              mStorageManager if is different (and not NULL)
+     */
+    public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager
+            , boolean onlyOnDevice) {
+        if (updatedStorageManager != null && !updatedStorageManager.equals(mStorageManager)) {
+            mStorageManager = updatedStorageManager;
+            mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
+        }
+        if (mStorageManager != null) {
+            mFiles = mStorageManager.getFolderContent(directory, onlyOnDevice);
+
+            if (mJustFolders) {
+                mFiles = getFolders(mFiles);
+            }
+            if (!PreferenceManager.showHiddenFilesEnabled(mContext)) {
+                mFiles = filterHiddenFiles(mFiles);
+            }
+            FileSortOrder sortOrder = PreferenceManager.getSortOrder(mContext, directory);
+            mFiles = sortOrder.sortCloudFiles(mFiles);
+            mFilesAll.clear();
+            mFilesAll.addAll(mFiles);
+
+            currentDirectory = directory;
+        } else {
+            mFiles.clear();
+            mFilesAll.clear();
+        }
+
+        notifyDataSetChanged();
+    }
+
+    private void searchForLocalFileInDefaultPath(OCFile file) {
+        if (file.getStoragePath() == null && !file.isFolder()) {
+            File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+            if (f.exists()) {
+                file.setStoragePath(f.getAbsolutePath());
+                file.setLastSyncDateForData(f.lastModified());
+            }
+        }
+    }
+
+    public void setData(ArrayList<Object> objects, ExtendedListFragment.SearchType searchType,
+                        FileDataStorageManager storageManager, OCFile folder) {
+        if (storageManager != null && mStorageManager == null) {
+            mStorageManager = storageManager;
+        }
+        mFiles.clear();
+
+        // early exit
+        if (objects.size() > 0 && mStorageManager != null) {
+            if (searchType.equals(ExtendedListFragment.SearchType.SHARED_FILTER)) {
+                parseShares(objects);
+            } else {
+                parseVirtuals(objects, searchType);
+            }
+        }
+
+        if (!searchType.equals(ExtendedListFragment.SearchType.PHOTO_SEARCH) &&
+                !searchType.equals(ExtendedListFragment.SearchType.PHOTOS_SEARCH_FILTER) &&
+                !searchType.equals(ExtendedListFragment.SearchType.RECENTLY_MODIFIED_SEARCH) &&
+                !searchType.equals(ExtendedListFragment.SearchType.RECENTLY_MODIFIED_SEARCH_FILTER)) {
+            FileSortOrder sortOrder = PreferenceManager.getSortOrder(mContext, folder);
+            mFiles = sortOrder.sortCloudFiles(mFiles);
+        } else {
+            mFiles = FileStorageUtils.sortOcFolderDescDateModified(mFiles);
+        }
+
+        mFilesAll = new Vector<>();
+        mFilesAll.addAll(mFiles);
+
+        new Handler(Looper.getMainLooper()).post(new Runnable() {
+            @Override
+            public void run() {
+                notifyDataSetChanged();
+                OCFileListFragmentInterface.finishedFiltering();
+            }
+        });
+    }
+
+    private void parseShares(ArrayList<Object> objects) {
+        ArrayList<OCShare> shares = new ArrayList<>();
+        for (int i = 0; i < objects.size(); i++) {
+            // check type before cast as of long running data fetch it is possible that old result is filled
+            if (objects.get(i) instanceof OCShare) {
+                OCShare ocShare = (OCShare) objects.get(i);
+
+                shares.add(ocShare);
+
+                // get ocFile from Server to have an up-to-date copy
+                ReadRemoteFileOperation operation = new ReadRemoteFileOperation(ocShare.getPath());
+                RemoteOperationResult result = operation.execute(mAccount, mContext);
+                if (result.isSuccess()) {
+                    OCFile file = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
+                    searchForLocalFileInDefaultPath(file);
+                    file = mStorageManager.saveFileWithParent(file, mContext);
+
+                    ShareType newShareType = ocShare.getShareType();
+                    if (newShareType == ShareType.PUBLIC_LINK) {
+                        file.setShareViaLink(true);
+                    } else if (newShareType == ShareType.USER || newShareType == ShareType.GROUP ||
+                                    newShareType == ShareType.EMAIL || newShareType == ShareType.FEDERATED) {
+                        file.setShareWithSharee(true);
+                    }
+
+                    mStorageManager.saveFile(file);
+
+                    if (!mFiles.contains(file)) {
+                        mFiles.add(file);
+                    }
+                } else {
+                    Log_OC.e(TAG, "Error in getting prop for file: " + ocShare.getPath());
+                }
+            }
+        }
+        mStorageManager.saveShares(shares);
+    }
+
+    private void parseVirtuals(ArrayList<Object> objects, ExtendedListFragment.SearchType searchType) {
+        VirtualFolderType type;
+        boolean onlyImages = false;
+        switch (searchType) {
+            case FAVORITE_SEARCH:
+                type = VirtualFolderType.FAVORITE;
+                break;
+            case PHOTO_SEARCH:
+                type = VirtualFolderType.PHOTOS;
+                onlyImages = true;
+                break;
+            default:
+                type = VirtualFolderType.NONE;
+                break;
+        }
+
+        mStorageManager.deleteVirtuals(type);
+
+        ArrayList<ContentValues> contentValues = new ArrayList<>();
+
+        for (int i = 0; i < objects.size(); i++) {
+            OCFile ocFile = FileStorageUtils.fillOCFile((RemoteFile) objects.get(i));
+            searchForLocalFileInDefaultPath(ocFile);
+
+            try {
+                ocFile = mStorageManager.saveFileWithParent(ocFile, mContext);
+
+                if (!onlyImages || MimeTypeUtil.isImage(ocFile)) {
+                    mFiles.add(ocFile);
+                }
+
+                ContentValues cv = new ContentValues();
+                cv.put(ProviderMeta.ProviderTableMeta.VIRTUAL_TYPE, type.toString());
+                cv.put(ProviderMeta.ProviderTableMeta.VIRTUAL_OCFILE_ID, ocFile.getFileId());
+
+                contentValues.add(cv);
+            } catch (RemoteOperationFailedException e) {
+                Log_OC.e(TAG, "Error saving file with parent" + e.getMessage(),e);
+            }
+        }
+
+        mStorageManager.saveVirtuals(type, contentValues);
+    }
+
+    /**
+     * Filter for getting only the folders
+     *
+     * @param files Collection of files to filter
+     * @return Folders in the input
+     */
+    public Vector<OCFile> getFolders(Vector<OCFile> files) {
+        Vector<OCFile> ret = new Vector<>();
+        OCFile current;
+        for (int i = 0; i < files.size(); i++) {
+            current = files.get(i);
+            if (current.isFolder()) {
+                ret.add(current);
+            }
+        }
+        return ret;
+    }
+
+
+    public void setSortOrder(OCFile folder, FileSortOrder sortOrder) {
+        PreferenceManager.setSortOrder(mContext, folder, sortOrder);
+        mFiles = sortOrder.sortCloudFiles(mFiles);
+        notifyDataSetChanged();
+    }
+
+
+    public ArrayList<OCFile> getCheckedItems(AbsListView parentList) {
+        SparseBooleanArray checkedPositions = parentList.getCheckedItemPositions();
+        ArrayList<OCFile> files = new ArrayList<>();
+        Object item;
+        for (int i = 0; i < checkedPositions.size(); i++) {
+            if (checkedPositions.valueAt(i)) {
+                item = getItem(checkedPositions.keyAt(i));
+                if (item != null) {
+                    files.add((OCFile) item);
+                }
+            }
+        }
+        return files;
+    }
+
+    public Vector<OCFile> getFiles() {
+        return mFiles;
+    }
+
+    public Filter getFilter() {
+        if (mFilesFilter == null) {
+            mFilesFilter = new FilesFilter();
+        }
+        return mFilesFilter;
+    }
+
+    private class FilesFilter extends Filter {
+
+        @Override
+        protected FilterResults performFiltering(CharSequence constraint) {
+            FilterResults results = new FilterResults();
+            Vector<OCFile> filteredFiles = new Vector<>();
+
+            if (!TextUtils.isEmpty(constraint)) {
+                for (int i = 0; i < mFilesAll.size(); i++) {
+                    OCFile currentFile = mFilesAll.get(i);
+                    if (currentFile.getParentRemotePath().equals(currentDirectory.getRemotePath()) &&
+                            currentFile.getFileName().toLowerCase(Locale.getDefault()).contains(
+                                    constraint.toString().toLowerCase(Locale.getDefault())) && 
+                            !filteredFiles.contains(currentFile)) {
+                        filteredFiles.add(currentFile);
+                    }
+                }
+            }
+
+            results.values = filteredFiles;
+            results.count = filteredFiles.size();
+
+            return results;
+        }
+
+        @Override
+        protected void publishResults(CharSequence constraint, Filter.FilterResults results) {
+            Vector<OCFile> ocFiles = (Vector<OCFile>) results.values;
+            mFiles.clear();
+            if (ocFiles != null && ocFiles.size() > 0) {
+                mFiles.addAll(ocFiles);
+                if (!PreferenceManager.showHiddenFilesEnabled(mContext)) {
+                    mFiles = filterHiddenFiles(mFiles);
+                }
+                FileSortOrder sortOrder = PreferenceManager.getSortOrder(mContext, null);
+                mFiles = sortOrder.sortCloudFiles(mFiles);
+            }
+
+            notifyDataSetChanged();
+            OCFileListFragmentInterface.finishedFiltering();
+
+        }
+    }
+
+
+    /**
+     * Filter for hidden files
+     *
+     * @param files Collection of files to filter
+     * @return Non-hidden files
+     */
+    public Vector<OCFile> filterHiddenFiles(Vector<OCFile> files) {
+        Vector<OCFile> ret = new Vector<>();
+        OCFile current;
+        for (int i = 0; i < files.size(); i++) {
+            current = files.get(i);
+            if (!current.isHidden() && !ret.contains(current)) {
+                ret.add(current);
+            }
+        }
+        return ret;
+    }
+
+    public void cancelAllPendingTasks() {
+        for (ThumbnailsCacheManager.ThumbnailGenerationTask task : asyncTasks) {
+            if (task != null) {
+                task.cancel(true);
+                if (task.getGetMethod() != null) {
+                    Log_OC.d(TAG, "cancel: abort get method directly");
+                    task.getGetMethod().abort();
+                }
+            }
+        }
+
+        asyncTasks.clear();
+    }
+
+}

+ 406 - 211
src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java

@@ -1,34 +1,36 @@
-/**
- *   ownCloud Android client application
- *
- *   @author David A. Velasco
- *   Copyright (C) 2011  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/>.
+/*
+ * ownCloud Android client application
  *
+ * @author David A. Velasco
+ * Copyright (C) 2011  Bartek Przybylski
+ * Copyright (C) 2015 ownCloud Inc.
+ * <p>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ * <p>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 package com.owncloud.android.ui.adapter;
 
 import android.content.Context;
+import android.content.res.Resources;
+import android.database.DataSetObserver;
 import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
-import android.widget.BaseAdapter;
-import android.widget.GridView;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.TextView;
 
@@ -36,37 +38,47 @@ import com.owncloud.android.R;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
 import com.owncloud.android.db.PreferenceManager;
 import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.ui.interfaces.LocalFileListFragmentInterface;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.FileSortOrder;
 import com.owncloud.android.utils.MimeTypeUtil;
+import com.owncloud.android.utils.ThemeUtils;
 
 import java.io.File;
-import java.io.FileFilter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.List;
+import java.util.HashSet;
 import java.util.Locale;
-import java.util.Vector;
+import java.util.Set;
 
 /**
- * This Adapter populates a ListView with all files and directories contained
- * in a local directory
+ * This Adapter populates a RecycylerView with all files and directories contained in a local directory
  */
-public class LocalFileListAdapter extends BaseAdapter implements FilterableListAdapter {
+public class LocalFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements FilterableListAdapter {
 
     private static final String TAG = LocalFileListAdapter.class.getSimpleName();
 
+    private static final int showFilenameColumnThreshold = 4;
     private Context mContext;
-    private File[] mFiles = null;
-    private Vector<File> mFilesAll = new Vector<>();
+    private ArrayList<File> mFiles = new ArrayList<>();
+    private ArrayList<File> mFilesAll = new ArrayList<>();
     private boolean mLocalFolderPicker;
+    private boolean gridView = false;
+    private LocalFileListFragmentInterface localFileListFragmentInterface;
+    private Set<File> checkedFiles;
 
-    public LocalFileListAdapter(boolean localFolderPickerMode, File directory, Context context) {
+    private static final int VIEWTYPE_ITEM = 0;
+    private static final int VIEWTYPE_FOOTER = 1;
+
+    public LocalFileListAdapter(boolean localFolderPickerMode, File directory,
+                                LocalFileListFragmentInterface localFileListFragmentInterface, Context context) {
         mContext = context;
         mLocalFolderPicker = localFolderPickerMode;
         swapDirectory(directory);
+        this.localFileListFragmentInterface = localFileListFragmentInterface;
+        checkedFiles = new HashSet<>();
     }
 
     @Override
@@ -76,179 +88,267 @@ public class LocalFileListAdapter extends BaseAdapter implements FilterableListA
 
     @Override
     public boolean isEnabled(int position) {
-        return true;
+        // footer is not enabled
+        return position < getItemCount();
+    }
+
+    @Override
+    public void registerDataSetObserver(DataSetObserver observer) {
+        // not needed
+    }
+
+    public void unregisterDataSetObserver(DataSetObserver observer) {
+        // not needed
+    }
+
+    @Override
+    public int getItemCount() {
+        return mFiles.size() + 1;
     }
 
     @Override
     public int getCount() {
-        return mFiles != null ? mFiles.length : 0;
+        return mFiles.size() + 1;
     }
 
     @Override
-    public Object getItem(int position) {
-        if (mFiles == null || mFiles.length <= position) {
-            return null;
+    public File getItem(int position) {
+        return mFiles.get(position);
+    }
+
+    public boolean isCheckedFile(File file) {
+        return checkedFiles.contains(file);
+    }
+
+    public void removeCheckedFile(File file) {
+        checkedFiles.remove(file);
+    }
+
+    public void addCheckedFile(File file) {
+        checkedFiles.add(file);
+    }
+
+    public void addAllFilesToCheckedFiles() {
+        checkedFiles.addAll(mFiles);
+    }
+
+    public void removeAllFilesFromCheckedFiles() {
+        checkedFiles.clear();
+    }
+
+    public int getItemPosition(File file) {
+        return mFilesAll.indexOf(file);
+    }
+
+    public String[] getCheckedFilesPath() {
+        ArrayList<String> result = new ArrayList<>();
+
+        for (File file : checkedFiles) {
+            result.add(file.getAbsolutePath());
         }
-        return mFiles[position];
+
+        Log_OC.d(TAG, "Returning " + result.size() + " selected files");
+
+        return result.toArray(new String[result.size()]);
     }
 
     @Override
     public long getItemId(int position) {
-        return mFiles != null && mFiles.length <= position ? position : -1;
+        return mFiles.size() <= position ? position : -1;
     }
 
     @Override
-    public int getItemViewType(int position) {
-        return 0;
-    }
+    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+        if (holder instanceof LocalFileListFooterViewHolder) {
+            ((LocalFileListFooterViewHolder) holder).footerText.setText(getFooterText());
+        } else {
+            File file = null;
+            if (mFiles.size() > position && mFiles.get(position) != null) {
+                file = mFiles.get(position);
+            }
 
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        View view = convertView;
-        File file = null;
-        boolean isGridView = true;
-        LayoutInflater inflater = (LayoutInflater) mContext
-                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            if (file != null) {
+                LocalFileListGridViewHolder gridViewHolder = (LocalFileListGridViewHolder) holder;
 
-        if (mFiles != null && mFiles.length > position && mFiles[position] != null) {
-            file = mFiles[position];
-        }
+                // checkbox
+                if (isCheckedFile(file)) {
+                    gridViewHolder.itemLayout.setBackgroundColor(mContext.getResources()
+                            .getColor(R.color.selected_item_background));
+                    gridViewHolder.checkbox.setImageDrawable(ThemeUtils.tintDrawable(R.drawable.ic_checkbox_marked,
+                            ThemeUtils.primaryColor()));
+                } else {
+                    gridViewHolder.itemLayout.setBackgroundColor(Color.WHITE);
+                    gridViewHolder.checkbox.setImageResource(R.drawable.ic_checkbox_blank_outline);
+                }
+
+                gridViewHolder.thumbnail.setTag(file.hashCode());
+                setThumbnail(file, gridViewHolder.thumbnail);
 
-        if (file != null) {
-            // Find out which layout should be displayed
-            ViewType viewType;
-            if (parent instanceof GridView) {
-                String mimeType = MimeTypeUtil.getBestMimeTypeByFilename(file.getName());
-                if (MimeTypeUtil.isImage(mimeType) || MimeTypeUtil.isVideo(mimeType)) {
-                    viewType = ViewType.GRID_IMAGE;
+                if (file.isDirectory()) {
+                    gridViewHolder.checkbox.setVisibility(View.GONE);
                 } else {
-                    viewType = ViewType.GRID_ITEM;
+                    gridViewHolder.checkbox.setVisibility(View.VISIBLE);
                 }
-            } else {
-                viewType = ViewType.LIST_ITEM;
-                isGridView = false;
-            }
 
-            // create view only if differs, otherwise reuse
-            if (convertView == null || convertView.getTag() != viewType) {
-                switch (viewType) {
-                    case GRID_IMAGE:
-                        view = inflater.inflate(R.layout.grid_image, parent, false);
-                        view.setTag(ViewType.GRID_IMAGE);
-                        break;
-                    case GRID_ITEM:
-                        view = inflater.inflate(R.layout.grid_item, parent, false);
-                        view.setTag(ViewType.GRID_ITEM);
-                        break;
-                    case LIST_ITEM:
-                        view = inflater.inflate(R.layout.list_item, parent, false);
-                        view.setTag(ViewType.LIST_ITEM);
-                        break;
+                File finalFile = file;
+                gridViewHolder.itemLayout.setOnClickListener(v -> localFileListFragmentInterface
+                        .onItemClicked(finalFile));
+
+                gridViewHolder.fileName.setText(file.getName());
+
+                if (holder instanceof LocalFileListItemViewHolder) {
+                    LocalFileListItemViewHolder itemViewHolder = (LocalFileListItemViewHolder) holder;
+
+                    if (file.isDirectory()) {
+                        itemViewHolder.fileSize.setVisibility(View.GONE);
+                        itemViewHolder.fileSeparator.setVisibility(View.GONE);
+                    } else {
+                        itemViewHolder.fileSize.setVisibility(View.VISIBLE);
+                        itemViewHolder.fileSeparator.setVisibility(View.VISIBLE);
+                        itemViewHolder.fileSize.setText(DisplayUtils.bytesToHumanReadable(file.length()));
+                    }
+                    itemViewHolder.lastModification.setText(DisplayUtils.getRelativeTimestamp(mContext,
+                            file.lastModified()));
                 }
-            }
 
-            if(!ViewType.GRID_IMAGE.equals(viewType)) {
-                TextView fileName = view.findViewById(R.id.Filename);
-                String name = file.getName();
-                fileName.setText(name);
+                if (gridView && (MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file) ||
+                        localFileListFragmentInterface.getColumnSize() > showFilenameColumnThreshold)) {
+                    gridViewHolder.fileName.setVisibility(View.GONE);
+                } else {
+                    gridViewHolder.fileName.setVisibility(View.VISIBLE);
+                }
             }
+        }
+    }
 
-            ImageView fileIcon = view.findViewById(R.id.thumbnail);
+    private void setThumbnail(File file, ImageView thumbnailView) {
+        if (file.isDirectory()) {
+            thumbnailView.setImageDrawable(MimeTypeUtil.getDefaultFolderIcon());
+        } else {
+            thumbnailView.setImageResource(R.drawable.file);
 
             /** Cancellation needs do be checked and done before changing the drawable in fileIcon, or
              * {@link ThumbnailsCacheManager#cancelPotentialThumbnailWork} will NEVER cancel any task.
              **/
-            boolean allowedToCreateNewThumbnail = (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon));
+            boolean allowedToCreateNewThumbnail = (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailView));
 
-            if (!file.isDirectory()) {
-                fileIcon.setImageResource(R.drawable.file);
-            } else {
-                fileIcon.setImageDrawable(MimeTypeUtil.getDefaultFolderIcon());
-            }
-            fileIcon.setTag(file.hashCode());
 
-            ImageView checkBoxV = view.findViewById(R.id.custom_checkbox);
-            TextView fileSizeV = view.findViewById(R.id.file_size);
-            TextView fileSizeSeparatorV = view.findViewById(R.id.file_separator);
-            if (!isGridView) {
-                TextView lastModV = view.findViewById(R.id.last_mod);
-                lastModV.setVisibility(View.VISIBLE);
-                lastModV.setText(DisplayUtils.getRelativeTimestamp(mContext, file.lastModified()));
-                view.findViewById(R.id.overflow_menu).setVisibility(View.GONE);
-            }
+            // get Thumbnail if file is image
+            if (MimeTypeUtil.isImage(file)) {
+                // Thumbnail in Cache?
+                Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
+                        ThumbnailsCacheManager.PREFIX_THUMBNAIL + String.valueOf(file.hashCode())
+                );
+                if (thumbnail != null) {
+                    thumbnailView.setImageBitmap(thumbnail);
+                } else {
 
-            if (!file.isDirectory()) {
-                if (!isGridView) {
-                    fileSizeSeparatorV.setVisibility(View.VISIBLE);
-                    fileSizeV.setVisibility(View.VISIBLE);
-                    fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.length()));
+                    // generate new Thumbnail
+                    if (allowedToCreateNewThumbnail) {
+                        final ThumbnailsCacheManager.ThumbnailGenerationTask task =
+                                new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView);
+                        if (MimeTypeUtil.isVideo(file)) {
+                            thumbnail = ThumbnailsCacheManager.mDefaultVideo;
+                        } else {
+                            thumbnail = ThumbnailsCacheManager.mDefaultImg;
+                        }
+                        final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
+                                new ThumbnailsCacheManager.AsyncThumbnailDrawable(
+                                        mContext.getResources(),
+                                        thumbnail,
+                                        task
+                                );
+                        thumbnailView.setImageDrawable(asyncDrawable);
+                        task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, null));
+                        Log_OC.v(TAG, "Executing task to generate a new thumbnail");
+
+                    } // else, already being generated, don't restart it
                 }
+            } else {
+                thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(null, file.getName(), null)
+                );
+            }
+        }
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        if (position == mFiles.size()) {
+            return VIEWTYPE_FOOTER;
+        } else {
+            return VIEWTYPE_ITEM;
+        }
+    }
 
-                AbsListView parentList = (AbsListView) parent;
-                if (parentList.getChoiceMode() == ListView.CHOICE_MODE_NONE) {
-                    checkBoxV.setVisibility(View.GONE);
+    @Override
+    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        switch (viewType) {
+            default:
+            case VIEWTYPE_ITEM:
+                if (gridView) {
+                    View itemView = LayoutInflater.from(mContext).inflate(R.layout.grid_item, parent, false);
+                    return new LocalFileListGridViewHolder(itemView);
                 } else {
-                    if (parentList.isItemChecked(position)) {
-                        checkBoxV.setImageResource(R.drawable.ic_checkbox_marked);
-                    } else {
-                        checkBoxV.setImageResource(R.drawable.ic_checkbox_blank_outline);
-                    }
-                    checkBoxV.setVisibility(View.VISIBLE);
+                    View itemView = LayoutInflater.from(mContext).inflate(R.layout.list_item, parent, false);
+                    return new LocalFileListItemViewHolder(itemView);
                 }
-                
-             // get Thumbnail if file is image
-                if (MimeTypeUtil.isImage(file)){
-                // Thumbnail in Cache?
-                    Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
-                            ThumbnailsCacheManager.PREFIX_THUMBNAIL + String.valueOf(file.hashCode())
-                    );
-                    if (thumbnail != null){
-                        fileIcon.setImageBitmap(thumbnail);
-                    } else {
 
-                        // generate new Thumbnail
-                        if (allowedToCreateNewThumbnail) {
-                            final ThumbnailsCacheManager.ThumbnailGenerationTask task =
-                                    new ThumbnailsCacheManager.ThumbnailGenerationTask(fileIcon);
-                            if (MimeTypeUtil.isVideo(file)) {
-                                thumbnail = ThumbnailsCacheManager.mDefaultVideo;
-                            } else {
-                                thumbnail = ThumbnailsCacheManager.mDefaultImg;
-                            }
-                            final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
-                                    new ThumbnailsCacheManager.AsyncThumbnailDrawable(
-                                            mContext.getResources(),
-                                            thumbnail,
-                                            task
-                                    );
-                            fileIcon.setImageDrawable(asyncDrawable);
-                            task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, null));
-                            Log_OC.v(TAG, "Executing task to generate a new thumbnail");
-
-                        } // else, already being generated, don't restart it
-                    }
-                } else {
-                    fileIcon.setImageDrawable(
-                            MimeTypeUtil.getFileTypeIcon(null, file.getName(), null)
-                    );
-                }  
+            case VIEWTYPE_FOOTER:
+                View itemView = LayoutInflater.from(mContext).inflate(R.layout.list_footer, parent, false);
+                return new LocalFileListFooterViewHolder(itemView);
+        }
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        View view = convertView;
+        File file = null;
+
+        boolean isGridView = true;
 
+        ImageView checkBoxV = view.findViewById(R.id.custom_checkbox);
+        TextView fileSizeV = view.findViewById(R.id.file_size);
+        TextView fileSizeSeparatorV = view.findViewById(R.id.file_separator);
+        if (!isGridView) {
+            TextView lastModV = view.findViewById(R.id.last_mod);
+            lastModV.setVisibility(View.VISIBLE);
+            lastModV.setText(DisplayUtils.getRelativeTimestamp(mContext, file.lastModified()));
+            view.findViewById(R.id.overflow_menu).setVisibility(View.GONE);
+        }
+
+        if (!file.isDirectory()) {
+            if (!isGridView) {
+                fileSizeSeparatorV.setVisibility(View.VISIBLE);
+                fileSizeV.setVisibility(View.VISIBLE);
+                fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.length()));
+            }
+
+            AbsListView parentList = (AbsListView) parent;
+            if (parentList.getChoiceMode() == ListView.CHOICE_MODE_NONE) {
+                checkBoxV.setVisibility(View.GONE);
             } else {
-                if (!isGridView) {
-                    fileSizeSeparatorV.setVisibility(View.GONE);
-                    fileSizeV.setVisibility(View.GONE);
+                if (parentList.isItemChecked(position)) {
+                    checkBoxV.setImageResource(R.drawable.ic_checkbox_marked);
+                } else {
+                    checkBoxV.setImageResource(R.drawable.ic_checkbox_blank_outline);
                 }
-                checkBoxV.setVisibility(View.GONE);
+                checkBoxV.setVisibility(View.VISIBLE);
             }
-
-            // not GONE; the alignment changes; ugly way to keep it
-            view.findViewById(R.id.localFileIndicator).setVisibility(View.INVISIBLE);   
-            view.findViewById(R.id.keptOfflineIcon).setVisibility(View.GONE);
-            view.findViewById(R.id.favorite_action).setVisibility(View.GONE);
-            
-            view.findViewById(R.id.sharedIcon).setVisibility(View.GONE);
+        } else {
+            if (!isGridView) {
+                fileSizeSeparatorV.setVisibility(View.GONE);
+                fileSizeV.setVisibility(View.GONE);
+            }
+            checkBoxV.setVisibility(View.GONE);
         }
 
+        // not GONE; the alignment changes; ugly way to keep it
+        view.findViewById(R.id.localFileIndicator).setVisibility(View.INVISIBLE);
+        view.findViewById(R.id.keptOfflineIcon).setVisibility(View.GONE);
+        view.findViewById(R.id.favorite_action).setVisibility(View.GONE);
+
+        view.findViewById(R.id.sharedIcon).setVisibility(View.GONE);
+
         return view;
     }
 
@@ -257,14 +357,9 @@ public class LocalFileListAdapter extends BaseAdapter implements FilterableListA
         return 1;
     }
 
-    @Override
-    public boolean hasStableIds() {
-        return false;
-    }
-
     @Override
     public boolean isEmpty() {
-        return (mFiles == null || mFiles.length == 0);
+        return mFiles.isEmpty();
     }
 
     /**
@@ -272,42 +367,49 @@ public class LocalFileListAdapter extends BaseAdapter implements FilterableListA
      * @param directory     New file to adapt. Can be NULL, meaning "no content to adapt".
      */
     public void swapDirectory(final File directory) {
-        if(mLocalFolderPicker) {
-            mFiles = (directory != null ? getFolders(directory) : null);
+        if (mLocalFolderPicker) {
+            if (directory == null) {
+                mFiles.clear();
+            } else {
+                mFiles = getFolders(directory);
+            }
         } else {
-            mFiles = (directory != null ? directory.listFiles() : null);
+            if (directory == null) {
+                mFiles.clear();
+            } else {
+                mFiles = getFiles(directory);
+            }
         }
-        if (mFiles != null) {
-            Arrays.sort(mFiles, new Comparator<File>() {
-                @Override
-                public int compare(File lhs, File rhs) {
-                    if (lhs.isDirectory() && !rhs.isDirectory()) {
-                        return -1;
-                    } else if (!lhs.isDirectory() && rhs.isDirectory()) {
-                        return 1;
-                    }
-                    return compareNames(lhs, rhs);
-                }
-            
-                private int compareNames(File lhs, File rhs) {
-                    return lhs.getName().toLowerCase(Locale.getDefault()).compareTo(
-                            rhs.getName().toLowerCase(Locale.getDefault()));                
-                }
-            });
 
-            FileSortOrder sortOrder = PreferenceManager.getSortOrder(mContext, null);
-            mFiles = sortOrder.sortLocalFiles(mFiles);
+        Collections.sort(mFiles, new Comparator<File>() {
+            @Override
+            public int compare(File lhs, File rhs) {
+                if (lhs.isDirectory() && !rhs.isDirectory()) {
+                    return -1;
+                } else if (!lhs.isDirectory() && rhs.isDirectory()) {
+                    return 1;
+                }
+                return compareNames(lhs, rhs);
+            }
 
-            // Fetch preferences for showing hidden files
-            boolean showHiddenFiles = PreferenceManager.showHiddenFilesEnabled(mContext);
-            if (!showHiddenFiles) {
-                mFiles = filterHiddenFiles(mFiles);
+            private int compareNames(File lhs, File rhs) {
+                return lhs.getName().toLowerCase(Locale.getDefault()).compareTo(
+                        rhs.getName().toLowerCase(Locale.getDefault()));
             }
+        });
 
-            mFilesAll.clear();
+        FileSortOrder sortOrder = PreferenceManager.getSortOrder(mContext, null);
+        mFiles = sortOrder.sortLocalFiles(mFiles);
 
-            Collections.addAll(mFilesAll, mFiles);
+        // Fetch preferences for showing hidden files
+        boolean showHiddenFiles = PreferenceManager.showHiddenFilesEnabled(mContext);
+        if (!showHiddenFiles) {
+            mFiles = filterHiddenFiles(mFiles);
         }
+
+        mFilesAll.clear();
+        mFilesAll.addAll(mFiles);
+
         notifyDataSetChanged();
     }
 
@@ -317,27 +419,38 @@ public class LocalFileListAdapter extends BaseAdapter implements FilterableListA
         notifyDataSetChanged();
     }
 
-    private File[] getFolders(final File directory) {
-        return directory.listFiles(new FileFilter() {
-            @Override
-            public boolean accept(File file) {
-                return file.isDirectory();
-            }
-        });
+    private ArrayList<File> getFolders(final File directory) {
+        File[] folders = directory.listFiles(File::isFile);
+
+        if (folders != null && folders.length > 0) {
+            return new ArrayList<>(Arrays.asList(folders));
+        } else {
+            return new ArrayList<>();
+        }
+    }
+
+    private ArrayList<File> getFiles(final File directory) {
+        File[] files = directory.listFiles();
+
+        if (files != null && files.length > 0) {
+            return new ArrayList<>(Arrays.asList(files));
+        } else {
+            return new ArrayList<>();
+        }
     }
 
-    public void filter(String text){
-        if(text.isEmpty()){
-            mFiles = mFilesAll.toArray(new File[1]);
+    public void filter(String text) {
+        if (text.isEmpty()) {
+            mFiles = mFilesAll;
         } else {
             ArrayList<File> result = new ArrayList<>();
             text = text.toLowerCase(Locale.getDefault());
-            for (File file: mFilesAll) {
+            for (File file : mFilesAll) {
                 if (file.getName().toLowerCase(Locale.getDefault()).contains(text)) {
                     result.add(file);
                 }
             }
-            mFiles = result.toArray(new File[1]);
+            mFiles = result;
         }
         notifyDataSetChanged();
     }
@@ -345,16 +458,98 @@ public class LocalFileListAdapter extends BaseAdapter implements FilterableListA
     /**
      * Filter for hidden files
      *
-     * @param files             Array of files to filter
-     * @return                  Non-hidden files as an array
+     * @param files             ArrayList of files to filter
+     * @return Non-hidden files
      */
-    public File[] filterHiddenFiles(File[] files) {
-        List<File> ret = new ArrayList<>();
-        for (File file: files) {
+    public ArrayList<File> filterHiddenFiles(ArrayList<File> files) {
+        ArrayList<File> ret = new ArrayList<>();
+        
+        for (File file : files) {
             if (!file.isHidden()) {
                 ret.add(file);
             }
         }
-        return ret.toArray(new File[ret.size()]);
+        return ret;
+    }
+
+    private String getFooterText() {
+        int filesCount = 0;
+        int foldersCount = 0;
+
+        for (File file : mFiles) {
+            if (file.isDirectory()) {
+                foldersCount++;
+            } else {
+                if (!file.isHidden()) {
+                    filesCount++;
+                }
+            }
+        }
+
+        return generateFooterText(filesCount, foldersCount);
+    }
+
+    private String generateFooterText(int filesCount, int foldersCount) {
+        String output;
+        Resources resources = mContext.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;
+    }
+
+    static class LocalFileListItemViewHolder extends LocalFileListGridViewHolder {
+        private final TextView fileSize;
+        private final TextView lastModification;
+        private final TextView fileSeparator;
+
+        private LocalFileListItemViewHolder(View itemView) {
+            super(itemView);
+
+            fileSize = itemView.findViewById(R.id.file_size);
+            fileSeparator = itemView.findViewById(R.id.file_separator);
+            lastModification = itemView.findViewById(R.id.last_mod);
+        }
+    }
+
+    static class LocalFileListGridViewHolder extends RecyclerView.ViewHolder {
+        private final ImageView thumbnail;
+        private final TextView fileName;
+        private final ImageView checkbox;
+        private final LinearLayout itemLayout;
+
+        private LocalFileListGridViewHolder(View itemView) {
+            super(itemView);
+
+            thumbnail = itemView.findViewById(R.id.thumbnail);
+            fileName = itemView.findViewById(R.id.Filename);
+            checkbox = itemView.findViewById(R.id.custom_checkbox);
+            itemLayout = itemView.findViewById(R.id.ListItemLayout);
+
+            itemView.findViewById(R.id.overflow_menu).setVisibility(View.GONE);
+            itemView.findViewById(R.id.sharedIcon).setVisibility(View.GONE);
+            itemView.findViewById(R.id.favorite_action).setVisibility(View.GONE);
+            itemView.findViewById(R.id.keptOfflineIcon).setVisibility(View.GONE);
+            itemView.findViewById(R.id.localFileIndicator).setVisibility(View.GONE);
+        }
+    }
+
+    static class LocalFileListFooterViewHolder extends RecyclerView.ViewHolder {
+        private final TextView footerText;
+
+        private LocalFileListFooterViewHolder(View itemView) {
+            super(itemView);
+
+            footerText = itemView.findViewById(R.id.footerText);
+        }
     }
 }

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

@@ -0,0 +1,813 @@
+/*
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author Tobias Kaminsky
+ * @author David A. Velasco
+ * @author masensio
+ * Copyright (C) 2011  Bartek Przybylski
+ * Copyright (C) 2016 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.adapter;
+
+
+import android.accounts.Account;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Filter;
+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.OCFile;
+import com.owncloud.android.datamodel.ThumbnailsCacheManager;
+import com.owncloud.android.datamodel.VirtualFolderType;
+import com.owncloud.android.db.PreferenceManager;
+import com.owncloud.android.db.ProviderMeta;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
+import com.owncloud.android.lib.resources.files.RemoteFile;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.lib.resources.shares.ShareType;
+import com.owncloud.android.operations.RemoteOperationFailedException;
+import com.owncloud.android.services.OperationsService;
+import com.owncloud.android.ui.activity.ComponentsGetter;
+import com.owncloud.android.ui.fragment.ExtendedListFragment;
+import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface;
+import com.owncloud.android.utils.DisplayUtils;
+import com.owncloud.android.utils.FileSortOrder;
+import com.owncloud.android.utils.FileStorageUtils;
+import com.owncloud.android.utils.MimeTypeUtil;
+import com.owncloud.android.utils.ThemeUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Vector;
+
+
+/**
+ * This Adapter populates a RecyclerView with all files and folders in a Nextcloud instance.
+ */
+public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+
+    private static final int showFilenameColumnThreshold = 4;
+    private final FileDownloader.FileDownloaderBinder downloaderBinder;
+    private final FileUploader.FileUploaderBinder uploaderBinder;
+    private final OperationsService.OperationsServiceBinder operationsServiceBinder;
+    private Context mContext;
+    private ArrayList<OCFile> mFiles = new ArrayList<>();
+    private ArrayList<OCFile> mFilesAll = new ArrayList<>();
+    private boolean mJustFolders;
+    private boolean mHideItemOptions;
+    private boolean gridView = false;
+    private boolean multiSelect = false;
+    private HashSet<OCFile> checkedFiles;
+
+    private FileDataStorageManager mStorageManager;
+    private Account mAccount;
+    private OCFileListFragmentInterface ocFileListFragmentInterface;
+
+    private FilesFilter mFilesFilter;
+    private OCFile currentDirectory;
+    private static final String TAG = OCFileListAdapter.class.getSimpleName();
+
+    private static final int VIEWTYPE_ITEM = 0;
+    private static final int VIEWTYPE_FOOTER = 1;
+
+    private ArrayList<ThumbnailsCacheManager.ThumbnailGenerationTask> asyncTasks = new ArrayList<>();
+
+    public OCFileListAdapter(boolean justFolders, Context context, ComponentsGetter transferServiceGetter,
+                             OCFileListFragmentInterface ocFileListFragmentInterface, boolean argHideItemOptions,
+                             boolean gridView) {
+
+        this.ocFileListFragmentInterface = ocFileListFragmentInterface;
+        mJustFolders = justFolders;
+        mContext = context;
+        mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
+        mHideItemOptions = argHideItemOptions;
+        this.gridView = gridView;
+        checkedFiles = new HashSet<>();
+
+        downloaderBinder = transferServiceGetter.getFileDownloaderBinder();
+        uploaderBinder = transferServiceGetter.getFileUploaderBinder();
+        operationsServiceBinder = transferServiceGetter.getOperationsServiceBinder();
+
+        // initialise thumbnails cache on background thread
+        new ThumbnailsCacheManager.InitDiskCacheTask().execute();
+    }
+
+    public boolean isMultiSelect() {
+        return multiSelect;
+    }
+
+    public void setMultiSelect(boolean bool) {
+        multiSelect = bool;
+        notifyDataSetChanged();
+    }
+
+    public boolean isCheckedFile(OCFile file) {
+        return checkedFiles.contains(file);
+    }
+
+    public void removeCheckedFile(OCFile file) {
+        checkedFiles.remove(file);
+    }
+
+    public void addCheckedFile(OCFile file) {
+        checkedFiles.add(file);
+    }
+
+    public void addAllFilesToCheckedFiles() {
+        checkedFiles.addAll(mFiles);
+    }
+
+    public void removeAllFilesFromCheckedFiles() {
+        checkedFiles.clear();
+    }
+
+    public int getItemPosition(OCFile file) {
+        return mFiles.indexOf(file);
+    }
+
+    public void setFavoriteAttributeForItemID(String fileId, boolean favorite) {
+        for (int i = 0; i < mFiles.size(); i++) {
+            if (mFiles.get(i).getRemoteId().equals(fileId)) {
+                mFiles.get(i).setFavorite(favorite);
+                break;
+            }
+        }
+
+        for (int i = 0; i < mFilesAll.size(); i++) {
+            if (mFilesAll.get(i).getRemoteId().equals(fileId)) {
+                mFilesAll.get(i).setFavorite(favorite);
+                break;
+            }
+        }
+
+        new Handler(Looper.getMainLooper()).post(new Runnable() {
+            @Override
+            public void run() {
+                notifyDataSetChanged();
+            }
+        });
+    }
+
+    public void setEncryptionAttributeForItemID(String fileId, boolean encrypted) {
+        for (int i = 0; i < mFiles.size(); i++) {
+            if (mFiles.get(i).getRemoteId().equals(fileId)) {
+                mFiles.get(i).setEncrypted(encrypted);
+                break;
+            }
+        }
+
+        for (int i = 0; i < mFilesAll.size(); i++) {
+            if (mFilesAll.get(i).getRemoteId().equals(fileId)) {
+                mFilesAll.get(i).setEncrypted(encrypted);
+                break;
+            }
+        }
+
+        new Handler(Looper.getMainLooper()).post(new Runnable() {
+            @Override
+            public void run() {
+                notifyDataSetChanged();
+            }
+        });
+    }
+
+    @Override
+    public long getItemId(int position) {
+        if (mFiles == null || mFiles.size() <= position) {
+            return 0;
+        }
+        return mFiles.get(position).getFileId();
+    }
+
+    @Override
+    public int getItemCount() {
+        return mFiles.size() + 1;
+    }
+
+    @Override
+    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        switch (viewType) {
+            default:
+            case VIEWTYPE_ITEM:
+                if (gridView) {
+                    View itemView = LayoutInflater.from(mContext).inflate(R.layout.grid_item, parent, false);
+                    return new OCFileListGridViewHolder(itemView);
+                } else {
+                    View itemView = LayoutInflater.from(mContext).inflate(R.layout.list_item, parent, false);
+                    return new OCFileListItemViewHolder(itemView);
+                }
+
+            case VIEWTYPE_FOOTER:
+                View itemView = LayoutInflater.from(mContext).inflate(R.layout.list_footer, parent, false);
+                return new OCFileListFooterViewHolder(itemView);
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+        // TODO handle if file is null: if (mFiles != null && mFiles.size() > position)
+
+        if (holder instanceof OCFileListFooterViewHolder) {
+            ((OCFileListFooterViewHolder) holder).footerText.setText(getFooterText());
+
+        } else {
+            OCFileListGridViewHolder gridViewHolder = (OCFileListGridViewHolder) holder;
+
+            OCFile file = mFiles.get(position);
+
+            boolean gridImage = MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file);
+
+            gridViewHolder.thumbnail.setTag(file.getFileId());
+            setThumbnail(file, gridViewHolder.thumbnail);
+
+            if (isCheckedFile(file)) {
+                gridViewHolder.itemLayout.setBackgroundColor(mContext.getResources()
+                        .getColor(R.color.selected_item_background));
+                gridViewHolder.checkbox.setImageDrawable(ThemeUtils.tintDrawable(R.drawable.ic_checkbox_marked,
+                        ThemeUtils.primaryColor()));
+            } else {
+                gridViewHolder.itemLayout.setBackgroundColor(Color.WHITE);
+                gridViewHolder.checkbox.setImageResource(R.drawable.ic_checkbox_blank_outline);
+            }
+
+            gridViewHolder.itemLayout.setOnClickListener(v -> ocFileListFragmentInterface.onItemClicked(file));
+
+            gridViewHolder.itemLayout.setLongClickable(true);
+            gridViewHolder.itemLayout.setOnLongClickListener(v -> ocFileListFragmentInterface.onLongItemClicked(file));
+
+            gridViewHolder.fileName.setText(file.getFileName());
+
+            if (holder instanceof OCFileListItemViewHolder) {
+                OCFileListItemViewHolder itemViewHolder = (OCFileListItemViewHolder) holder;
+                itemViewHolder.fileSize.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
+                itemViewHolder.lastModification.setText(DisplayUtils.getRelativeTimestamp(mContext,
+                        file.getModificationTimestamp()));
+
+                if (multiSelect || gridView || mHideItemOptions) {
+                    itemViewHolder.overflowMenu.setVisibility(View.GONE);
+                } else {
+                    itemViewHolder.overflowMenu.setVisibility(View.VISIBLE);
+                    itemViewHolder.overflowMenu.setOnClickListener(view -> ocFileListFragmentInterface
+                            .onOverflowIconClicked(file, view));
+                }
+            }
+
+            gridViewHolder.localFileIndicator.setVisibility(View.INVISIBLE);   // default first
+
+            if (operationsServiceBinder != null && operationsServiceBinder.isSynchronizing(mAccount, file)) {
+                //synchronizing
+                gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synchronizing);
+                gridViewHolder.localFileIndicator.setVisibility(View.VISIBLE);
+
+            } else if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {
+                // downloading
+                gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synchronizing);
+                gridViewHolder.localFileIndicator.setVisibility(View.VISIBLE);
+
+            } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {
+                //uploading
+                gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synchronizing);
+                gridViewHolder.localFileIndicator.setVisibility(View.VISIBLE);
+
+            } else if (file.getEtagInConflict() != null) {
+                // conflict
+                gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synchronizing_error);
+                gridViewHolder.localFileIndicator.setVisibility(View.VISIBLE);
+
+            } else if (file.isDown()) {
+                gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synced);
+                gridViewHolder.localFileIndicator.setVisibility(View.VISIBLE);
+            }
+
+            gridViewHolder.favorite.setVisibility(file.getIsFavorite() ? View.VISIBLE : View.GONE);
+            gridViewHolder.offlineIcon.setVisibility(file.isAvailableOffline() ? View.VISIBLE : View.GONE);
+
+            if (multiSelect) {
+                gridViewHolder.checkbox.setVisibility(View.VISIBLE);
+            } else {
+                gridViewHolder.checkbox.setVisibility(View.GONE);
+            }
+
+            if (gridView && gridImage) {
+                gridViewHolder.fileName.setVisibility(View.GONE);
+            } else {
+                if (gridView && ocFileListFragmentInterface.getColumnSize() > showFilenameColumnThreshold) {
+                    gridViewHolder.fileName.setVisibility(View.GONE);
+                } else {
+                    gridViewHolder.fileName.setVisibility(View.VISIBLE);
+                }
+            }
+
+            if (mHideItemOptions) {
+                gridViewHolder.shared.setVisibility(View.GONE);
+            } else {
+                showShareIcon(gridViewHolder, file);
+            }
+        }
+    }
+
+    private void setThumbnail(OCFile file, ImageView thumbnailView) {
+        if (file.isFolder()) {
+            thumbnailView.setImageDrawable(MimeTypeUtil.getFolderTypeIcon(file.isSharedWithMe() ||
+                    file.isSharedWithSharee(), file.isSharedViaLink(), file.isEncrypted()));
+        } 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 && !file.needsUpdateThumbnail()) {
+                    if (MimeTypeUtil.isVideo(file)) {
+                        Bitmap withOverlay = ThumbnailsCacheManager.addVideoOverlay(thumbnail);
+                        thumbnailView.setImageBitmap(withOverlay);
+                    } else {
+                        thumbnailView.setImageBitmap(thumbnail);
+                    }
+                } else {
+                    // generate new thumbnail
+                    if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailView)) {
+                        try {
+                            final ThumbnailsCacheManager.ThumbnailGenerationTask task =
+                                    new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView, mStorageManager,
+                                            mAccount, asyncTasks);
+
+                            if (thumbnail == null) {
+                                if (MimeTypeUtil.isVideo(file)) {
+                                    thumbnail = ThumbnailsCacheManager.mDefaultVideo;
+                                } else {
+                                    thumbnail = ThumbnailsCacheManager.mDefaultImg;
+                                }
+                            }
+                            final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
+                                    new ThumbnailsCacheManager.AsyncThumbnailDrawable(mContext.getResources(),
+                                            thumbnail, task);
+                            thumbnailView.setImageDrawable(asyncDrawable);
+                            asyncTasks.add(task);
+                            task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file,
+                                    file.getRemoteId()));
+                        } catch (IllegalArgumentException e) {
+                            Log_OC.d(TAG, "ThumbnailGenerationTask : " + e.getMessage());
+                        }
+                    }
+                }
+
+                if (file.getMimetype().equalsIgnoreCase("image/png")) {
+                    thumbnailView.setBackgroundColor(mContext.getResources().getColor(R.color.background_color));
+                }
+            } else {
+                thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimetype(), file.getFileName(),
+                        mAccount));
+            }
+        }
+    }
+
+    private String getFooterText() {
+        if (!mJustFolders) {
+            int filesCount = 0;
+            int foldersCount = 0;
+            int count = mFiles.size();
+            OCFile file;
+            for (int i = 0; i < count; i++) {
+                file = getItem(i);
+                if (file.isFolder()) {
+                    foldersCount++;
+                } else {
+                    if (!file.isHidden()) {
+                        filesCount++;
+                    }
+                }
+            }
+
+            return generateFooterText(filesCount, foldersCount);
+        } else {
+            return "";
+        }
+    }
+
+    private String generateFooterText(int filesCount, int foldersCount) {
+        String output;
+        Resources resources = mContext.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;
+    }
+
+    public OCFile getItem(int position) {
+        return mFiles.get(position);
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        if (position == mFiles.size()) {
+            return VIEWTYPE_FOOTER;
+        } else {
+            return VIEWTYPE_ITEM;
+        }
+    }
+
+    private void showShareIcon(OCFileListGridViewHolder gridViewHolder, OCFile file) {
+        ImageView sharedIconView = gridViewHolder.shared;
+        sharedIconView.setVisibility(View.VISIBLE);
+        
+        if (file.isSharedWithSharee() || file.isSharedWithMe()) {
+            sharedIconView.setImageResource(R.drawable.shared_via_users);
+        } else if (file.isSharedViaLink()) {
+            sharedIconView.setImageResource(R.drawable.shared_via_link);
+        } else {
+            sharedIconView.setImageResource(R.drawable.ic_unshared);
+        }
+        sharedIconView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                ocFileListFragmentInterface.onShareIconClick(file);
+            }
+        });
+    }
+
+    /**
+     * Change the adapted directory for a new one
+     *
+     * @param directory             New folder to adapt. Can be NULL, meaning
+     *                              "no content to adapt".
+     * @param updatedStorageManager Optional updated storage manager; used to replace
+     *                              mStorageManager if is different (and not NULL)
+     */
+    public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager
+            , boolean onlyOnDevice) {
+        if (updatedStorageManager != null && !updatedStorageManager.equals(mStorageManager)) {
+            mStorageManager = updatedStorageManager;
+            mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
+        }
+        if (mStorageManager != null) {
+            mFiles = mStorageManager.getFolderContent(directory, onlyOnDevice);
+
+            if (mJustFolders) {
+                mFiles = getFolders(mFiles);
+            }
+            if (!PreferenceManager.showHiddenFilesEnabled(mContext)) {
+                mFiles = filterHiddenFiles(mFiles);
+            }
+            FileSortOrder sortOrder = PreferenceManager.getSortOrder(mContext, directory);
+            mFiles = sortOrder.sortCloudFiles(mFiles);
+            mFilesAll.clear();
+            mFilesAll.addAll(mFiles);
+
+            currentDirectory = directory;
+        } else {
+            mFiles.clear();
+            mFilesAll.clear();
+        }
+
+        notifyDataSetChanged();
+    }
+
+    private void searchForLocalFileInDefaultPath(OCFile file) {
+        if (file.getStoragePath() == null && !file.isFolder()) {
+            File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+            if (f.exists()) {
+                file.setStoragePath(f.getAbsolutePath());
+                file.setLastSyncDateForData(f.lastModified());
+            }
+        }
+    }
+
+    public void setData(ArrayList<Object> objects, ExtendedListFragment.SearchType searchType,
+                        FileDataStorageManager storageManager, OCFile folder) {
+        if (storageManager != null && mStorageManager == null) {
+            mStorageManager = storageManager;
+        }
+        mFiles.clear();
+
+        // early exit
+        if (objects.size() > 0 && mStorageManager != null) {
+            if (searchType.equals(ExtendedListFragment.SearchType.SHARED_FILTER)) {
+                parseShares(objects);
+            } else {
+                parseVirtuals(objects, searchType);
+            }
+        }
+
+        if (!searchType.equals(ExtendedListFragment.SearchType.PHOTO_SEARCH) &&
+                !searchType.equals(ExtendedListFragment.SearchType.PHOTOS_SEARCH_FILTER) &&
+                !searchType.equals(ExtendedListFragment.SearchType.RECENTLY_MODIFIED_SEARCH) &&
+                !searchType.equals(ExtendedListFragment.SearchType.RECENTLY_MODIFIED_SEARCH_FILTER)) {
+            FileSortOrder sortOrder = PreferenceManager.getSortOrder(mContext, folder);
+            mFiles = sortOrder.sortCloudFiles(mFiles);
+        } else {
+            mFiles = FileStorageUtils.sortOcFolderDescDateModified(mFiles);
+        }
+
+        mFilesAll.clear();
+        mFilesAll.addAll(mFiles);
+
+        new Handler(Looper.getMainLooper()).post(new Runnable() {
+            @Override
+            public void run() {
+                notifyDataSetChanged();
+            }
+        });
+    }
+
+    private void parseShares(ArrayList<Object> objects) {
+        ArrayList<OCShare> shares = new ArrayList<>();
+        for (int i = 0; i < objects.size(); i++) {
+            // check type before cast as of long running data fetch it is possible that old result is filled
+            if (objects.get(i) instanceof OCShare) {
+                OCShare ocShare = (OCShare) objects.get(i);
+
+                shares.add(ocShare);
+
+                // get ocFile from Server to have an up-to-date copy
+                ReadRemoteFileOperation operation = new ReadRemoteFileOperation(ocShare.getPath());
+                RemoteOperationResult result = operation.execute(mAccount, mContext);
+                if (result.isSuccess()) {
+                    OCFile file = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
+                    searchForLocalFileInDefaultPath(file);
+                    file = mStorageManager.saveFileWithParent(file, mContext);
+
+                    ShareType newShareType = ocShare.getShareType();
+                    if (newShareType == ShareType.PUBLIC_LINK) {
+                        file.setShareViaLink(true);
+                    } else if (newShareType == ShareType.USER || newShareType == ShareType.GROUP ||
+                            newShareType == ShareType.EMAIL || newShareType == ShareType.FEDERATED) {
+                        file.setShareWithSharee(true);
+                    }
+
+                    mStorageManager.saveFile(file);
+
+                    if (!mFiles.contains(file)) {
+                        mFiles.add(file);
+                    }
+                } else {
+                    Log_OC.e(TAG, "Error in getting prop for file: " + ocShare.getPath());
+                }
+            }
+        }
+        mStorageManager.saveShares(shares);
+    }
+
+    private void parseVirtuals(ArrayList<Object> objects, ExtendedListFragment.SearchType searchType) {
+        VirtualFolderType type;
+        boolean onlyImages = false;
+        switch (searchType) {
+            case FAVORITE_SEARCH:
+                type = VirtualFolderType.FAVORITE;
+                break;
+            case PHOTO_SEARCH:
+                type = VirtualFolderType.PHOTOS;
+                onlyImages = true;
+                break;
+            default:
+                type = VirtualFolderType.NONE;
+                break;
+        }
+
+        mStorageManager.deleteVirtuals(type);
+
+        ArrayList<ContentValues> contentValues = new ArrayList<>();
+
+        for (int i = 0; i < objects.size(); i++) {
+            OCFile ocFile = FileStorageUtils.fillOCFile((RemoteFile) objects.get(i));
+            searchForLocalFileInDefaultPath(ocFile);
+
+            try {
+                ocFile = mStorageManager.saveFileWithParent(ocFile, mContext);
+
+                if (!onlyImages || MimeTypeUtil.isImage(ocFile)) {
+                    mFiles.add(ocFile);
+                }
+
+                ContentValues cv = new ContentValues();
+                cv.put(ProviderMeta.ProviderTableMeta.VIRTUAL_TYPE, type.toString());
+                cv.put(ProviderMeta.ProviderTableMeta.VIRTUAL_OCFILE_ID, ocFile.getFileId());
+
+                contentValues.add(cv);
+            } catch (RemoteOperationFailedException e) {
+                Log_OC.e(TAG, "Error saving file with parent" + e.getMessage(), e);
+            }
+        }
+
+        mStorageManager.saveVirtuals(type, contentValues);
+    }
+
+    /**
+     * Filter for getting only the folders
+     *
+     * @param files Collection of files to filter
+     * @return Folders in the input
+     */
+    public ArrayList<OCFile> getFolders(ArrayList<OCFile> files) {
+        ArrayList<OCFile> ret = new ArrayList<>();
+
+        for (OCFile file : files) {
+            if (file.isFolder()) {
+                ret.add(file);
+            }
+        }
+
+        return ret;
+    }
+
+
+    public void setSortOrder(OCFile folder, FileSortOrder sortOrder) {
+        PreferenceManager.setSortOrder(mContext, folder, sortOrder);
+        mFiles = sortOrder.sortCloudFiles(mFiles);
+        notifyDataSetChanged();
+    }
+
+    public HashSet<OCFile> getCheckedItems() {
+        return checkedFiles;
+    }
+
+    public void setCheckedItem(HashSet<OCFile> files) {
+        checkedFiles.clear();
+        checkedFiles.addAll(files);
+    }
+
+    public void clearCheckedItems() {
+        checkedFiles.clear();
+    }
+
+    public ArrayList<OCFile> getFiles() {
+        return mFiles;
+    }
+
+    public Filter getFilter() {
+        if (mFilesFilter == null) {
+            mFilesFilter = new FilesFilter();
+        }
+        return mFilesFilter;
+    }
+
+    private class FilesFilter extends Filter {
+
+        @Override
+        protected FilterResults performFiltering(CharSequence constraint) {
+            FilterResults results = new FilterResults();
+            Vector<OCFile> filteredFiles = new Vector<>();
+
+            if (!TextUtils.isEmpty(constraint)) {
+                for (int i = 0; i < mFilesAll.size(); i++) {
+                    OCFile currentFile = mFilesAll.get(i);
+                    if (currentFile.getParentRemotePath().equals(currentDirectory.getRemotePath()) &&
+                            currentFile.getFileName().toLowerCase(Locale.getDefault()).contains(
+                                    constraint.toString().toLowerCase(Locale.getDefault())) &&
+                            !filteredFiles.contains(currentFile)) {
+                        filteredFiles.add(currentFile);
+                    }
+                }
+            }
+
+            results.values = filteredFiles;
+            results.count = filteredFiles.size();
+
+            return results;
+        }
+
+        @Override
+        protected void publishResults(CharSequence constraint, Filter.FilterResults results) {
+            Vector<OCFile> ocFiles = (Vector<OCFile>) results.values;
+            mFiles.clear();
+            if (ocFiles != null && ocFiles.size() > 0) {
+                mFiles.addAll(ocFiles);
+                if (!PreferenceManager.showHiddenFilesEnabled(mContext)) {
+                    mFiles = filterHiddenFiles(mFiles);
+                }
+                FileSortOrder sortOrder = PreferenceManager.getSortOrder(mContext, null);
+                mFiles = sortOrder.sortCloudFiles(mFiles);
+            }
+
+            notifyDataSetChanged();
+        }
+    }
+
+
+    /**
+     * Filter for hidden files
+     *
+     * @param files Collection of files to filter
+     * @return Non-hidden files
+     */
+    public ArrayList<OCFile> filterHiddenFiles(ArrayList<OCFile> files) {
+        ArrayList<OCFile> ret = new ArrayList<>();
+
+        for (OCFile file : files) {
+            if (!file.isHidden() && !ret.contains(file)) {
+                ret.add(file);
+            }
+        }
+
+        return ret;
+    }
+
+    public void cancelAllPendingTasks() {
+        for (ThumbnailsCacheManager.ThumbnailGenerationTask task : asyncTasks) {
+            if (task != null) {
+                task.cancel(true);
+                if (task.getGetMethod() != null) {
+                    Log_OC.d(TAG, "cancel: abort get method directly");
+                    task.getGetMethod().abort();
+                }
+            }
+        }
+
+        asyncTasks.clear();
+    }
+
+    public void setGridView(boolean bool) {
+        gridView = bool;
+    }
+
+    static class OCFileListItemViewHolder extends OCFileListGridViewHolder {
+        private final TextView fileSize;
+        private final TextView lastModification;
+        private final ImageView overflowMenu;
+
+        private OCFileListItemViewHolder(View itemView) {
+            super(itemView);
+
+            fileSize = itemView.findViewById(R.id.file_size);
+            lastModification = itemView.findViewById(R.id.last_mod);
+            overflowMenu = itemView.findViewById(R.id.overflow_menu);
+        }
+    }
+
+    static class OCFileListGridViewHolder extends RecyclerView.ViewHolder {
+        private final ImageView thumbnail;
+        private final ImageView favorite;
+        private final ImageView offlineIcon;
+        private final ImageView localFileIndicator;
+        private final TextView fileName;
+        private final ImageView shared;
+        private final ImageView checkbox;
+        private final LinearLayout itemLayout;
+
+        private OCFileListGridViewHolder(View itemView) {
+            super(itemView);
+
+            thumbnail = itemView.findViewById(R.id.thumbnail);
+            favorite = itemView.findViewById(R.id.favorite_action);
+            offlineIcon = itemView.findViewById(R.id.keptOfflineIcon);
+            localFileIndicator = itemView.findViewById(R.id.localFileIndicator);
+            fileName = itemView.findViewById(R.id.Filename);
+            shared = itemView.findViewById(R.id.sharedIcon);
+            checkbox = itemView.findViewById(R.id.custom_checkbox);
+            itemLayout = itemView.findViewById(R.id.ListItemLayout);
+        }
+    }
+
+    static class OCFileListFooterViewHolder extends RecyclerView.ViewHolder {
+        private final TextView footerText;
+
+        private OCFileListFooterViewHolder(View itemView) {
+            super(itemView);
+
+            footerText = itemView.findViewById(R.id.footerText);
+        }
+    }
+}

+ 9 - 4
src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java

@@ -1,4 +1,4 @@
-/**
+/*
  *   Nextcloud Android client application
  *
  * @author Andy Scherzinger
@@ -23,7 +23,6 @@ package com.owncloud.android.ui.adapter;
 
 import android.content.Context;
 import android.support.annotation.Nullable;
-import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -34,6 +33,7 @@ import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter;
+import com.afollestad.sectionedrecyclerview.SectionedViewHolder;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.MediaFolderType;
 import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
@@ -109,7 +109,7 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SyncedFold
     }
 
     @Override
-    public void onBindHeaderViewHolder(final MainViewHolder holder, final int section) {
+    public void onBindHeaderViewHolder(final MainViewHolder holder, final int section, boolean expanded) {
         holder.mainHeaderContainer.setVisibility(View.VISIBLE);
 
         holder.title.setText(mSyncFolderItems.get(section).getFolderName());
@@ -150,6 +150,11 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SyncedFold
         }
     }
 
+    @Override
+    public void onBindFooterViewHolder(MainViewHolder holder, int section) {
+
+    }
+
 
     @Override
     public void onBindViewHolder(MainViewHolder holder, int section, int relativePosition, int absolutePosition) {
@@ -198,7 +203,7 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SyncedFold
         void onSyncFolderSettingsClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem);
     }
 
-    static class MainViewHolder extends RecyclerView.ViewHolder {
+    static class MainViewHolder extends SectionedViewHolder {
         @Nullable
         @BindView(R.id.thumbnail)
         public ImageView image;

+ 672 - 0
src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java

@@ -0,0 +1,672 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2018 Tobias Kaminsky
+ * Copyright (C) 2018 Nextcloud
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.adapter;
+
+import android.accounts.Account;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.support.design.widget.Snackbar;
+import android.text.format.DateUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter;
+import com.afollestad.sectionedrecyclerview.SectionedViewHolder;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.ThumbnailsCacheManager;
+import com.owncloud.android.datamodel.UploadsStorageManager;
+import com.owncloud.android.datamodel.UploadsStorageManager.UploadStatus;
+import com.owncloud.android.db.OCUpload;
+import com.owncloud.android.db.UploadResult;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.utils.DisplayUtils;
+import com.owncloud.android.utils.MimeTypeUtil;
+import com.owncloud.android.utils.ThemeUtils;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * This Adapter populates a ListView with following types of uploads: pending,
+ * active, completed. Filtering possible.
+ */
+public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedViewHolder> {
+
+    private static final String TAG = UploadListAdapter.class.getSimpleName();
+
+    private ProgressListener mProgressListener;
+
+    private FileActivity mParentActivity;
+
+    private UploadsStorageManager mUploadsStorageManager;
+
+    private UploadGroup[] mUploadGroups = null;
+
+    @Override
+    public int getSectionCount() {
+        return mUploadGroups.length;
+    }
+
+    @Override
+    public int getItemCount(int section) {
+        return mUploadGroups[section].getItems().length;
+
+    }
+
+    @Override
+    public void onBindHeaderViewHolder(SectionedViewHolder holder, int section, boolean expanded) {
+        HeaderViewHolder headerViewHolder = (HeaderViewHolder) holder;
+
+        UploadGroup group = mUploadGroups[section];
+
+        headerViewHolder.title.setText(String.format(mParentActivity.getString(R.string.uploads_view_group_header),
+                group.getGroupName(), group.getGroupItemCount()));
+        headerViewHolder.title.setTextColor(ThemeUtils.primaryAccentColor());
+    }
+
+    @Override
+    public void onBindFooterViewHolder(SectionedViewHolder holder, int section) {
+
+    }
+
+    public UploadListAdapter(FileActivity parentActivity) {
+        Log_OC.d(TAG, "UploadListAdapter");
+        mParentActivity = parentActivity;
+        mUploadsStorageManager = new UploadsStorageManager(mParentActivity.getContentResolver(),
+                parentActivity.getApplicationContext());
+        mUploadGroups = new UploadGroup[3];
+
+        shouldShowHeadersForEmptySections(false);
+
+        mUploadGroups[0] = new UploadGroup(mParentActivity.getString(R.string.uploads_view_group_current_uploads)) {
+            @Override
+            public void refresh() {
+                setItems(mUploadsStorageManager.getCurrentAndPendingUploadsForCurrentAccount());
+                Arrays.sort(getItems(), comparator);
+            }
+        };
+
+        mUploadGroups[1] = new UploadGroup(mParentActivity.getString(R.string.uploads_view_group_failed_uploads)) {
+            @Override
+            public void refresh() {
+                setItems(mUploadsStorageManager.getFailedButNotDelayedUploadsForCurrentAccount());
+                Arrays.sort(getItems(), comparator);
+            }
+        };
+
+        mUploadGroups[2] = new UploadGroup(mParentActivity.getString(R.string.uploads_view_group_finished_uploads)) {
+            @Override
+            public void refresh() {
+                setItems(mUploadsStorageManager.getFinishedUploadsForCurrentAccount());
+                Arrays.sort(getItems(), comparator);
+            }
+        };
+        loadUploadItemsFromDb();
+    }
+
+    @Override
+    public void onBindViewHolder(SectionedViewHolder holder, int section, int relativePosition, int absolutePosition) {
+        ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
+
+        OCUpload item = mUploadGroups[section].getItem(relativePosition);
+
+        itemViewHolder.name.setText(item.getLocalPath());
+
+        // local file name
+        File remoteFile = new File(item.getRemotePath());
+        String fileName = remoteFile.getName();
+        if (fileName.length() == 0) {
+            fileName = File.separator;
+        }
+        itemViewHolder.name.setText(fileName);
+
+        // remote path to parent folder
+        itemViewHolder.remotePath.setText(new File(item.getRemotePath()).getParent());
+
+        // file size
+        if (item.getFileSize() != 0) {
+            itemViewHolder.fileSize.setText(String.format("%s, ",
+                    DisplayUtils.bytesToHumanReadable(item.getFileSize())));
+        } else {
+            itemViewHolder.fileSize.setText("");
+        }
+
+        //* upload date
+        long updateTime = item.getUploadEndTimestamp();
+        CharSequence dateString = DisplayUtils.getRelativeDateTimeString(mParentActivity, updateTime,
+                DateUtils.SECOND_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, 0);
+        itemViewHolder.date.setText(dateString);
+
+        Account account = AccountUtils.getOwnCloudAccountByName(mParentActivity, item.getAccountName());
+        if (account != null) {
+            itemViewHolder.account.setText(DisplayUtils.getAccountNameDisplayText(mParentActivity, account,
+                    account.name, item.getAccountName()));
+        } else {
+            itemViewHolder.account.setText(item.getAccountName());
+        }
+
+        // Reset fields visibility
+        itemViewHolder.date.setVisibility(View.VISIBLE);
+        itemViewHolder.remotePath.setVisibility(View.VISIBLE);
+
+        itemViewHolder.fileSize.setVisibility(View.VISIBLE);
+        itemViewHolder.account.setVisibility(View.VISIBLE);
+        itemViewHolder.status.setVisibility(View.VISIBLE);
+        itemViewHolder.progressBar.setVisibility(View.GONE);
+
+        // Update information depending of upload details
+        String status = getStatusText(item);
+        switch (item.getUploadStatus()) {
+            case UPLOAD_IN_PROGRESS:
+                ThemeUtils.colorHorizontalProgressBar(itemViewHolder.progressBar, ThemeUtils.primaryAccentColor());
+                itemViewHolder.progressBar.setProgress(0);
+                itemViewHolder.progressBar.setVisibility(View.VISIBLE);
+
+                FileUploader.FileUploaderBinder binder = mParentActivity.getFileUploaderBinder();
+                if (binder != null) {
+                    if (binder.isUploadingNow(item)) {
+                        // really uploading, so...
+                        // ... unbind the old progress bar, if any; ...
+                        if (mProgressListener != null) {
+                            binder.removeDatatransferProgressListener(
+                                    mProgressListener,
+                                    mProgressListener.getUpload()   // the one that was added
+                            );
+                        }
+                        // ... then, bind the current progress bar to listen for updates
+                        mProgressListener = new ProgressListener(item, itemViewHolder.progressBar);
+                        binder.addDatatransferProgressListener(mProgressListener, item);
+                    } else {
+                        // not really uploading; stop listening progress if view is reused!
+                        if (mProgressListener != null && mProgressListener.isWrapping(itemViewHolder.progressBar)) {
+                            binder.removeDatatransferProgressListener(mProgressListener,
+                                    mProgressListener.getUpload()   // the one that was added
+                            );
+                            mProgressListener = null;
+                        }
+                    }
+                } else {
+                    Log_OC.w(TAG, "FileUploaderBinder not ready yet for upload " + item.getRemotePath());
+                }
+
+                itemViewHolder.date.setVisibility(View.GONE);
+                itemViewHolder.fileSize.setVisibility(View.GONE);
+                itemViewHolder.progressBar.invalidate();
+                break;
+
+            case UPLOAD_FAILED:
+                itemViewHolder.date.setVisibility(View.GONE);
+                break;
+
+            case UPLOAD_SUCCEEDED:
+                itemViewHolder.status.setVisibility(View.GONE);
+                break;
+        }
+        itemViewHolder.status.setText(status);
+
+        // bind listeners to perform actions
+        if (item.getUploadStatus() == UploadStatus.UPLOAD_IN_PROGRESS) {
+            // Cancel
+            itemViewHolder.button.setImageResource(R.drawable.ic_action_cancel_grey);
+            itemViewHolder.button.setVisibility(View.VISIBLE);
+            itemViewHolder.button.setOnClickListener(v -> {
+                FileUploader.FileUploaderBinder uploaderBinder = mParentActivity.getFileUploaderBinder();
+                if (uploaderBinder != null) {
+                    uploaderBinder.cancel(item);
+                    loadUploadItemsFromDb();
+                }
+            });
+
+        } else if (item.getUploadStatus() == UploadStatus.UPLOAD_FAILED) {
+            // Delete
+            itemViewHolder.button.setImageResource(R.drawable.ic_action_delete_grey);
+            itemViewHolder.button.setVisibility(View.VISIBLE);
+            itemViewHolder.button.setOnClickListener(v -> {
+                mUploadsStorageManager.removeUpload(item);
+                loadUploadItemsFromDb();
+            });
+
+        } else {    // UploadStatus.UPLOAD_SUCCESS
+            itemViewHolder.button.setVisibility(View.INVISIBLE);
+        }
+
+        itemViewHolder.itemLayout.setOnClickListener(null);
+
+        // click on item
+        if (item.getUploadStatus() == UploadStatus.UPLOAD_FAILED) {
+            if (UploadResult.CREDENTIAL_ERROR.equals(item.getLastResult())) {
+                itemViewHolder.itemLayout.setOnClickListener(v ->
+                        mParentActivity.getFileOperationsHelper().checkCurrentCredentials(
+                                item.getAccount(mParentActivity)));
+            } else {
+                // not a credentials error
+                itemViewHolder.itemLayout.setOnClickListener(v -> {
+                    File file = new File(item.getLocalPath());
+                    if (file.exists()) {
+                        FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
+                        requester.retry(mParentActivity, item);
+                        loadUploadItemsFromDb();
+                    } else {
+                        Snackbar.make(v.getRootView().findViewById(android.R.id.content),
+                                mParentActivity.getString(R.string.local_file_not_found_message), Snackbar.LENGTH_LONG)
+                                .show();
+                    }
+                });
+            }
+        } else {
+            itemViewHolder.itemLayout.setOnClickListener(v ->
+                    onUploadItemClick(item));
+        }
+
+        // Set icon or thumbnail
+        itemViewHolder.thumbnail.setImageResource(R.drawable.file);
+
+        /*
+         * Cancellation needs do be checked and done before changing the drawable in fileIcon, or
+         * {@link ThumbnailsCacheManager#cancelPotentialWork} will NEVER cancel any task.
+         */
+        OCFile fakeFileToCheatThumbnailsCacheManagerInterface = new OCFile(item.getRemotePath());
+        fakeFileToCheatThumbnailsCacheManagerInterface.setStoragePath(item.getLocalPath());
+        fakeFileToCheatThumbnailsCacheManagerInterface.setMimetype(item.getMimeType());
+
+        boolean allowedToCreateNewThumbnail = (ThumbnailsCacheManager.cancelPotentialThumbnailWork(
+                fakeFileToCheatThumbnailsCacheManagerInterface, itemViewHolder.thumbnail)
+        );
+
+        // TODO this code is duplicated; refactor to a common place
+        if ((MimeTypeUtil.isImage(fakeFileToCheatThumbnailsCacheManagerInterface)
+                && fakeFileToCheatThumbnailsCacheManagerInterface.getRemoteId() != null &&
+                item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED)) {
+            // Thumbnail in Cache?
+            Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
+                    String.valueOf(fakeFileToCheatThumbnailsCacheManagerInterface.getRemoteId())
+            );
+            if (thumbnail != null && !fakeFileToCheatThumbnailsCacheManagerInterface.needsUpdateThumbnail()) {
+                itemViewHolder.thumbnail.setImageBitmap(thumbnail);
+            } else {
+                // generate new Thumbnail
+                if (allowedToCreateNewThumbnail) {
+                    final ThumbnailsCacheManager.ThumbnailGenerationTask task =
+                            new ThumbnailsCacheManager.ThumbnailGenerationTask(
+                                    itemViewHolder.thumbnail, mParentActivity.getStorageManager(), mParentActivity.getAccount()
+                            );
+                    if (thumbnail == null) {
+                        if (MimeTypeUtil.isVideo(fakeFileToCheatThumbnailsCacheManagerInterface)) {
+                            thumbnail = ThumbnailsCacheManager.mDefaultVideo;
+                        } else {
+                            thumbnail = ThumbnailsCacheManager.mDefaultImg;
+                        }
+                    }
+                    final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
+                            new ThumbnailsCacheManager.AsyncThumbnailDrawable(
+                                    mParentActivity.getResources(),
+                                    thumbnail,
+                                    task
+                            );
+                    itemViewHolder.thumbnail.setImageDrawable(asyncDrawable);
+                    task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(
+                            fakeFileToCheatThumbnailsCacheManagerInterface, null));
+                }
+            }
+
+            if ("image/png".equals(item.getMimeType())) {
+                itemViewHolder.thumbnail.setBackgroundColor(mParentActivity.getResources()
+                        .getColor(R.color.background_color));
+            }
+
+
+        } else if (MimeTypeUtil.isImage(fakeFileToCheatThumbnailsCacheManagerInterface)) {
+            File file = new File(item.getLocalPath());
+            // Thumbnail in Cache?
+            Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
+                    String.valueOf(file.hashCode()));
+            if (thumbnail != null) {
+                itemViewHolder.thumbnail.setImageBitmap(thumbnail);
+            } else {
+                // generate new Thumbnail
+                if (allowedToCreateNewThumbnail) {
+                    final ThumbnailsCacheManager.ThumbnailGenerationTask task =
+                            new ThumbnailsCacheManager.ThumbnailGenerationTask(itemViewHolder.thumbnail);
+
+                    if (MimeTypeUtil.isVideo(file)) {
+                        thumbnail = ThumbnailsCacheManager.mDefaultVideo;
+                    } else {
+                        thumbnail = ThumbnailsCacheManager.mDefaultImg;
+                    }
+
+                    final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
+                            new ThumbnailsCacheManager.AsyncThumbnailDrawable(mParentActivity.getResources(), thumbnail,
+                                    task);
+
+                    itemViewHolder.thumbnail.setImageDrawable(asyncDrawable);
+                    task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, null));
+                    Log_OC.v(TAG, "Executing task to generate a new thumbnail");
+                }
+            }
+
+            if ("image/png".equalsIgnoreCase(item.getMimeType())) {
+                itemViewHolder.thumbnail.setBackgroundColor(mParentActivity.getResources()
+                        .getColor(R.color.background_color));
+            }
+        } else {
+            itemViewHolder.thumbnail.setImageDrawable(MimeTypeUtil.getFileTypeIcon(item.getMimeType(), fileName,
+                    account));
+        }
+    }
+
+    /**
+     * Gets the status text to show to the user according to the status and last result of the
+     * the given upload.
+     *
+     * @param upload Upload to describe.
+     * @return Text describing the status of the given upload.
+     */
+    private String getStatusText(OCUpload upload) {
+
+        String status;
+        switch (upload.getUploadStatus()) {
+
+            case UPLOAD_IN_PROGRESS:
+                status = mParentActivity.getString(R.string.uploads_view_later_waiting_to_upload);
+                FileUploader.FileUploaderBinder binder = mParentActivity.getFileUploaderBinder();
+                if (binder != null && binder.isUploadingNow(upload)) {
+                    // really uploading, bind the progress bar to listen for progress updates
+                    status = mParentActivity.getString(R.string.uploader_upload_in_progress_ticker);
+                }
+                break;
+
+            case UPLOAD_SUCCEEDED:
+                status = mParentActivity.getString(R.string.uploads_view_upload_status_succeeded);
+                break;
+
+            case UPLOAD_FAILED:
+                switch (upload.getLastResult()) {
+                    case CREDENTIAL_ERROR:
+                        status = mParentActivity.getString(
+                                R.string.uploads_view_upload_status_failed_credentials_error
+                        );
+                        break;
+                    case FOLDER_ERROR:
+                        status = mParentActivity.getString(
+                                R.string.uploads_view_upload_status_failed_folder_error
+                        );
+                        break;
+                    case FILE_NOT_FOUND:
+                        status = mParentActivity.getString(
+                                R.string.uploads_view_upload_status_failed_localfile_error
+                        );
+                        break;
+                    case FILE_ERROR:
+                        status = mParentActivity.getString(
+                                R.string.uploads_view_upload_status_failed_file_error
+                        );
+                        break;
+                    case PRIVILEDGES_ERROR:
+                        status = mParentActivity.getString(
+                                R.string.uploads_view_upload_status_failed_permission_error
+                        );
+                        break;
+                    case NETWORK_CONNECTION:
+                        status = mParentActivity.getString(
+                                R.string.uploads_view_upload_status_failed_connection_error
+                        );
+                        break;
+                    case DELAYED_FOR_WIFI:
+                        status = mParentActivity.getString(
+                                R.string.uploads_view_upload_status_waiting_for_wifi
+                        );
+                        break;
+                    case DELAYED_FOR_CHARGING:
+                        status = mParentActivity.getString(
+                                R.string.uploads_view_upload_status_waiting_for_charging);
+                        break;
+                    case CONFLICT_ERROR:
+                        status = mParentActivity.getString(
+                                R.string.uploads_view_upload_status_conflict
+                        );
+                        break;
+                    case SERVICE_INTERRUPTED:
+                        status = mParentActivity.getString(
+                                R.string.uploads_view_upload_status_service_interrupted
+                        );
+                        break;
+                    case CANCELLED:
+                        // should not get here ; cancelled uploads should be wiped out
+                        status = mParentActivity.getString(
+                                R.string.uploads_view_upload_status_cancelled
+                        );
+                        break;
+                    case UPLOADED:
+                        // should not get here ; status should be UPLOAD_SUCCESS
+                        status = mParentActivity.getString(R.string.uploads_view_upload_status_succeeded);
+                        break;
+                    case MAINTENANCE_MODE:
+                        status = mParentActivity.getString(R.string.maintenance_mode);
+                        break;
+                    case SSL_RECOVERABLE_PEER_UNVERIFIED:
+                        status =
+                                mParentActivity.getString(
+                                        R.string.uploads_view_upload_status_failed_ssl_certificate_not_trusted
+                                );
+                        break;
+                    case UNKNOWN:
+                        status = mParentActivity.getString(R.string.uploads_view_upload_status_unknown_fail);
+                        break;
+                    case DELAYED_IN_POWER_SAVE_MODE:
+                        status = mParentActivity.getString(
+                                R.string.uploads_view_upload_status_waiting_exit_power_save_mode);
+                        break;
+                    default:
+                        status = "New fail result but no description for the user";
+                        break;
+                }
+                break;
+
+            default:
+                status = "Uncontrolled status: " + upload.getUploadStatus().toString();
+        }
+        return status;
+    }
+
+
+    @Override
+    public SectionedViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        if (viewType == VIEW_TYPE_HEADER) {
+            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.upload_list_header, parent, false);
+            return new HeaderViewHolder(v);
+        } else {
+            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.upload_list_item, parent, false);
+            return new ItemViewHolder(v);
+        }
+    }
+
+    /**
+     * Load upload items from {@link UploadsStorageManager}.
+     */
+    public void loadUploadItemsFromDb() {
+        Log_OC.d(TAG, "loadUploadItemsFromDb");
+
+        for (UploadGroup group : mUploadGroups) {
+            group.refresh();
+        }
+
+        notifyDataSetChanged();
+    }
+
+    private boolean onUploadItemClick(OCUpload file) {
+        File f = new File(file.getLocalPath());
+        if (!f.exists()) {
+            DisplayUtils.showSnackMessage(mParentActivity, R.string.local_file_not_found_message);
+        } else {
+            openFileWithDefault(file.getLocalPath());
+        }
+        return true;
+    }
+
+
+    /**
+     * Open file with app associates with its MIME type. If MIME type unknown, show list with all apps.
+     */
+    private void openFileWithDefault(String localPath) {
+        Intent myIntent = new Intent(Intent.ACTION_VIEW);
+        File file = new File(localPath);
+        String mimetype = MimeTypeUtil.getBestMimeTypeByFilename(localPath);
+        if ("application/octet-stream".equals(mimetype)) {
+            mimetype = "*/*";
+        }
+        myIntent.setDataAndType(Uri.fromFile(file), mimetype);
+        try {
+            mParentActivity.startActivity(myIntent);
+        } catch (ActivityNotFoundException e) {
+            DisplayUtils.showSnackMessage(mParentActivity, R.string.file_list_no_app_for_file_type);
+            Log_OC.i(TAG, "Could not find app for sending log history.");
+        }
+    }
+
+    static class HeaderViewHolder extends SectionedViewHolder {
+        private final TextView title;
+
+        HeaderViewHolder(View itemView) {
+            super(itemView);
+            title = itemView.findViewById(R.id.upload_list_title);
+        }
+    }
+
+    static class ItemViewHolder extends SectionedViewHolder {
+        private TextView name;
+        private ImageView thumbnail;
+        private TextView fileSize;
+        private TextView date;
+        private TextView status;
+        private TextView account;
+        private TextView remotePath;
+        private ProgressBar progressBar;
+        private ImageButton button;
+        private LinearLayout itemLayout;
+
+        ItemViewHolder(View itemView) {
+            super(itemView);
+            name = itemView.findViewById(R.id.upload_name);
+            thumbnail = itemView.findViewById(R.id.thumbnail);
+            fileSize = itemView.findViewById(R.id.upload_file_size);
+            date = itemView.findViewById(R.id.upload_date);
+            status = itemView.findViewById(R.id.upload_status);
+            account = itemView.findViewById(R.id.upload_account);
+            remotePath = itemView.findViewById(R.id.upload_remote_path);
+            progressBar = itemView.findViewById(R.id.upload_progress_bar);
+            button = itemView.findViewById(R.id.upload_right_button);
+            itemLayout = itemView.findViewById(R.id.upload_list_item_layout);
+        }
+    }
+
+    interface Refresh {
+        void refresh();
+    }
+
+    abstract class UploadGroup implements Refresh {
+        private OCUpload[] items;
+        private String name;
+
+        UploadGroup(String groupName) {
+            this.name = groupName;
+            items = new OCUpload[0];
+        }
+
+        String getGroupName() {
+            return name;
+        }
+
+        public OCUpload[] getItems() {
+            return items;
+        }
+
+        public OCUpload getItem(int position) {
+            return items[position];
+        }
+
+        public void setItems(OCUpload[] items) {
+            this.items = items;
+        }
+
+        int getGroupItemCount() {
+            return items == null ? 0 : items.length;
+        }
+
+        Comparator<OCUpload> comparator = new Comparator<OCUpload>() {
+            @Override
+            public int compare(OCUpload upload1, OCUpload upload2) {
+                if (upload1 == null) {
+                    return -1;
+                }
+                if (upload2 == null) {
+                    return 1;
+                }
+                if (UploadStatus.UPLOAD_IN_PROGRESS.equals(upload1.getUploadStatus())) {
+                    if (!UploadStatus.UPLOAD_IN_PROGRESS.equals(upload2.getUploadStatus())) {
+                        return -1;
+                    }
+                    // both are in progress
+                    FileUploader.FileUploaderBinder binder = mParentActivity.getFileUploaderBinder();
+                    if (binder != null) {
+                        if (binder.isUploadingNow(upload1)) {
+                            return -1;
+                        } else if (binder.isUploadingNow(upload2)) {
+                            return 1;
+                        }
+                    }
+                } else if (upload2.getUploadStatus().equals(UploadStatus.UPLOAD_IN_PROGRESS)) {
+                    return 1;
+                }
+                if (upload1.getUploadEndTimestamp() == 0 || upload2.getUploadEndTimestamp() == 0) {
+                    return compareUploadId(upload1, upload2);
+                } else {
+                    return compareUpdateTime(upload1, upload2);
+                }
+            }
+
+            @SuppressFBWarnings("Bx")
+            private int compareUploadId(OCUpload upload1, OCUpload upload2) {
+                return Long.valueOf(upload1.getUploadId()).compareTo(upload2.getUploadId());
+            }
+
+            @SuppressFBWarnings("Bx")
+            private int compareUpdateTime(OCUpload upload1, OCUpload upload2) {
+                return Long.valueOf(upload2.getUploadEndTimestamp()).compareTo(upload1.getUploadEndTimestamp());
+            }
+        };
+    }
+}

+ 2 - 2
src/main/java/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java

@@ -36,7 +36,6 @@ import com.owncloud.android.ui.activity.ComponentsGetter;
 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
 
 import java.util.ArrayList;
-import java.util.Vector;
 
 public class RemoveFileDialogFragment extends ConfirmationDialogFragment 
 implements ConfirmationDialogFragmentListener {
@@ -113,7 +112,8 @@ implements ConfirmationDialogFragmentListener {
         
         boolean containsFavorite = false;
         if (mTargetFile.isFolder()) {
-            Vector<OCFile> files = storageManager.getFolderContent(mTargetFile, false);
+            ArrayList<OCFile> files = storageManager.getFolderContent(mTargetFile, false);
+            
             for(OCFile file: files) {
                 containsFavorite = file.isAvailableOffline() || containsFavorite;
 

+ 0 - 89
src/main/java/com/owncloud/android/ui/fragment/ExpandableListFragment.java

@@ -1,89 +0,0 @@
-/**
- *   ownCloud Android client application
- *
- *   Copyright (C) 2012 Bartek Przybylski
- *   Copyright (C) 2012-2016 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.ui.fragment;
-
-import android.os.Bundle;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ExpandableListAdapter;
-import android.widget.ExpandableListView;
-import android.widget.ExpandableListView.OnChildClickListener;
-
-import com.owncloud.android.R;
-import com.owncloud.android.lib.common.utils.Log_OC;
-
-/**
- *  Extending ExtendedListFragment. This allows dividing list in groups.
- */
-public class ExpandableListFragment extends ExtendedListFragment implements OnChildClickListener
- {
-    protected static final String TAG = ExpandableListFragment.class.getSimpleName();
-    
-    protected ExpandableListView mList;
-    
-    public void setListAdapter(ExpandableListAdapter listAdapter) {
-        mList.setAdapter(listAdapter);
-        mList.invalidate();
-    }
-
-    public ExpandableListView getListView() {
-        return mList;
-    }
-    
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        Log_OC.e(TAG, "onCreateView");
-        
-        View v = inflater.inflate(R.layout.list_fragment_expandable, null);
-        setupEmptyList(v);
-
-        mList = (ExpandableListView)(v.findViewById(R.id.list_root));
-        mList.setOnChildClickListener(this);
-
-        mList.setDivider(getResources().getDrawable(R.drawable.uploader_list_separator));
-        mList.setDividerHeight(1);
-
-//        if (savedInstanceState != null) {
-//            int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
-//            setReferencePosition(referencePosition);
-//        }
-        
-        // Pull down refresh
-        mRefreshListLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_refresh_files);
-        mRefreshEmptyLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_refresh_files_emptyView);
-        
-        onCreateSwipeToRefresh(mRefreshListLayout);
-        onCreateSwipeToRefresh(mRefreshEmptyLayout);
-        
-        mList.setEmptyView(mRefreshEmptyLayout);
-
-        return v;
-    }
-
-    @Override
-    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
-        // to be @overridden
-        Log_OC.e(TAG, "onChildClick(). This method should be overridden!");
-        return false;
-    }
-}

+ 72 - 210
src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java

@@ -34,10 +34,12 @@ import android.support.design.widget.BottomNavigationView;
 import android.support.v4.app.Fragment;
 import android.support.v4.view.MenuItemCompat;
 import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.SearchView;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
-import android.view.GestureDetector;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -47,10 +49,8 @@ import android.view.ScaleGestureDetector;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
-import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
-import android.widget.BaseAdapter;
 import android.widget.GridView;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -67,13 +67,13 @@ import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.db.PreferenceManager;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.files.SearchOperation;
-import com.owncloud.android.ui.ExtendedListView;
+import com.owncloud.android.ui.EmptyRecyclerView;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.activity.FolderPickerActivity;
 import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
 import com.owncloud.android.ui.activity.UploadFilesActivity;
-import com.owncloud.android.ui.adapter.FileListListAdapter;
 import com.owncloud.android.ui.adapter.LocalFileListAdapter;
+import com.owncloud.android.ui.adapter.OCFileListAdapter;
 import com.owncloud.android.ui.events.SearchEvent;
 import com.owncloud.android.utils.ThemeUtils;
 
@@ -82,8 +82,6 @@ import org.parceler.Parcel;
 
 import java.util.ArrayList;
 
-import third_parties.in.srain.cube.GridViewWithHeaderAndFooter;
-
 public class ExtendedListFragment extends Fragment
         implements OnItemClickListener, OnEnforceableRefreshListener, SearchView.OnQueryTextListener {
 
@@ -105,8 +103,6 @@ public class ExtendedListFragment extends Fragment
 
     private ScaleGestureDetector mScaleGestureDetector = null;
     protected SwipeRefreshLayout mRefreshListLayout;
-    protected SwipeRefreshLayout mRefreshGridLayout;
-    protected SwipeRefreshLayout mRefreshEmptyLayout;
     protected LinearLayout mEmptyListContainer;
     protected TextView mEmptyListMessage;
     protected TextView mEmptyListHeadline;
@@ -126,13 +122,7 @@ public class ExtendedListFragment extends Fragment
 
     private SwipeRefreshLayout.OnRefreshListener mOnRefreshListener = null;
 
-    protected AbsListView mCurrentListView;
-    private ExtendedListView mListView;
-    private View mListFooterView;
-    private GridViewWithHeaderAndFooter mGridView;
-    private View mGridFooterView;
-
-    private BaseAdapter mAdapter;
+    private EmptyRecyclerView mRecyclerView;
 
     protected SearchView searchView;
     private Handler handler = new Handler();
@@ -158,14 +148,12 @@ public class ExtendedListFragment extends Fragment
         SHARED_FILTER
     }
 
-    protected void setListAdapter(BaseAdapter listAdapter) {
-        mAdapter = listAdapter;
-        mCurrentListView.setAdapter(listAdapter);
-        mCurrentListView.invalidateViews();
+    protected void setRecyclerViewAdapter(RecyclerView.Adapter recyclerViewAdapter) {
+        mRecyclerView.setAdapter(recyclerViewAdapter);
     }
 
-    protected AbsListView getListView() {
-        return mCurrentListView;
+    protected RecyclerView getRecyclerView() {
+        return mRecyclerView;
     }
 
     public FloatingActionButton getFabUpload() {
@@ -186,26 +174,18 @@ public class ExtendedListFragment extends Fragment
 
     public void switchToGridView() {
         if (!isGridEnabled()) {
-            mListView.setAdapter(null);
-            mRefreshListLayout.setVisibility(View.GONE);
-            mRefreshGridLayout.setVisibility(View.VISIBLE);
-            mCurrentListView = mGridView;
-            setListAdapter(mAdapter);
+            getRecyclerView().setLayoutManager(new GridLayoutManager(getContext(), getColumnSize()));
         }
     }
 
     public void switchToListView() {
         if (isGridEnabled()) {
-            mGridView.setAdapter(null);
-            mRefreshGridLayout.setVisibility(View.GONE);
-            mRefreshListLayout.setVisibility(View.VISIBLE);
-            mCurrentListView = mListView;
-            setListAdapter(mAdapter);
+            getRecyclerView().setLayoutManager(new LinearLayoutManager(getContext()));
         }
     }
 
     public boolean isGridEnabled() {
-        return (mCurrentListView != null && mCurrentListView.equals(mGridView));
+        return getRecyclerView().getLayoutManager() instanceof GridLayoutManager;
     }
 
     @Override
@@ -323,15 +303,16 @@ public class ExtendedListFragment extends Fragment
     private void performSearch(final String query, boolean isSubmit) {
         handler.removeCallbacksAndMessages(null);
 
-        if (!TextUtils.isEmpty(query)) {
+        RecyclerView.Adapter adapter = getRecyclerView().getAdapter();
 
+        if (!TextUtils.isEmpty(query)) {
             int delay = 500;
 
             if (isSubmit) {
                 delay = 0;
             }
 
-            if (mAdapter != null && mAdapter instanceof FileListListAdapter) {
+            if (adapter != null && adapter instanceof OCFileListAdapter) {
                 handler.postDelayed(new Runnable() {
                     @Override
                     public void run() {
@@ -340,16 +321,16 @@ public class ExtendedListFragment extends Fragment
                             EventBus.getDefault().post(new SearchEvent(query, SearchOperation.SearchType.FILE_SEARCH,
                                     SearchEvent.UnsetType.NO_UNSET));
                         } else {
-                            FileListListAdapter fileListListAdapter = (FileListListAdapter) mAdapter;
+                            OCFileListAdapter fileListListAdapter = (OCFileListAdapter) adapter;
                             fileListListAdapter.getFilter().filter(query);
                         }
                     }
                 }, delay);
-            } else if (mAdapter != null && mAdapter instanceof LocalFileListAdapter) {
+            } else if (adapter != null && adapter instanceof LocalFileListAdapter) {
                 handler.postDelayed(new Runnable() {
                     @Override
                     public void run() {
-                        LocalFileListAdapter localFileListAdapter = (LocalFileListAdapter) mAdapter;
+                        LocalFileListAdapter localFileListAdapter = (LocalFileListAdapter) adapter;
                         localFileListAdapter.filter(query);
                     }
                 }, delay);
@@ -366,15 +347,13 @@ public class ExtendedListFragment extends Fragment
                     fileDisplayActivity.resetSearchView();
                     fileDisplayActivity.refreshListOfFilesFragment(true);
                 } else if (activity instanceof UploadFilesActivity) {
-                    LocalFileListAdapter localFileListAdapter = (LocalFileListAdapter) mAdapter;
+                    LocalFileListAdapter localFileListAdapter = (LocalFileListAdapter) adapter;
                     localFileListAdapter.filter(query);
                 } else if (activity instanceof FolderPickerActivity) {
                     ((FolderPickerActivity) activity).refreshListOfFilesFragment(true);
                 }
-
             }
         }
-
     }
 
 
@@ -386,56 +365,40 @@ public class ExtendedListFragment extends Fragment
         View v = inflater.inflate(R.layout.list_fragment, null);
         setupEmptyList(v);
 
-        mListView = v.findViewById(R.id.list_root);
-        mListView.setOnItemClickListener(this);
-        mListFooterView = inflater.inflate(R.layout.list_footer, null, false);
-
-        mGridView = v.findViewById(R.id.grid_root);
+        mRecyclerView = v.findViewById(R.id.list_root);
+        mRecyclerView.setHasFooter(true);
+        mRecyclerView.setEmptyView(v.findViewById(R.id.empty_list_view));
+        mRecyclerView.setHasFixedSize(true);
+        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
 
         mScale = PreferenceManager.getGridColumns(getContext());
         setGridViewColumns(1f);
 
-        mGridView.setOnItemClickListener(this);
-
-        mGridFooterView = inflater.inflate(R.layout.list_footer, null, false);
-
         mScaleGestureDetector = new ScaleGestureDetector(MainApp.getAppContext(),new ScaleListener());
 
-        mGridView.setOnTouchListener(new View.OnTouchListener() {
-            @Override
-            public boolean onTouch(View view, MotionEvent motionEvent) {
-                mScaleGestureDetector.onTouchEvent(motionEvent);
-
-                if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
-                    view.performClick();
-                }
+        getRecyclerView().setOnTouchListener((view, motionEvent) -> {
+            mScaleGestureDetector.onTouchEvent(motionEvent);
 
-                return false;
+            if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
+                view.performClick();
             }
+
+            return false;
         });
 
         if (savedInstanceState != null) {
             int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
-            if (mCurrentListView!= null && mCurrentListView.equals(mListView)) {
+
+            if (mRecyclerView != null) {
                 Log_OC.v(TAG, "Setting and centering around list position " + referencePosition);
-                mListView.setAndCenterSelection(referencePosition);
-            } else {
-                Log_OC.v(TAG, "Setting grid position " + referencePosition);
-                mGridView.setSelection(referencePosition);
+
+                mRecyclerView.getLayoutManager().scrollToPosition(referencePosition);
             }
         }
 
         // Pull-down to refresh layout
         mRefreshListLayout = v.findViewById(R.id.swipe_containing_list);
-        mRefreshGridLayout = v.findViewById(R.id.swipe_containing_grid);
-        mRefreshEmptyLayout = v.findViewById(R.id.swipe_containing_empty);
-
         onCreateSwipeToRefresh(mRefreshListLayout);
-        onCreateSwipeToRefresh(mRefreshGridLayout);
-        onCreateSwipeToRefresh(mRefreshEmptyLayout);
-
-        mListView.setEmptyView(mRefreshEmptyLayout);
-        mGridView.setEmptyView(mRefreshEmptyLayout);
 
         mFabMain = v.findViewById(R.id.fab_main);
         mFabUpload = v.findViewById(R.id.fab_upload);
@@ -458,37 +421,19 @@ public class ExtendedListFragment extends Fragment
             layoutParams.setMargins(0, 0, pixel / 2, bottomNavigationView.getMeasuredHeight() + pixel * 2);
         }
 
-        mCurrentListView = mListView;   // list by default
         if (savedInstanceState != null) {
-
-
             if (savedInstanceState.getBoolean(KEY_IS_GRID_VISIBLE, false)) {
                 switchToGridView();
             }
             int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
-            if (isGridEnabled()) {
-                Log_OC.v(TAG, "Setting grid position " + referencePosition);
-                mGridView.setSelection(referencePosition);
-            } else {
-                Log_OC.v(TAG, "Setting and centering around list position " + referencePosition);
-                mListView.setAndCenterSelection(referencePosition);
-            }
+
+            Log_OC.v(TAG, "Setting grid position " + referencePosition);
+            scrollToPosition(referencePosition);
         }
 
         return v;
     }
 
-    public void setEmptyListVisible() {
-        mEmptyListContainer.setVisibility(View.VISIBLE);
-    }
-
-    private class SingleTapConfirm extends GestureDetector.SimpleOnGestureListener {
-        @Override
-        public boolean onSingleTapUp(MotionEvent e) {
-            return true;
-        }
-    }
-
     private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
         @Override
         public boolean onScale(ScaleGestureDetector detector) {
@@ -496,22 +441,25 @@ public class ExtendedListFragment extends Fragment
 
             PreferenceManager.setGridColumns(getContext(), mScale);
 
-            mAdapter.notifyDataSetChanged();
+            getRecyclerView().getAdapter().notifyDataSetChanged();
 
             return true;
         }
     }
 
     private void setGridViewColumns(float scaleFactor) {
-        if (mScale == -1f) {
-            mGridView.setNumColumns(GridView.AUTO_FIT);
-            mScale = mGridView.getNumColumns();
+        if (mRecyclerView.getLayoutManager() instanceof GridLayoutManager) {
+            GridLayoutManager gridLayoutManager = (GridLayoutManager) mRecyclerView.getLayoutManager();
+            if (mScale == -1f) {
+                gridLayoutManager.setSpanCount(GridView.AUTO_FIT);
+                mScale = gridLayoutManager.getSpanCount();
+            }
+            mScale *= 1.f - (scaleFactor - 1.f);
+            mScale = Math.max(minColumnSize, Math.min(mScale, maxColumnSize));
+            Integer scaleInt = Math.round(mScale);
+            gridLayoutManager.setSpanCount(scaleInt);
+            mRecyclerView.getAdapter().notifyDataSetChanged();
         }
-        mScale *= 1.f - (scaleFactor - 1.f);
-        mScale = Math.max(minColumnSize, Math.min(mScale, maxColumnSize));
-        Integer scaleInt = Math.round(mScale);
-        mGridView.setNumColumns(scaleInt);
-        mGridView.invalidateViews();
     }
 
     protected void setupEmptyList(View view) {
@@ -520,8 +468,7 @@ public class ExtendedListFragment extends Fragment
         mEmptyListHeadline = view.findViewById(R.id.empty_list_view_headline);
         mEmptyListIcon = view.findViewById(R.id.empty_list_icon);
         mEmptyListProgress = view.findViewById(R.id.empty_list_progress);
-        mEmptyListProgress.getIndeterminateDrawable().setColorFilter(ThemeUtils.primaryColor(),
-                PorterDuff.Mode.SRC_IN);
+        mEmptyListProgress.getIndeterminateDrawable().setColorFilter(ThemeUtils.primaryColor(), PorterDuff.Mode.SRC_IN);
     }
 
     /**
@@ -553,7 +500,6 @@ public class ExtendedListFragment extends Fragment
         super.onSaveInstanceState(savedInstanceState);
         Log_OC.d(TAG, "onSaveInstanceState()");
         savedInstanceState.putBoolean(KEY_IS_GRID_VISIBLE, isGridEnabled());
-        savedInstanceState.putInt(KEY_SAVED_LIST_POSITION, getReferencePosition());
         savedInstanceState.putIntegerArrayList(KEY_INDEXES, mIndexes);
         savedInstanceState.putIntegerArrayList(KEY_FIRST_POSITIONS, mFirstPositions);
         savedInstanceState.putIntegerArrayList(KEY_TOPS, mTops);
@@ -563,31 +509,10 @@ public class ExtendedListFragment extends Fragment
         PreferenceManager.setGridColumns(getContext(), mScale);
     }
 
-    /**
-     * Calculates the position of the item that will be used as a reference to
-     * reposition the visible items in the list when the device is turned to
-     * other position.
-     * <p>
-     * The current policy is take as a reference the visible item in the center
-     * of the screen.
-     *
-     * @return The position in the list of the visible item in the center of the
-     * screen.
-     */
-    protected int getReferencePosition() {
-        if (mCurrentListView != null) {
-            return (mCurrentListView.getFirstVisiblePosition() +
-                    mCurrentListView.getLastVisiblePosition()) / 2;
-        } else {
-            return 0;
-        }
-    }
-
     public int getColumnSize() {
         return Math.round(mScale);
     }
 
-
     /*
      * Restore index and position
      */
@@ -602,23 +527,17 @@ public class ExtendedListFragment extends Fragment
             Log_OC.v(TAG, "Setting selection to position: " + firstPosition + "; top: "
                     + top + "; index: " + index);
 
-            if (mCurrentListView != null && mCurrentListView.equals(mListView)) {
-                if (mHeightCell * index <= mListView.getHeight()) {
-                    mListView.setSelectionFromTop(firstPosition, top);
-                } else {
-                    mListView.setSelectionFromTop(index, 0);
-                }
+            scrollToPosition(firstPosition);
+        }
+    }
 
-            } else {
-                if (mHeightCell * index <= mGridView.getHeight()) {
-                    mGridView.setSelection(firstPosition);
-                    //mGridView.smoothScrollToPosition(firstPosition);
-                } else {
-                    mGridView.setSelection(index);
-                    //mGridView.smoothScrollToPosition(index);
-                }
-            }
+    private void scrollToPosition(int position) {
+        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
 
+        if (mRecyclerView != null) {
+            int visibleItemCount = linearLayoutManager.findLastCompletelyVisibleItemPosition() -
+                    linearLayoutManager.findFirstCompletelyVisibleItemPosition();
+            linearLayoutManager.scrollToPositionWithOffset(position, (visibleItemCount / 2) * mHeightCell);
         }
     }
 
@@ -629,10 +548,17 @@ public class ExtendedListFragment extends Fragment
 
         mIndexes.add(index);
 
-        int firstPosition = mCurrentListView.getFirstVisiblePosition();
+        RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
+        int firstPosition;
+        if (layoutManager instanceof GridLayoutManager) {
+            firstPosition = ((GridLayoutManager) layoutManager).findFirstCompletelyVisibleItemPosition();
+        } else {
+            firstPosition = ((LinearLayoutManager) layoutManager).findFirstCompletelyVisibleItemPosition();
+        }
+
         mFirstPositions.add(firstPosition);
 
-        View view = mCurrentListView.getChildAt(0);
+        View view = mRecyclerView.getChildAt(0);
         int top = (view == null) ? 0 : view.getTop();
 
         mTops.add(top);
@@ -642,7 +568,6 @@ public class ExtendedListFragment extends Fragment
     }
 
 
-    @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         // to be @overridden
     }
@@ -661,8 +586,6 @@ public class ExtendedListFragment extends Fragment
         }
 
         mRefreshListLayout.setRefreshing(false);
-        mRefreshGridLayout.setRefreshing(false);
-        mRefreshEmptyLayout.setRefreshing(false);
 
         if (mOnRefreshListener != null) {
             mOnRefreshListener.onRefresh();
@@ -679,14 +602,12 @@ public class ExtendedListFragment extends Fragment
      * <p>
      * Sets the 'enabled' state of the refresh layouts contained in the fragment.
      * <p>
-     * When 'false' is set, prevents user gestures but keeps the option to refresh programatically,
+     * When 'false' is set, prevents user gestures but keeps the option to refresh programmatically,
      *
      * @param enabled Desired state for capturing swipe gesture.
      */
     public void setSwipeEnabled(boolean enabled) {
         mRefreshListLayout.setEnabled(enabled);
-        mRefreshGridLayout.setEnabled(enabled);
-        mRefreshEmptyLayout.setEnabled(enabled);
     }
 
     /**
@@ -876,70 +797,12 @@ public class ExtendedListFragment extends Fragment
     @Override
     public void onRefresh(boolean ignoreETag) {
         mRefreshListLayout.setRefreshing(false);
-        mRefreshGridLayout.setRefreshing(false);
-        mRefreshEmptyLayout.setRefreshing(false);
 
         if (mOnRefreshListener != null) {
             mOnRefreshListener.onRefresh();
         }
     }
 
-    protected void setChoiceMode(int choiceMode) {
-        mListView.setChoiceMode(choiceMode);
-        mGridView.setChoiceMode(choiceMode);
-    }
-
-    protected void setMultiChoiceModeListener(AbsListView.MultiChoiceModeListener listener) {
-        mListView.setMultiChoiceModeListener(listener);
-        mGridView.setMultiChoiceModeListener(listener);
-    }
-
-    /**
-     * TODO doc
-     * To be called before setAdapter, or GridViewWithHeaderAndFooter will throw an exception
-     *
-     * @param enabled flag if footer should be shown/calculated
-     */
-    protected void setFooterEnabled(boolean enabled) {
-        if (enabled) {
-            if (mGridView.getFooterViewCount() == 0 && mGridView.isCorrectAdapter()) {
-                if (mGridFooterView.getParent() != null) {
-                    ((ViewGroup) mGridFooterView.getParent()).removeView(mGridFooterView);
-                }
-                mGridView.addFooterView(mGridFooterView, null, false);
-            }
-            mGridFooterView.invalidate();
-
-            if (mListView.getFooterViewsCount() == 0) {
-                if (mListFooterView.getParent() != null) {
-                    ((ViewGroup) mListFooterView.getParent()).removeView(mListFooterView);
-                }
-                mListView.addFooterView(mListFooterView, null, false);
-            }
-            mListFooterView.invalidate();
-
-        } else {
-            mGridView.removeFooterView(mGridFooterView);
-            mListView.removeFooterView(mListFooterView);
-        }
-    }
-
-    /**
-     * set the list/grid footer text.
-     *
-     * @param text the footer text
-     */
-    protected void setFooterText(String text) {
-        if (text != null && text.length() > 0) {
-            ((TextView) mListFooterView.findViewById(R.id.footerText)).setText(text);
-            ((TextView) mGridFooterView.findViewById(R.id.footerText)).setText(text);
-            setFooterEnabled(true);
-
-        } else {
-            setFooterEnabled(false);
-        }
-    }
-
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
@@ -952,9 +815,8 @@ public class ExtendedListFragment extends Fragment
             maxColumnSize = maxColumnSizePortrait;
         }
 
-        if (mGridView != null && mGridView.getNumColumns() > maxColumnSize) {
-            mGridView.setNumColumns(maxColumnSize);
-            mGridView.invalidateViews();
+        if (isGridEnabled() && getColumnSize() > maxColumnSize) {
+            ((GridLayoutManager) getRecyclerView().getLayoutManager()).setSpanCount(maxColumnSize);
         }
     }
 }

+ 83 - 107
src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java

@@ -1,63 +1,55 @@
 /**
- *   ownCloud Android client application
- *
- *   @author David A. Velasco
- *   Copyright (C) 2011  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/>.
+ * ownCloud Android client application
  *
+ * @author David A. Velasco
+ * Copyright (C) 2011  Bartek Przybylski
+ * Copyright (C) 2015 ownCloud Inc.
+ * <p>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ * <p>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 package com.owncloud.android.ui.fragment;
 
 import android.app.Activity;
-import android.graphics.Color;
 import android.os.Bundle;
 import android.os.Environment;
-import android.util.SparseBooleanArray;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.ImageView;
-import android.widget.ListView;
 
 import com.owncloud.android.R;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.ui.adapter.LocalFileListAdapter;
+import com.owncloud.android.ui.interfaces.LocalFileListFragmentInterface;
 import com.owncloud.android.utils.AnalyticsUtils;
 import com.owncloud.android.utils.FileSortOrder;
-import com.owncloud.android.utils.ThemeUtils;
 
 import java.io.File;
-import java.util.ArrayList;
 
 
 /**
  * A Fragment that lists all files and folders in a given LOCAL path.
  */
-public class LocalFileListFragment extends ExtendedListFragment {
+public class LocalFileListFragment extends ExtendedListFragment implements LocalFileListFragmentInterface {
     private static final String TAG = LocalFileListFragment.class.getSimpleName();
-    
+
     /** Reference to the Activity which this fragment is attached to. For callbacks */
     private LocalFileListFragment.ContainerActivity mContainerActivity;
-    
+
     /** Directory to show */
     private File mDirectory = null;
-    
+
     /** Adapter to connect the data from the directory with the View object */
     private LocalFileListAdapter mAdapter = null;
 
@@ -90,7 +82,7 @@ public class LocalFileListFragment extends ExtendedListFragment {
                     LocalFileListFragment.ContainerActivity.class.getSimpleName());
         }
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -99,8 +91,7 @@ public class LocalFileListFragment extends ExtendedListFragment {
         Log_OC.i(TAG, "onCreateView() start");
         View v = super.onCreateView(inflater, container, savedInstanceState);
 
-        if(!mContainerActivity.isFolderPickerMode()) {
-            setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+        if (!mContainerActivity.isFolderPickerMode()) {
             setMessageForEmptyList(R.string.file_list_empty_headline, R.string.local_file_list_empty,
                     R.drawable.ic_list_empty_folder, true);
         } else {
@@ -122,16 +113,13 @@ public class LocalFileListFragment extends ExtendedListFragment {
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         Log_OC.i(TAG, "onActivityCreated() start");
-        
+
         super.onActivityCreated(savedInstanceState);
 
-        mAdapter = new LocalFileListAdapter(
-                mContainerActivity.isFolderPickerMode(),
-                mContainerActivity.getInitialDirectory(),
-                getActivity()
-        );
-        setListAdapter(mAdapter);
-        
+        mAdapter = new LocalFileListAdapter(mContainerActivity.isFolderPickerMode(),
+                mContainerActivity.getInitialDirectory(), this, getActivity());
+        setRecyclerViewAdapter(mAdapter);
+
         Log_OC.i(TAG, "onActivityCreated() stop");
     }
 
@@ -147,14 +135,14 @@ public class LocalFileListFragment extends ExtendedListFragment {
             super.onCreateOptionsMenu(menu, inflater);
         }
     }
-    
+
+
     /**
      * Checks the file clicked over. Browses inside if it is a directory.
      * Notifies the container activity in any case.
      */
     @Override
-    public void onItemClick(AdapterView<?> l, View v, int position, long id) {
-        File file = (File) mAdapter.getItem(position); 
+    public void onItemClicked(File file) {
         if (file != null) {
             /// Click on a directory
             if (file.isDirectory()) {
@@ -164,37 +152,35 @@ public class LocalFileListFragment extends ExtendedListFragment {
                 mContainerActivity.onDirectoryClick(file);
 
                 // save index and top position
-                saveIndexAndTopPosition(position);
-            
+                saveIndexAndTopPosition(mAdapter.getItemPosition(file));
+
             } else {    /// Click on a file
-                ImageView checkBoxV = (ImageView) v.findViewById(R.id.custom_checkbox);
-                if (checkBoxV != null) {
-                    if (getListView().isItemChecked(position)) {
-                        v.setBackgroundColor(getContext().getResources().getColor(R.color.selected_item_background));
-                        checkBoxV.setImageDrawable(ThemeUtils.tintDrawable(R.drawable.ic_checkbox_marked,
-                                ThemeUtils.primaryColor()));
-
-                    } else {
-                        v.setBackgroundColor(Color.WHITE);
-                        checkBoxV.setImageResource(R.drawable.ic_checkbox_blank_outline);
-                    }
+                if (mAdapter.isCheckedFile(file)) {
+                    // uncheck
+                    mAdapter.removeCheckedFile(file);
+                } else {
+                    // check
+                    mAdapter.addCheckedFile(file);
                 }
+
+                mAdapter.notifyItemChanged(mAdapter.getItemPosition(file));
+
                 // notify the change to the container Activity
                 mContainerActivity.onFileClick(file);
             }
-            
+
         } else {
             Log_OC.w(TAG, "Null object in ListAdapter!!");
         }
     }
 
-    
+
     /**
      * Call this, when the user presses the up button
      */
     public void onNavigateUp() {
         File parentDir = null;
-        if(mDirectory != null) {
+        if (mDirectory != null) {
             parentDir = mDirectory.getParentFile();  // can be null
         }
         listDirectory(parentDir);
@@ -203,39 +189,39 @@ public class LocalFileListFragment extends ExtendedListFragment {
         restoreIndexAndTopPosition();
     }
 
-    
+
     /**
      * Use this to query the {@link File} object for the directory
      * that is currently being displayed by this fragment
-     * 
+     *
      * @return File     The currently displayed directory
      */
-    public File getCurrentDirectory(){
+    public File getCurrentDirectory() {
         return mDirectory;
     }
-    
-    
+
+
     /**
      * Calls {@link LocalFileListFragment#listDirectory(File)} with a null parameter
      * to refresh the current directory.
      */
-    public void listDirectory(){
+    public void listDirectory() {
         listDirectory(null);
     }
-    
-    
+
+
     /**
      * Lists the given directory on the view. When the input parameter is null,
      * it will either refresh the last known directory. list the root
      * if there never was a directory.
-     * 
+     *
      * @param directory     Directory to be listed
      */
     public void listDirectory(File directory) {
-        
+
         // Check input parameters for null
-        if(directory == null) {
-            if(mDirectory != null){
+        if (directory == null) {
+            if (mDirectory != null) {
                 directory = mDirectory;
             } else {
                 directory = Environment.getExternalStorageDirectory();
@@ -245,43 +231,29 @@ public class LocalFileListFragment extends ExtendedListFragment {
                 }
             }
         }
-        
-        
+
+
         // if that's not a directory -> List its parent
-        if(!directory.isDirectory()){
+        if (!directory.isDirectory()) {
             Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
             directory = directory.getParentFile();
         }
 
         // by now, only files in the same directory will be kept as selected
-        mCurrentListView.clearChoices();
+        mAdapter.removeAllFilesFromCheckedFiles();
         mAdapter.swapDirectory(directory);
-        if (mDirectory == null || !mDirectory.equals(directory)) {
-            mCurrentListView.setSelection(0);
-        }
+
         mDirectory = directory;
     }
-    
+
 
     /**
-     * Returns the fule paths to the files checked by the user
-     * 
-     * @return      File paths to the files checked by the user.
+     * Returns the full paths to the files checked by the user
+     *
+     * @return File paths to the files checked by the user.
      */
     public String[] getCheckedFilePaths() {
-        ArrayList<String> result = new ArrayList<>();
-        SparseBooleanArray positions = mCurrentListView.getCheckedItemPositions();
-        if (positions.size() > 0) {
-            for (int i = 0; i < positions.size(); i++) {
-                if (positions.get(positions.keyAt(i))) {
-                    result.add(((File) mCurrentListView.getItemAtPosition(
-                            positions.keyAt(i))).getAbsolutePath());
-                }
-            }
-
-            Log_OC.d(TAG, "Returning " + result.size() + " selected files");
-        }
-        return result.toArray(new String[result.size()]);
+        return mAdapter.getCheckedFilesPath();
     }
 
     public void sortFiles(FileSortOrder sortOrder) {
@@ -294,15 +266,19 @@ public class LocalFileListFragment extends ExtendedListFragment {
      * @param select <code>true</code> to select all, <code>false</code> to deselect all
      */
     public void selectAllFiles(boolean select) {
-        AbsListView listView = getListView();
-        for (int position = 0; position < listView.getCount(); position++) {
-            File file = (File) mAdapter.getItem(position);
-            if (file.isFile()) {
-                listView.setItemChecked(position, select);
-            }
+        LocalFileListAdapter localFileListAdapter = (LocalFileListAdapter) getRecyclerView().getAdapter();
+
+        if (select) {
+            localFileListAdapter.addAllFilesToCheckedFiles();
+        } else {
+            localFileListAdapter.removeAllFilesFromCheckedFiles();
+        }
+
+        for (int i = 0; i < mAdapter.getItemCount(); i++) {
+            mAdapter.notifyItemChanged(i);
         }
     }
-    
+
     /**
      * Interface to implement by any Activity that includes some instance of LocalFileListFragment
      */
@@ -310,15 +286,15 @@ public class LocalFileListFragment extends ExtendedListFragment {
 
         /**
          * Callback method invoked when a directory is clicked by the user on the files list
-         *  
+         *
          * @param directory
          */
         void onDirectoryClick(File directory);
-        
+
         /**
          * Callback method invoked when a file (non directory)
          * is clicked by the user on the files list
-         *  
+         *
          * @param file
          */
         void onFileClick(File file);
@@ -326,8 +302,8 @@ public class LocalFileListFragment extends ExtendedListFragment {
         /**
          * Callback method invoked when the parent activity
          * is fully created to get the directory to list firstly.
-         * 
-         * @return  Directory to list firstly. Can be NULL.
+         *
+         * @return Directory to list firstly. Can be NULL.
          */
         File getInitialDirectory();
 

+ 177 - 178
src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -37,11 +37,14 @@ import android.support.annotation.Nullable;
 import android.support.annotation.StringRes;
 import android.support.design.widget.BottomNavigationView;
 import android.support.design.widget.Snackbar;
+import android.support.v4.app.FragmentActivity;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v4.widget.SwipeRefreshLayout;
 import android.support.v7.app.ActionBar;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
-import android.util.SparseBooleanArray;
 import android.view.ActionMode;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -50,8 +53,6 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.ListView;
 import android.widget.PopupMenu;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
@@ -82,7 +83,7 @@ import com.owncloud.android.ui.activity.FolderPickerActivity;
 import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
 import com.owncloud.android.ui.activity.ToolbarActivity;
 import com.owncloud.android.ui.activity.UploadFilesActivity;
-import com.owncloud.android.ui.adapter.FileListListAdapter;
+import com.owncloud.android.ui.adapter.OCFileListAdapter;
 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
 import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
 import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment;
@@ -93,7 +94,6 @@ import com.owncloud.android.ui.events.DummyDrawerEvent;
 import com.owncloud.android.ui.events.EncryptionEvent;
 import com.owncloud.android.ui.events.FavoriteEvent;
 import com.owncloud.android.ui.events.SearchEvent;
-import com.owncloud.android.ui.helpers.SparseBooleanArrayParcelable;
 import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface;
 import com.owncloud.android.ui.preview.PreviewImageFragment;
 import com.owncloud.android.ui.preview.PreviewMediaFragment;
@@ -115,11 +115,12 @@ import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * A Fragment that lists all files and folders in a given path.
- *
+ * <p>
  * TODO refactor to get rid of direct dependency on FileDisplayActivity
  */
 public class OCFileListFragment extends ExtendedListFragment implements OCFileListFragmentInterface {
@@ -154,7 +155,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
     private FileFragment.ContainerActivity mContainerActivity;
 
     private OCFile mFile = null;
-    private FileListListAdapter mAdapter;
+    private OCFileListAdapter mAdapter;
     private boolean mOnlyFoldersClickable;
 
     private int mSystemBarActionModeColor;
@@ -200,6 +201,10 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         }
 
         searchFragment = currentSearchType != null;
+
+        if (isGridViewPreferred(getCurrentFile())) {
+            switchToGridView();
+        }
     }
 
     /**
@@ -268,6 +273,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         if (allowContextualActions) {
             setChoiceModeAsMultipleModal(savedInstanceState);
         }
+
         Log_OC.i(TAG, "onCreateView() end");
         return v;
     }
@@ -299,7 +305,6 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
     }
 
 
-
     /**
      * {@inheritDoc}
      */
@@ -313,16 +318,16 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             mFile = savedInstanceState.getParcelable(KEY_FILE);
         }
 
-        setFooterEnabled(true);
-
         Bundle args = getArguments();
         mOnlyFoldersClickable = (args != null) && args.getBoolean(ARG_ONLY_FOLDERS_CLICKABLE, false);
         boolean hideItemOptions = (args != null) && args.getBoolean(ARG_HIDE_ITEM_OPTIONS, false);
 
-        mAdapter = new FileListListAdapter(getActivity(), mContainerActivity, this, hideItemOptions);
-        setListAdapter(mAdapter);
+        mAdapter = new OCFileListAdapter(getActivity(), mContainerActivity, this, hideItemOptions,
+                isGridViewPreferred(mFile));
+        setRecyclerViewAdapter(mAdapter);
 
         mHideFab = (args != null) && args.getBoolean(ARG_HIDE_FAB, false);
+
         if (mHideFab) {
             setFabEnabled(false);
         } else {
@@ -512,44 +517,31 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
                 com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE);
     }
 
-    @Override
-    public void finishedFiltering() {
-        updateFooter();
-    }
-
     @Override
     public void onShareIconClick(OCFile file) {
         mContainerActivity.getFileOperationsHelper().sendShareFile(file);
     }
 
     @Override
-    public void onOverflowIconClick(View view, OCFile file) {
+    public void onOverflowIconClicked(OCFile file, View view) {
         PopupMenu popup = new PopupMenu(getActivity(), view);
         popup.inflate(R.menu.file_actions_menu);
-        FileMenuFilter mf = new FileMenuFilter(
-                mAdapter.getFiles().size(),
-                Collections.singleton(file),
-                ((FileActivity) getActivity()).getAccount(),
-                mContainerActivity,
-                getActivity(),
-                true
-        );
+        FileMenuFilter mf = new FileMenuFilter(mAdapter.getFiles().size(), Collections.singleton(file),
+                ((FileActivity) getActivity()).getAccount(), mContainerActivity, getActivity(), true);
         mf.filter(popup.getMenu(), true);
-        popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
-            @Override
-            public boolean onMenuItemClick(MenuItem item) {
-                ArrayList<OCFile> checkedFiles = new ArrayList<>(Collections.singletonList(file));
-                return onFileActionChosen(item.getItemId(), checkedFiles);
-            }
+        popup.setOnMenuItemClickListener(item -> {
+            Set<OCFile> checkedFiles = new HashSet<>();
+            checkedFiles.add(file);
+            return onFileActionChosen(item.getItemId(), checkedFiles);
         });
         popup.show();
     }
 
     /**
      * Handler for multiple selection mode.
-     *
+     * <p>
      * Manages input from the user when one or more files or folders are selected in the list.
-     *
+     * <p>
      * Also listens to changes in navigation drawer to hide and recover multiple selection when it's opened
      * and closed.
      */
@@ -567,7 +559,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         /**
          * Selected items in list when action mode is closed by drawer
          */
-        private SparseBooleanArray mSelectionWhenActionModeClosedByDrawer = null;
+        private HashSet<OCFile> mSelectionWhenActionModeClosedByDrawer = new HashSet<>();
 
         @Override
         public void onDrawerSlide(View drawerView, float slideOffset) {
@@ -587,17 +579,16 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
          */
         @Override
         public void onDrawerClosed(View drawerView) {
-            if (mSelectionWhenActionModeClosedByDrawer != null && mActionModeClosedByDrawer) {
-                for (int i = 0; i < mSelectionWhenActionModeClosedByDrawer.size(); i++) {
-                    if (mSelectionWhenActionModeClosedByDrawer.valueAt(i)) {
-                        getListView().setItemChecked(
-                                mSelectionWhenActionModeClosedByDrawer.keyAt(i),
-                                true
-                        );
-                    }
-                }
+            if (mActionModeClosedByDrawer && mSelectionWhenActionModeClosedByDrawer.size() > 0) {
+                FragmentActivity actionBarActivity = getActivity();
+                actionBarActivity.startActionMode(mMultiChoiceModeListener);
+
+                getAdapter().setCheckedItem(mSelectionWhenActionModeClosedByDrawer);
+
+                mActiveActionMode.invalidate();
+
+                mSelectionWhenActionModeClosedByDrawer.clear();
             }
-            mSelectionWhenActionModeClosedByDrawer = null;
         }
 
         /**
@@ -609,7 +600,8 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         @Override
         public void onDrawerStateChanged(int newState) {
             if (DrawerLayout.STATE_DRAGGING == newState && mActiveActionMode != null) {
-                mSelectionWhenActionModeClosedByDrawer = getListView().getCheckedItemPositions().clone();
+                mSelectionWhenActionModeClosedByDrawer.addAll(((OCFileListAdapter) getRecyclerView().getAdapter())
+                        .getCheckedItems());
                 mActiveActionMode.finish();
                 mActionModeClosedByDrawer = true;
             }
@@ -621,8 +613,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
          */
         @Override
         public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
-            getListView().invalidateViews();
-            mode.invalidate();
+            // nothing to do here
         }
 
         /**
@@ -643,6 +634,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             // hide FAB in multi selection mode
             setFabEnabled(false);
 
+            mAdapter.setMultiSelect(true);
             return true;
         }
 
@@ -651,13 +643,9 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
          */
         @Override
         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-            List<OCFile> checkedFiles = mAdapter.getCheckedItems(getListView());
-            final int checkedCount = checkedFiles.size();
-            String title = getResources().getQuantityString(
-                    R.plurals.items_selected_count,
-                    checkedCount,
-                    checkedCount
-            );
+            Set<OCFile> checkedFiles = mAdapter.getCheckedItems();
+            final int checkedCount = mAdapter.getCheckedItems().size();
+            String title = getResources().getQuantityString(R.plurals.items_selected_count, checkedCount, checkedCount);
             mode.setTitle(title);
             FileMenuFilter mf = new FileMenuFilter(
                     mAdapter.getFiles().size(),
@@ -676,7 +664,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
          */
         @Override
         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-            ArrayList<OCFile> checkedFiles = mAdapter.getCheckedItems(getListView());
+            Set<OCFile> checkedFiles = mAdapter.getCheckedItems();
             return onFileActionChosen(item.getItemId(), checkedFiles);
         }
 
@@ -695,30 +683,19 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             if (!mHideFab && !searchFragment) {
                 setFabEnabled(true);
             }
+
+            mAdapter.setMultiSelect(false);
+            mAdapter.clearCheckedItems();
         }
 
 
         public void storeStateIn(Bundle outState) {
             outState.putBoolean(KEY_ACTION_MODE_CLOSED_BY_DRAWER, mActionModeClosedByDrawer);
-            if (mSelectionWhenActionModeClosedByDrawer != null) {
-                SparseBooleanArrayParcelable sbap = new SparseBooleanArrayParcelable(
-                        mSelectionWhenActionModeClosedByDrawer
-                );
-                outState.putParcelable(KEY_SELECTION_WHEN_CLOSED_BY_DRAWER, sbap);
-            }
         }
 
         public void loadStateFrom(Bundle savedInstanceState) {
-            mActionModeClosedByDrawer = savedInstanceState.getBoolean(
-                    KEY_ACTION_MODE_CLOSED_BY_DRAWER,
-                    mActionModeClosedByDrawer
-            );
-            SparseBooleanArrayParcelable sbap = savedInstanceState.getParcelable(
-                    KEY_SELECTION_WHEN_CLOSED_BY_DRAWER
-            );
-            if (sbap != null) {
-                mSelectionWhenActionModeClosedByDrawer = sbap.getSparseBooleanArray();
-            }
+            mActionModeClosedByDrawer = savedInstanceState.getBoolean(KEY_ACTION_MODE_CLOSED_BY_DRAWER,
+                    mActionModeClosedByDrawer);
         }
     }
 
@@ -726,11 +703,9 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
      * Init listener that will handle interactions in multiple selection mode.
      */
     private void setChoiceModeAsMultipleModal(Bundle savedInstanceState) {
-        setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
         if (savedInstanceState != null) {
             mMultiChoiceModeListener.loadStateFrom(savedInstanceState);
         }
-        setMultiChoiceModeListener(mMultiChoiceModeListener);
         ((FileActivity) getActivity()).addDrawerListener(mMultiChoiceModeListener);
     }
 
@@ -783,9 +758,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
                         menuItemOrig.getTitle());
             }
 
-        } else if (menuItemAddRemoveValue.equals(MenuItemAddRemove.ADD_GRID_AND_SORT))
-
-        {
+        } else if (menuItemAddRemoveValue.equals(MenuItemAddRemove.ADD_GRID_AND_SORT)) {
             if (menu.findItem(R.id.action_switch_view) == null) {
                 menuItemOrig = mOriginalMenuItems.get(0);
                 menu.add(menuItemOrig.getGroupId(), menuItemOrig.getItemId(), menuItemOrig.getOrder(),
@@ -830,7 +803,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
 
     /**
      * Call this, when the user presses the up button.
-     *
+     * <p>
      * Tries to move up the current folder one level. If the parent folder was removed from the
      * database, it continues browsing up until finding an existing folders.
      * <p/>
@@ -874,19 +847,38 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         return moveCount;
     }
 
+
     @Override
-    public void onItemClick(AdapterView<?> l, View v, int position, long id) {
-        OCFile file = (OCFile) mAdapter.getItem(position);
-
-        if (file != null) {
-            if (file.isFolder()) {
-                if (file.isEncrypted()) {
-                    // check if API >= 19
-                    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
-                        Snackbar.make(mCurrentListView, R.string.end_to_end_encryption_not_supported,
-                                Snackbar.LENGTH_LONG).show();
-                        return;
-                    }
+    public boolean onLongItemClicked(OCFile file) {
+        FragmentActivity actionBarActivity = getActivity();
+        getAdapter().addCheckedFile(file);
+        actionBarActivity.startActionMode(mMultiChoiceModeListener);
+
+        return true;
+    }
+
+    @Override
+    public void onItemClicked(OCFile file) {
+        if (getAdapter().isMultiSelect()) {
+            if (getAdapter().isCheckedFile(file)) {
+                getAdapter().removeCheckedFile(file);
+            } else {
+                getAdapter().addCheckedFile(file);
+            }
+            mActiveActionMode.invalidate();
+            mAdapter.notifyItemChanged(getAdapter().getItemPosition(file));
+        } else {
+            if (file != null) {
+                int position = mAdapter.getItemPosition(file);
+                
+                if (file.isFolder()) {
+                    if (file.isEncrypted()) {
+                        // check if API >= 19
+                        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
+                            Snackbar.make(getRecyclerView(), R.string.end_to_end_encryption_not_supported,
+                                    Snackbar.LENGTH_LONG).show();
+                            return;
+                        }
 
                     Account account = ((FileActivity) mContainerActivity).getAccount();
 
@@ -898,30 +890,29 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
                         Snackbar.make(mCurrentListView, R.string.end_to_end_encryption_not_enabled,
                                 Snackbar.LENGTH_LONG).show();
                         return;
-                    }
-                    
-                    // check if keys are stored
+                    }// check if keys are stored
                     ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(
                             getContext().getContentResolver());
 
+                    
                     String publicKey = arbitraryDataProvider.getValue(account, EncryptionUtils.PUBLIC_KEY);
                     String privateKey = arbitraryDataProvider.getValue(account, EncryptionUtils.PRIVATE_KEY);
 
-                    if (publicKey.isEmpty() || privateKey.isEmpty()) {
-                        Log_OC.d(TAG, "no public key for " + account.name);
+                        if (publicKey.isEmpty() || privateKey.isEmpty()) {
+                            Log_OC.d(TAG, "no public key for " + account.name);
 
-                        SetupEncryptionDialogFragment dialog = SetupEncryptionDialogFragment.newInstance(account,
-                                position);
-                        dialog.setTargetFragment(this, SetupEncryptionDialogFragment.SETUP_ENCRYPTION_REQUEST_CODE);
-                        dialog.show(getFragmentManager(), SetupEncryptionDialogFragment.SETUP_ENCRYPTION_DIALOG_TAG);
-                    } else {
-                        // update state and view of this fragment
-                        searchFragment = false;
+                            SetupEncryptionDialogFragment dialog = SetupEncryptionDialogFragment.newInstance(account,
+                                    position);
+                            dialog.setTargetFragment(this, SetupEncryptionDialogFragment.SETUP_ENCRYPTION_REQUEST_CODE);
+                            dialog.show(getFragmentManager(), SetupEncryptionDialogFragment.SETUP_ENCRYPTION_DIALOG_TAG);
+                        } else {
+                            // update state and view of this fragment
+                            searchFragment = false;
 
                         if (mContainerActivity instanceof FolderPickerActivity &&
                                 ((FolderPickerActivity) mContainerActivity)
                                         .isDoNotEnterEncryptedFolder()) {
-                            Snackbar.make(getListView(),
+                            Snackbar.make(getRecyclerView(),
                                     R.string.copy_move_to_encrypted_folder_not_supported,
                                     Snackbar.LENGTH_LONG).show();
                         } else {
@@ -941,7 +932,8 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
                     // save index and top position
                     saveIndexAndTopPosition(position);
                 }
-            } else if (!mOnlyFoldersClickable) { // Click on a file
+
+            } else if (!mOnlyFoldersClickable){ // Click on a file
                 if (PreviewImageFragment.canBePreviewed(file)) {
                     // preview image - it handles the download, if needed
                     if (searchFragment) {
@@ -973,13 +965,16 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
                     } else {
                         mContainerActivity.getFileOperationsHelper().openFile(file);
                     }
+
                 } else {
                     // automatic download, preview on finish
                     ((FileDisplayActivity) mContainerActivity).startDownloadForPreview(file);
-                }
+                
+
             }
+}
         } else {
-            Log_OC.d(TAG, "Null object in ListAdapter!!");
+            Log_OC.d(TAG, "Null object in ListAdapter!!");}
         }
     }
 
@@ -990,7 +985,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
                 data.getBooleanExtra(SetupEncryptionDialogFragment.SUCCESS, false)) {
 
             int position = data.getIntExtra(SetupEncryptionDialogFragment.ARG_POSITION, -1);
-            OCFile file = (OCFile) mAdapter.getItem(position);
+            OCFile file = mAdapter.getItem(position);
 
             // update state and view of this fragment
             searchFragment = false;
@@ -1007,18 +1002,18 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
     /**
      * Start the appropriate action(s) on the currently selected files given menu selected by the user.
      *
-     * @param menuId Identifier of the action menu selected by the user
+     * @param menuId       Identifier of the action menu selected by the user
      * @param checkedFiles List of files selected by the user on which the action should be performed
      * @return 'true' if the menu selection started any action, 'false' otherwise.
      */
-    public boolean onFileActionChosen(int menuId, ArrayList<OCFile> checkedFiles) {
+    public boolean onFileActionChosen(int menuId, Set<OCFile> checkedFiles) {
         if (checkedFiles.size() <= 0) {
             return false;
         }
 
         if (checkedFiles.size() == 1) {
             /// action only possible on a single file
-            OCFile singleFile = checkedFiles.get(0);
+            OCFile singleFile = checkedFiles.iterator().next();
             switch (menuId) {
                 case R.id.action_send_share_file: {
                     mContainerActivity.getFileOperationsHelper().sendShareFile(singleFile);
@@ -1044,11 +1039,11 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
                     mContainerActivity.getFileOperationsHelper().setPictureAs(singleFile, getView());
                     return true;
                 }
-                case R.id. action_encrypted: {
+                case R.id.action_encrypted: {
                     mContainerActivity.getFileOperationsHelper().toggleEncryption(singleFile, true);
                     return true;
                 }
-                case R.id. action_unset_encrypted: {
+                case R.id.action_unset_encrypted: {
                     mContainerActivity.getFileOperationsHelper().toggleEncryption(singleFile, false);
                     return true;
                 }
@@ -1058,7 +1053,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         /// actions possible on a batch of files
         switch (menuId) {
             case R.id.action_remove_file: {
-                RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(checkedFiles);
+                RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(new ArrayList<>(checkedFiles));
                 dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
                 return true;
             }
@@ -1089,7 +1084,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             }
             case R.id.action_move: {
                 Intent action = new Intent(getActivity(), FolderPickerActivity.class);
-                action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, checkedFiles);
+                action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, new ArrayList<>(checkedFiles));
                 action.putExtra(FolderPickerActivity.EXTRA_CURRENT_FOLDER, mFile);
                 action.putExtra(FolderPickerActivity.EXTRA_ACTION, FolderPickerActivity.MOVE);
                 getActivity().startActivityForResult(action, FileDisplayActivity.REQUEST_CODE__MOVE_FILES);
@@ -1097,7 +1092,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             }
             case R.id.action_copy: {
                 Intent action = new Intent(getActivity(), FolderPickerActivity.class);
-                action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, checkedFiles);
+                action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, new ArrayList<>(checkedFiles));
                 action.putExtra(FolderPickerActivity.EXTRA_CURRENT_FOLDER, mFile);
                 action.putExtra(FolderPickerActivity.EXTRA_ACTION, FolderPickerActivity.COPY);
                 getActivity().startActivityForResult(action, FileDisplayActivity.REQUEST_CODE__COPY_FILES);
@@ -1194,7 +1189,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
 
                 mAdapter.swapDirectory(directory, storageManager, onlyOnDevice);
                 if (mFile == null || !mFile.equals(directory)) {
-                    mCurrentListView.setSelection(0);
+                    getRecyclerView().scrollToPosition(0);
                 }
                 mFile = directory;
 
@@ -1203,28 +1198,9 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         }
     }
 
-    private void updateFooter() {
-        int filesCount = 0;
-        int foldersCount = 0;
-        int count = mAdapter.getCount();
-        OCFile file;
-        for (int i = 0; i < count; i++) {
-            file = (OCFile) mAdapter.getItem(i);
-            if (file.isFolder()) {
-                foldersCount++;
-            } else {
-                if (!file.isHidden()) {
-                    filesCount++;
-                }
-            }
-        }
-        // set footer text
-        setFooterText(generateFooterText(filesCount, foldersCount));
-    }
 
     private void updateLayout() {
-        
-            updateFooter();
+        if (!mJustFolders) {
             // decide grid vs list view
             if ( AccountUtils.getServerVersion(((FileActivity) mContainerActivity).getAccount())
             .supportsRemoteThumbnails() && isGridViewPreferred(mFile)) {
@@ -1243,40 +1219,6 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         }
     }
 
-    private String generateFooterText(int filesCount, int foldersCount) {
-        String output = "";
-
-        if (getActivity() != null) {
-            if (filesCount + foldersCount <= 0) {
-                output = "";
-            } else if (foldersCount <= 0) {
-                output = getResources().getQuantityString(
-                        R.plurals.file_list__footer__file,
-                        filesCount,
-                        filesCount
-                );
-            } else if (filesCount <= 0) {
-                output = getResources().getQuantityString(
-                        R.plurals.file_list__footer__folder,
-                        foldersCount,
-                        foldersCount
-                );
-            } else {
-                output = getResources().getQuantityString(
-                        R.plurals.file_list__footer__file,
-                        filesCount,
-                        filesCount)
-                        + ", "
-                        + getResources().getQuantityString(
-                        R.plurals.file_list__footer__folder,
-                        foldersCount,
-                        foldersCount
-                );
-            }
-        }
-
-        return output;
-    }
 
     public void sortFiles(FileSortOrder sortOrder) {
         mAdapter.setSortOrder(mFile, sortOrder);
@@ -1298,11 +1240,60 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         switchToListView();
     }
 
+    public void switchToListView() {
+        if (isGridEnabled()) {
+            switchLayoutManager(false);
+        }
+    }
+
     public void setGridAsPreferred() {
         PreferenceManager.setFolderLayout(getActivity(), mFile, FOLDER_LAYOUT_GRID);
         switchToGridView();
     }
 
+    public void switchToGridView() {
+        if (!isGridEnabled()) {
+            switchLayoutManager(true);
+        }
+    }
+
+    public void switchLayoutManager(boolean grid) {
+        int position = 0;
+
+        if (getRecyclerView().getLayoutManager() != null) {
+            position = ((LinearLayoutManager) getRecyclerView().getLayoutManager())
+                    .findFirstCompletelyVisibleItemPosition();
+        }
+
+        RecyclerView.LayoutManager layoutManager;
+        if (grid) {
+            layoutManager = new GridLayoutManager(getContext(), getColumnSize());
+            ((GridLayoutManager) layoutManager).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+                @Override
+                public int getSpanSize(int position) {
+                    if (position == getAdapter().getItemCount() - 1) {
+                        return ((GridLayoutManager) layoutManager).getSpanCount();
+                    } else {
+                        return 1;
+                    }
+                }
+            });
+            
+        } else {
+            layoutManager = new LinearLayoutManager(getContext());
+        }
+
+        getRecyclerView().setLayoutManager(layoutManager);
+        getRecyclerView().scrollToPosition(position);
+        getAdapter().setGridView(grid);
+        getRecyclerView().setAdapter(getAdapter());
+        getAdapter().notifyDataSetChanged();
+    }
+
+    public OCFileListAdapter getAdapter() {
+        return mAdapter;
+    }
+
     private void changeGridIcon(Menu menu) {
         MenuItem menuItem = menu.findItem(R.id.action_switch_view);
         if (menuItem != null) {
@@ -1610,9 +1601,9 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             if (remoteOperationResult.isSuccess()) {
                 mAdapter.setEncryptionAttributeForItemID(event.remoteId, event.shouldBeEncrypted);
             } else if (remoteOperationResult.getHttpCode() == HttpStatus.SC_FORBIDDEN) {
-                Snackbar.make(mCurrentListView, R.string.end_to_end_encryption_folder_not_empty, Snackbar.LENGTH_LONG).show();
+                Snackbar.make(getRecyclerView(), R.string.end_to_end_encryption_folder_not_empty, Snackbar.LENGTH_LONG).show();
             } else {
-                Snackbar.make(mCurrentListView, R.string.common_error_unknown, Snackbar.LENGTH_LONG).show();
+                Snackbar.make(getRecyclerView(), R.string.common_error_unknown, Snackbar.LENGTH_LONG).show();
             }
 
         } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
@@ -1672,8 +1663,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             onMessageEvent(searchEvent);
 
             mRefreshListLayout.setRefreshing(false);
-            mRefreshGridLayout.setRefreshing(false);
-            mRefreshEmptyLayout.setRefreshing(false);
+//            mRefreshEmptyLayout.setRefreshing(false);
         } else {
             super.onRefresh();
         }
@@ -1693,9 +1683,18 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
      * @param select <code>true</code> to select all, <code>false</code> to deselect all
      */
     public void selectAllFiles(boolean select) {
-        AbsListView listView = getListView();
-        for (int position = 0; position < listView.getCount(); position++) {
-            listView.setItemChecked(position, select);
+        OCFileListAdapter ocFileListAdapter = (OCFileListAdapter) getRecyclerView().getAdapter();
+
+        if (select) {
+            ocFileListAdapter.addAllFilesToCheckedFiles();
+        } else {
+            ocFileListAdapter.removeAllFilesFromCheckedFiles();
         }
+
+        for (int i = 0; i < mAdapter.getItemCount(); i++) {
+            mAdapter.notifyItemChanged(i);
+        }
+
+        mActiveActionMode.invalidate();        
     }
 }

+ 0 - 163
src/main/java/com/owncloud/android/ui/fragment/UploadListFragment.java

@@ -1,163 +0,0 @@
-/**
- *   ownCloud Android client application
- *
- *   @author LukeOwncloud
- *   Copyright (C) 2016 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-package com.owncloud.android.ui.fragment;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ExpandableListView;
-import android.widget.ListView;
-
-import com.owncloud.android.R;
-import com.owncloud.android.db.OCUpload;
-import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.ui.activity.FileActivity;
-import com.owncloud.android.ui.adapter.ExpandableUploadListAdapter;
-import com.owncloud.android.utils.AnalyticsUtils;
-
-/**
- * A Fragment that lists all files and folders in a given LOCAL path.
- */
-public class UploadListFragment extends ExpandableListFragment {
-    private static final String TAG = UploadListFragment.class.getSimpleName();
-
-    private static final String SCREEN_NAME = "Uploads";
-
-    /**
-     * Reference to the Activity which this fragment is attached to.
-     * For callbacks.
-     */
-    private UploadListFragment.ContainerActivity mContainerActivity;
-
-    private ExpandableUploadListAdapter mAdapter;
-
-    /** Is binder ready in the Activity? */
-    private boolean mBinderReady = false;
-
-    public void setBinderReady(boolean ready) {
-        mBinderReady = ready;
-    }
-    public boolean isBinderReady(){
-        return mBinderReady;
-    }
-
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        View v = super.onCreateView(inflater, container, savedInstanceState);
-        getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
-        setMessageForEmptyList(
-                R.string.upload_list_empty_headline, R.string.upload_list_empty_text_auto_upload,
-                R.drawable.ic_list_empty_upload, true
-        );
-        setOnRefreshListener(this);
-        return v;
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (getActivity() != null) {
-            AnalyticsUtils.setCurrentScreenName(getActivity(), SCREEN_NAME, TAG);
-        }
-    }
-
-    @Override
-    public void onRefresh() {
-        // remove the progress circle as soon as pull is triggered, like in the list of files
-        mRefreshEmptyLayout.setRefreshing(false);
-        mRefreshListLayout.setRefreshing(false);
-
-        mAdapter.notifyDataSetChanged();
-    }
-
-    @Override
-    public void onAttach(Activity activity) {
-        super.onAttach(activity);
-        try {
-            mContainerActivity = (ContainerActivity) activity;
-        } catch (ClassCastException e) {
-            throw new ClassCastException(activity.toString() + " must implement "
-                    + UploadListFragment.ContainerActivity.class.getSimpleName());
-        }
-    }
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        Log_OC.d(TAG, "onActivityCreated() start");
-        super.onActivityCreated(savedInstanceState);
-
-    }
-
-    @Override
-    public void onStart() {
-        Log_OC.d(TAG, "onStart() start");
-        super.onStart();
-        mAdapter = new ExpandableUploadListAdapter((FileActivity)getActivity());
-        setListAdapter(mAdapter);
-    }
-
-    @Override
-    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
-        boolean handled = false;
-        OCUpload OCUpload = (OCUpload) mAdapter.getChild(groupPosition, childPosition);
-        if (OCUpload != null) {
-            // notify the click to container Activity
-            handled = mContainerActivity.onUploadItemClick(OCUpload);
-        } else {
-            Log_OC.w(TAG, "Null object in ListAdapter!!");
-        }
-        return handled;
-    }
-
-    /**
-     * Interface to implement by any Activity that includes some instance of
-     * UploadListFragment
-     * 
-     * @author LukeOwncloud
-     */
-    public interface ContainerActivity {
-
-        /**
-         * Callback method invoked when an upload item is clicked by the user on
-         * the upload list
-         *
-         * @param file the file that has been clicked on.
-         * @return return true if click was handled.
-         */
-        public boolean onUploadItemClick(OCUpload file);
-    }
-
-    public void binderReady(){
-        setBinderReady(true);
-
-        if (mAdapter != null) {
-            mAdapter.addBinder();
-        }
-    }
-
-    public void updateUploads(){
-        if (mAdapter != null) {
-            mAdapter.refreshView();
-        }
-    }
-}

+ 8 - 7
src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java

@@ -59,11 +59,11 @@ import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.PermissionUtil;
 import com.owncloud.android.utils.ThemeUtils;
 
+import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
-import java.util.Vector;
 
 import butterknife.BindView;
 import butterknife.ButterKnife;
@@ -223,7 +223,7 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
                 if (result) {
                     OCFile backupFolder = contactsPreferenceActivity.getStorageManager().getFileByPath(backupFolderPath);
 
-                    Vector<OCFile> backupFiles = contactsPreferenceActivity.getStorageManager()
+                    ArrayList<OCFile> backupFiles = contactsPreferenceActivity.getStorageManager()
                             .getFolderContent(backupFolder, false);
 
                     if (backupFiles == null || backupFiles.size() == 0) {
@@ -387,7 +387,7 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
         String backupFolderString = getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR;
         OCFile backupFolder = contactsPreferenceActivity.getStorageManager().getFileByPath(backupFolderString);
 
-        Vector<OCFile> backupFiles = contactsPreferenceActivity.getStorageManager().getFolderContent(backupFolder,
+        ArrayList<OCFile> backupFiles = contactsPreferenceActivity.getStorageManager().getFolderContent(backupFolder,
                 false);
 
         Collections.sort(backupFiles, new Comparator<OCFile>() {
@@ -420,10 +420,11 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
             day = savedDate.getDay();
         }
 
-        if (backupFiles.size() > 0 && backupFiles.lastElement() != null) {
+        if (backupFiles.size() > 0 && backupFiles.get(backupFiles.size() - 1) != null) {
             datePickerDialog = new DatePickerDialog(contactsPreferenceActivity, this, year, month, day);
-            datePickerDialog.getDatePicker().setMaxDate(backupFiles.lastElement().getModificationTimestamp());
-            datePickerDialog.getDatePicker().setMinDate(backupFiles.firstElement().getModificationTimestamp());
+            datePickerDialog.getDatePicker().setMaxDate(backupFiles.get(backupFiles.size() - 1)
+                    .getModificationTimestamp());
+            datePickerDialog.getDatePicker().setMinDate(backupFiles.get(0).getModificationTimestamp());
 
             datePickerDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
                 @Override
@@ -468,7 +469,7 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
 
         String backupFolderString = getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR;
         OCFile backupFolder = contactsPreferenceActivity.getStorageManager().getFileByPath(backupFolderString);
-        Vector<OCFile> backupFiles = contactsPreferenceActivity.getStorageManager().getFolderContent(
+        ArrayList<OCFile> backupFiles = contactsPreferenceActivity.getStorageManager().getFolderContent(
                 backupFolder, false);
 
         // find file with modification with date and time between 00:00 and 23:59

+ 36 - 0
src/main/java/com/owncloud/android/ui/interfaces/LocalFileListFragmentInterface.java

@@ -0,0 +1,36 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 Mario Danic
+ * Copyright (C) 2017 Nextcloud GmbH
+ * <p>
+ * 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.
+ * <p>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.interfaces;
+
+import com.owncloud.android.ui.adapter.LocalFileListAdapter;
+
+import java.io.File;
+
+/**
+ * Interface for communication between {@link com.owncloud.android.ui.fragment.LocalFileListFragment}
+ * and {@link LocalFileListAdapter}
+ */
+
+public interface LocalFileListFragmentInterface {
+    int getColumnSize();
+
+    void onItemClicked(File file);
+}

+ 7 - 4
src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java

@@ -23,18 +23,21 @@ package com.owncloud.android.ui.interfaces;
 import android.view.View;
 
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.ui.adapter.OCFileListAdapter;
 
 /**
  * Interface for communication between {@link com.owncloud.android.ui.fragment.OCFileListFragment}
- * and {@link com.owncloud.android.ui.adapter.FileListListAdapter}
+ * and {@link OCFileListAdapter}
  */
 
 public interface OCFileListFragmentInterface {
-    void finishedFiltering();
-
     int getColumnSize();
 
     void onShareIconClick(OCFile file);
 
-    void onOverflowIconClick(View view, OCFile file);
+    void onOverflowIconClicked(OCFile file, View view);
+
+    void onItemClicked(OCFile file);
+
+    boolean onLongItemClicked(OCFile file);
 }

+ 3 - 3
src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java

@@ -35,9 +35,9 @@ import com.owncloud.android.ui.fragment.FileFragment;
 import com.owncloud.android.utils.FileSortOrder;
 import com.owncloud.android.utils.FileStorageUtils;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Set;
-import java.util.Vector;
 
 import javax.annotation.Nullable;
 
@@ -45,8 +45,8 @@ import javax.annotation.Nullable;
  * Adapter class that provides Fragment instances
  */
 public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
-    
-    private Vector<OCFile> mImageFiles;
+
+    private ArrayList<OCFile> mImageFiles;
     private Account mAccount;
     private Set<Object> mObsoleteFragments;
     private Set<Integer> mObsoletePositions;

+ 11 - 14
src/main/java/com/owncloud/android/utils/FileSortOrder.java

@@ -23,11 +23,10 @@ package com.owncloud.android.utils;
 import com.owncloud.android.datamodel.OCFile;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Vector;
 
 /**
  * Sort order 
@@ -60,11 +59,11 @@ public class FileSortOrder {
         mAscending = ascending;
     }
 
-    public Vector<OCFile> sortCloudFiles(Vector<OCFile> files) {
+    public ArrayList<OCFile> sortCloudFiles(ArrayList<OCFile> files) {
         return sortCloudFilesByFavourite(files);
     }
 
-    public File[] sortLocalFiles(File[] files) {
+    public ArrayList<File> sortLocalFiles(ArrayList<File> files) {
         return files;
     }
 
@@ -73,18 +72,16 @@ public class FileSortOrder {
      *
      * @param files files to sort
      */
-    public static Vector<OCFile> sortCloudFilesByFavourite(Vector<OCFile> files) {
-        Collections.sort(files, new Comparator<OCFile>() {
-            public int compare(OCFile o1, OCFile o2) {
-                if (o1.getIsFavorite() && o2.getIsFavorite()) {
-                    return 0;
-                } else if (o1.getIsFavorite()) {
-                    return -1;
-                } else if (o2.getIsFavorite()) {
-                    return 1;
-                }
+    public static ArrayList<OCFile> sortCloudFilesByFavourite(ArrayList<OCFile> files) {
+        Collections.sort(files, (o1, o2) -> {
+            if (o1.getIsFavorite() && o2.getIsFavorite()) {
                 return 0;
+            } else if (o1.getIsFavorite()) {
+                return -1;
+            } else if (o2.getIsFavorite()) {
+                return 1;
             }
+            return 0;
         });
 
         return files;

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

@@ -28,7 +28,6 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
-import java.util.Vector;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
@@ -47,7 +46,7 @@ public class FileSortOrderByDate extends FileSortOrder {
      *
      * @param files list of files to sort
      */
-    public Vector<OCFile> sortCloudFiles(Vector<OCFile> files) {
+    public ArrayList<OCFile> sortCloudFiles(ArrayList<OCFile> files) {
         final int multiplier = mAscending ? 1 : -1;
 
         Collections.sort(files, new Comparator<OCFile>() {

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

@@ -28,7 +28,6 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
-import java.util.Vector;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import third_parties.daveKoeller.AlphanumComparator;
@@ -49,7 +48,7 @@ public class FileSortOrderByName extends FileSortOrder {
      * @param files files to sort
      */
     @SuppressFBWarnings(value = "Bx")
-    public Vector<OCFile> sortCloudFiles(Vector<OCFile> files) {
+    public ArrayList<OCFile> sortCloudFiles(ArrayList<OCFile> files) {
         final int multiplier = mAscending ? 1 : -1;
 
         Collections.sort(files, new Comparator<OCFile>() {

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

@@ -28,7 +28,6 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
-import java.util.Vector;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
@@ -47,7 +46,7 @@ public class FileSortOrderBySize extends FileSortOrder {
      *
      * @param files list of files to sort
      */
-    public Vector<OCFile> sortCloudFiles(Vector<OCFile> files) {
+    public ArrayList<OCFile> sortCloudFiles(ArrayList<OCFile> files) {
         final int multiplier = mAscending ? 1 : -1;
 
         Collections.sort(files, new Comparator<OCFile>() {

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

@@ -38,12 +38,11 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.Date;
 import java.util.Locale;
 import java.util.TimeZone;
-import java.util.Vector;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
@@ -197,14 +196,12 @@ public class FileStorageUtils {
         return file;
     }
 
-    public static Vector<OCFile> sortOcFolderDescDateModified(Vector<OCFile> files) {
+    public static ArrayList<OCFile> sortOcFolderDescDateModified(ArrayList<OCFile> files) {
         final int multiplier = -1;
-        Collections.sort(files, new Comparator<OCFile>() {
+        Collections.sort(files, (o1, o2) -> {
             @SuppressFBWarnings(value = "Bx", justification = "Would require stepping up API level")
-            public int compare(OCFile o1, OCFile o2) {
-                Long obj1 = o1.getModificationTimestamp();
-                return multiplier * obj1.compareTo(o2.getModificationTimestamp());
-            }
+            Long obj1 = o1.getModificationTimestamp();
+            return multiplier * obj1.compareTo(o2.getModificationTimestamp());
         });
 
         return FileSortOrder.sortCloudFilesByFavourite(files);

+ 0 - 96
src/main/res/layout/grid_image.xml

@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ownCloud Android client application
-  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="match_parent"
-    android:layout_gravity="center_horizontal"
-    android:background="@drawable/list_selector"
-    android:gravity="center_horizontal"
-    android:orientation="vertical">
-
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" >
-
-        <com.owncloud.android.ui.SquareImageView
-            android:id="@+id/thumbnail"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:paddingLeft="@dimen/alternate_padding"
-            android:paddingRight="@dimen/alternate_padding"
-            android:scaleType="centerCrop"
-            android:src="@drawable/folder"/>
-
-
-        <ImageView
-            android:id="@+id/favorite_action"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="top|end"
-            android:layout_margin="@dimen/standard_quarter_margin"
-            android:src="@drawable/ic_favorite"
-            android:contentDescription="@string/favorite_icon"/>
-
-        <ImageView
-            android:id="@+id/sharedIcon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="top|end"
-            android:layout_marginTop="24dp"
-            android:layout_marginRight="4dp"
-            android:layout_marginEnd="4dp"
-            android:src="@drawable/shared_via_link"
-            android:contentDescription="@string/shared_via_link_icon"/>
-
-        <ImageView
-            android:id="@+id/localFileIndicator"
-            android:layout_width="16dp"
-            android:layout_height="16dp"
-            android:layout_gravity="bottom|end"
-            android:layout_marginBottom="@dimen/standard_quarter_margin"
-            android:layout_marginRight="@dimen/standard_quarter_margin"
-            android:layout_marginEnd="@dimen/standard_quarter_margin"
-            android:layout_marginTop="@dimen/standard_quarter_margin"
-            android:src="@drawable/ic_synced"
-            android:contentDescription="@string/synced_icon"/>
-
-        <ImageView
-            android:id="@+id/keptOfflineIcon"
-            android:layout_width="16dp"
-            android:layout_height="16dp"
-            android:layout_gravity="bottom|end"
-            android:layout_marginBottom="@dimen/standard_quarter_margin"
-            android:layout_marginRight="24dp"
-            android:layout_marginEnd="24dp"
-            android:src="@drawable/ic_available_offline"
-            android:contentDescription="@string/available_offline_icon"/>
-
-        <ImageView
-            android:id="@+id/custom_checkbox"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="@dimen/standard_quarter_margin"
-            android:layout_marginRight="@dimen/standard_quarter_margin"
-            android:layout_gravity="center_vertical|top"
-            android:src="@android:drawable/checkbox_off_background"
-            android:contentDescription="@string/checkbox"/>
-    </FrameLayout>
-
-</LinearLayout>

+ 28 - 65
src/main/res/layout/list_fragment.xml

@@ -22,119 +22,82 @@
                 android:layout_width="match_parent"
                 android:layout_height="match_parent">
 
-<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:id="@+id/list_fragment_layout">
 
-    <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" >
+    <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                                                     android:id="@+id/list_fragment_layout"
+                                                     android:layout_width="match_parent"
+                                                     android:layout_height="match_parent">
 
-        <com.owncloud.android.ui.ExtendedListView
-            android:id="@+id/list_root"
+        <android.support.v4.widget.SwipeRefreshLayout
+            android:id="@+id/swipe_containing_list"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:divider="@color/list_divider_background"
-            android:dividerHeight="1px"
-            android:visibility="visible"
-            android:descendantFocusability="blocksDescendants"/>
-
-    </android.support.v4.widget.SwipeRefreshLayout>
+            android:footerDividersEnabled="false"
+            android:visibility="visible">
 
-    <android.support.v4.widget.SwipeRefreshLayout
-        android:id="@+id/swipe_containing_grid"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:footerDividersEnabled="false"
-        android:visibility="gone" >
-
-        <third_parties.in.srain.cube.GridViewWithHeaderAndFooter
-            android:id="@+id/grid_root"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:columnWidth="@dimen/list_fragment_column_width"
-            android:gravity="center"
-            android:horizontalSpacing="@dimen/list_fragment_spacing"
-            android:stretchMode="columnWidth"
-            android:verticalSpacing="@dimen/list_fragment_spacing"
-            android:visibility="visible"/>
-
-    </android.support.v4.widget.SwipeRefreshLayout>
-
-    <android.support.v4.widget.SwipeRefreshLayout
-        android:id="@+id/swipe_containing_empty"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="gone" >
-            <ScrollView
+            <com.owncloud.android.ui.EmptyRecyclerView
+                android:id="@+id/list_root"
                 android:layout_width="match_parent"
-                android:layout_height="match_parent" >
+                android:layout_height="match_parent"
+                />
+        </android.support.v4.widget.SwipeRefreshLayout>
 
-                <include layout="@layout/empty_list" />
+        <include layout="@layout/empty_list"/>
 
-            </ScrollView>
-    </android.support.v4.widget.SwipeRefreshLayout>
-
-
-</android.support.design.widget.CoordinatorLayout>
+    </android.support.design.widget.CoordinatorLayout>
 
     <com.getbase.floatingactionbutton.FloatingActionsMenu
         android:id="@+id/fab_main"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignParentRight="true"
+        android:layout_above="@+id/bottom_navigation_view"
         android:layout_alignParentEnd="true"
-        app:fab_labelStyle="@style/menu_labels_style"
+        android:layout_alignParentRight="true"
         android:layout_marginBottom="@dimen/standard_margin"
-        android:layout_marginRight="@dimen/standard_margin"
         android:layout_marginEnd="@dimen/standard_margin"
-        android:layout_above="@+id/bottom_navigation_view"
-        android:visibility="gone">
+        android:layout_marginRight="@dimen/standard_margin"
+        android:visibility="gone"
+        app:fab_labelStyle="@style/menu_labels_style">
 
         <com.getbase.floatingactionbutton.FloatingActionButton
             android:id="@+id/fab_upload"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            app:fab_size="mini"
-            app:fab_icon="@drawable/ic_action_upload"
             app:fab_colorNormal="@color/primary_button_background_color"
             app:fab_colorPressed="@color/primary"
+            app:fab_icon="@drawable/ic_action_upload"
+            app:fab_size="mini"
             app:fab_title=""/>
 
         <com.getbase.floatingactionbutton.FloatingActionButton
             android:id="@+id/fab_upload_from_app"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            app:fab_size="mini"
-            app:fab_icon="@drawable/ic_import"
             app:fab_colorNormal="@color/primary_button_background_color"
             app:fab_colorPressed="@color/primary"
+            app:fab_icon="@drawable/ic_import"
+            app:fab_size="mini"
             app:fab_title=""/>
 
         <com.getbase.floatingactionbutton.FloatingActionButton
             android:id="@+id/fab_mkdir"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            app:fab_size="mini"
-            app:fab_icon="@drawable/ic_action_create_dir"
             app:fab_colorNormal="@color/primary_button_background_color"
             app:fab_colorPressed="@color/primary"
+            app:fab_icon="@drawable/ic_action_create_dir"
+            app:fab_size="mini"
             app:fab_title=""/>
 
     </com.getbase.floatingactionbutton.FloatingActionsMenu>
 
     <android.support.design.widget.BottomNavigationView
         android:id="@+id/bottom_navigation_view"
-        android:visibility="gone"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="bottom"
         android:layout_alignParentBottom="true"
+        android:layout_gravity="bottom"
+        android:visibility="gone"
         app:itemBackground="@color/primary_button_background_color"
         app:itemIconTint="@color/primary_button_text_color"
         app:itemTextColor="@color/primary_button_text_color"

+ 16 - 0
src/main/res/layout/upload_list_header.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/ListItemLayout"
+              android:layout_width="match_parent"
+              android:orientation="horizontal"
+              android:layout_height="wrap_content"
+              android:paddingTop="@dimen/standard_half_padding"
+              android:paddingBottom="@dimen/standard_half_padding"
+    >
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/upload_list_title"/>
+
+</LinearLayout>

+ 7 - 10
src/main/res/layout/upload_list_item.xml

@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/ListItemLayout"
-    android:layout_width="match_parent"
-    android:orientation="horizontal"
-    android:layout_height="wrap_content"
-    android:paddingTop="@dimen/standard_half_padding"
-    android:paddingBottom="@dimen/standard_half_padding"
+              android:id="@+id/upload_list_item_layout"
+              android:layout_width="match_parent"
+              android:orientation="horizontal"
+              android:layout_height="wrap_content"
+              android:paddingTop="@dimen/standard_half_padding"
+              android:paddingBottom="@dimen/standard_half_padding"
     >
 
     <FrameLayout
@@ -103,7 +103,6 @@
             android:textSize="@dimen/upload_list_item_text_size"/>
 
     </LinearLayout>
-
     
     <FrameLayout
         android:layout_width="@dimen/min_list_item_size"
@@ -123,8 +122,6 @@
             android:layout_gravity="center"
             android:background="@android:color/transparent"
             android:contentDescription="@string/upload_item_action_button"/>
-
 	</FrameLayout>
-    
-    
+
 </LinearLayout>

+ 34 - 10
src/main/res/layout/upload_list_layout.xml

@@ -18,30 +18,54 @@
   -->
 <android.support.v4.widget.DrawerLayout 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"
                                         android:id="@+id/drawer_layout"
                                         android:layout_width="match_parent"
                                         android:layout_height="match_parent"
                                         android:clickable="true"
                                         android:fitsSystemWindows="true"
-                                        android:focusable="true">
+                                        tools:openDrawer="start">
 
-    <!-- The main content view -->
-    <RelativeLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical">
+    <android.support.design.widget.CoordinatorLayout 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"
+                                                     android:layout_width="match_parent"
+                                                     android:layout_height="match_parent">
 
         <include
             android:id="@+id/navigation_bar"
             layout="@layout/toolbar_standard"/>
 
         <FrameLayout
-            android:id="@+id/upload_list_fragment"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:background="@color/background_color"
             android:layout_above="@+id/bottom_navigation_view"
-            android:layout_below="@+id/navigation_bar" />
+            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:layout_marginBottom="-3dp"
+                    android:layout_marginLeft="-3dp"
+                    android:layout_marginRight="-3dp"
+                    android:clipToPadding="false"
+                    android:scrollbarStyle="outsideOverlay"
+                    android:scrollbars="vertical"
+                    android:visibility="visible"/>
+
+            </android.support.v4.widget.SwipeRefreshLayout>
+
+            <include layout="@layout/empty_list"/>
+
+        </FrameLayout>
 
         <android.support.design.widget.BottomNavigationView
             android:id="@+id/bottom_navigation_view"
@@ -54,7 +78,7 @@
             app:itemTextColor="@color/primary_button_text_color"
             app:menu="@menu/navigation_bar_menu"/>
 
-    </RelativeLayout>
+    </android.support.design.widget.CoordinatorLayout>
 
     <include
         layout="@layout/drawer"

+ 1 - 1
src/main/res/values-ast/strings.xml

@@ -353,7 +353,7 @@
     <string name="manage_space_title">Alministrar espaciu</string>
     <string name="manage_space_clear_data">Llimpiar datos</string>
 
-    <string name="local_file_not_found_toast">Nun s\'alcontró\'l ficheru nel sistema llocal de ficheros</string>
+    <string name="local_file_not_found_message">Nun s\'alcontró\'l ficheru nel sistema llocal de ficheros</string>
     <string name="actionbar_search">Guetar</string>
     <string name="learn_more">Deprendi más</string>
     <string name="participate_release_candidate_headline">Llanzamientu candidatu</string>

+ 1 - 1
src/main/res/values-b+en+001/strings.xml

@@ -518,7 +518,7 @@
     <string name="manage_space_clear_data">Clear data</string>
 
     <string name="permission_storage_access">Additional permissions required to upload and download files.</string>
-    <string name="local_file_not_found_toast">File not found in local file system</string>
+    <string name="local_file_not_found_message">File not found in local file system</string>
     <string name="confirmation_remove_files_alert">Do you really want to delete the selected items?</string>
     <string name="confirmation_remove_folders_alert">Do you really want to delete the selected items and their contents?</string>
     <string name="maintenance_mode">Server in maintenance mode</string>

+ 1 - 1
src/main/res/values-b+es+419/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-bg-rBG/strings.xml

@@ -519,7 +519,7 @@
     <string name="manage_space_clear_data">Изчисти данни</string>
 
     <string name="permission_storage_access">Изискват се допълнителни права за изтегляне и сваляне на файлове.</string>
-    <string name="local_file_not_found_toast">Файлът не е намерен в локалната система за файлове</string>
+    <string name="local_file_not_found_message">Файлът не е намерен в локалната система за файлове</string>
     <string name="confirmation_remove_files_alert">Наистина ли желаете избраните елементи да бъдат премахнати?</string>
     <string name="confirmation_remove_folders_alert">Наистина ли желаете избраните елементи и съдържанието им да бъдат премахнати?</string>
     <string name="maintenance_mode">Режимът за поддръжка е включен</string>

+ 1 - 1
src/main/res/values-cs-rCZ/strings.xml

@@ -456,7 +456,7 @@
     <string name="manage_space_clear_data">Vyčistit data</string>
 
     <string name="permission_storage_access">Vyžadována dodatečná oprávnění pro nahrávání &amp; stahování souborů.</string>
-    <string name="local_file_not_found_toast">Soubor nebyl nalezen v místním souborovém systému</string>
+    <string name="local_file_not_found_message">Soubor nebyl nalezen v místním souborovém systému</string>
     <string name="confirmation_remove_files_alert">Opravdu chcete odstranit vybrané položky?</string>
     <string name="confirmation_remove_folders_alert">Opravdu chcete odstranit vybrané položky a jejich obsah?</string>
     <string name="maintenance_mode">Server je v režimu údržby</string>

+ 1 - 1
src/main/res/values-de-rDE/strings.xml

@@ -518,7 +518,7 @@
     <string name="manage_space_clear_data">Daten löschen</string>
 
     <string name="permission_storage_access">Sie haben nicht die erforderlichen Rechte, um Dateien hoch- oder herunterzuladen.</string>
-    <string name="local_file_not_found_toast">Die Datei wurde im lokalen Dateisystem nicht gefunden</string>
+    <string name="local_file_not_found_message">Die Datei wurde im lokalen Dateisystem nicht gefunden</string>
     <string name="confirmation_remove_files_alert">Möchten Sie die ausgewählten Elemente wirklich löschen?</string>
     <string name="confirmation_remove_folders_alert">Möchten Sie die ausgewählten Elemente und deren inhalt wirklich löschen?</string>
     <string name="maintenance_mode">Server befindet sich im Wartungsmodus</string>

+ 1 - 1
src/main/res/values-de/strings.xml

@@ -519,7 +519,7 @@
     <string name="manage_space_clear_data">Daten löschen</string>
 
     <string name="permission_storage_access">Du hast nicht die erforderlichen Rechte, um Dateien hoch- oder herunterzuladen.</string>
-    <string name="local_file_not_found_toast">Die Datei wurde im lokalen Dateisystem nicht gefunden</string>
+    <string name="local_file_not_found_message">Die Datei wurde im lokalen Dateisystem nicht gefunden</string>
     <string name="confirmation_remove_files_alert">Möchtest du die ausgewählten Elemente wirklich löschen?</string>
     <string name="confirmation_remove_folders_alert">Möchtest du die ausgewählten Elemente und deren Inhalt wirklich löschen?</string>
     <string name="maintenance_mode">Der Server ist im Wartungsmodus

+ 1 - 1
src/main/res/values-el/strings.xml

@@ -441,7 +441,7 @@
     <string name="manage_space_clear_data">Εκκαθάριση δεδομένων</string>
 
     <string name="permission_storage_access">Επιπλέον διακαιώματα απαιτούνται για μεταφόρτωση και λήψη αρχείων.</string>
-    <string name="local_file_not_found_toast">Το αρχείο δεν βρέθηκε στο τοπικό σύστημα αρχείων</string>
+    <string name="local_file_not_found_message">Το αρχείο δεν βρέθηκε στο τοπικό σύστημα αρχείων</string>
     <string name="maintenance_mode">Ο διακομιστής είναι σε κατάσταση συντήρησης</string>
 
     <string name="uploads_view_upload_status_waiting_for_charging">Αναμονή για φόρτιση συσκευής</string>

+ 1 - 1
src/main/res/values-es-rAR/strings.xml

@@ -409,7 +409,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="maintenance_mode">El servidor se encuentra en modo mantenimiento</string>
 
     <string name="uploads_view_upload_status_waiting_for_charging">Aguardando la regarga del dispositivo </string>

+ 1 - 1
src/main/res/values-es-rCL/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rCO/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rCR/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rDO/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rEC/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rGT/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rHN/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rMX/strings.xml

@@ -518,7 +518,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rNI/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rPA/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rPE/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rPR/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rPY/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rSV/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es-rUY/strings.xml

@@ -450,7 +450,7 @@
     <string name="manage_space_clear_data">Borrar datos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para cargar y descargar archivos. </string>
-    <string name="local_file_not_found_toast">No se encontró el archivo en sistema de archivos local</string>
+    <string name="local_file_not_found_message">No se encontró el archivo en sistema de archivos local</string>
     <string name="confirmation_remove_files_alert">¿Realmente deseas borrar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">El servidor está en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-es/strings.xml

@@ -518,7 +518,7 @@
     <string name="manage_space_clear_data">Limpiar datos</string>
 
     <string name="permission_storage_access">Se necesitan permisos adicionales para subir y descargar archivos.</string>
-    <string name="local_file_not_found_toast">El archivo no se encuentra en el sistema de archivos local.</string>
+    <string name="local_file_not_found_message">El archivo no se encuentra en el sistema de archivos local.</string>
     <string name="confirmation_remove_files_alert">¿Estás seguro de que quieres eliminar los elementos seleccionados?</string>
     <string name="confirmation_remove_folders_alert">¿Estás seguro de que quieres eliminar los elementos seleccionados y sus contenidos?</string>
     <string name="maintenance_mode">Servidor en modo mantenimiento</string>

+ 1 - 1
src/main/res/values-eu/strings.xml

@@ -381,7 +381,7 @@ Mesedez, baimendu berriz</string>
     <string name="manage_space_clear_data">Datuak garbitu</string>
 
     <string name="permission_storage_access">Fitxategiak igo eta jaisteko baimen gehigarriak behar dira</string>
-    <string name="local_file_not_found_toast">Ez da fitxategia topatu fitxategi-sistema lokalean</string>
+    <string name="local_file_not_found_message">Ez da fitxategia topatu fitxategi-sistema lokalean</string>
     <string name="confirmation_remove_files_alert">Hautatutako elementuak benetan borratu nahi dituzu?</string>
     <string name="confirmation_remove_folders_alert">Hautatutako elementuak eta bere edukiak benetan borratu nahi dituzu?</string>
     <string name="maintenance_mode">Zerbitzaria mantentze moduan dago</string>

+ 1 - 1
src/main/res/values-fi-rFI/strings.xml

@@ -500,7 +500,7 @@
     <string name="manage_space_clear_data">Tyhjennä tiedot</string>
 
     <string name="permission_storage_access">Tiedostojen lähetys ja lataaminen vaatii lisäoikeuksia.</string>
-    <string name="local_file_not_found_toast">Tiedostoa ei löytynyt paikallisesta tiedostojärjestelmästä</string>
+    <string name="local_file_not_found_message">Tiedostoa ei löytynyt paikallisesta tiedostojärjestelmästä</string>
     <string name="confirmation_remove_files_alert">Haluatko varmasti poistaa valitut kohteet?</string>
     <string name="confirmation_remove_folders_alert">Haluatko varmasti poistaa valitut kohteet ja niiden sisällön?</string>
     <string name="maintenance_mode">Palvelin on huoltotilassa</string>

+ 1 - 1
src/main/res/values-fr/strings.xml

@@ -521,7 +521,7 @@ Veuillez contacter votre administrateur</string>
     <string name="manage_space_clear_data">Effacer les données</string>
 
     <string name="permission_storage_access">Des permissions supplémentaires sont exigées pour téléverser et télécharger des fichiers.</string>
-    <string name="local_file_not_found_toast">Le fichier n\'a pas été trouvé sur le système de fichier local</string>
+    <string name="local_file_not_found_message">Le fichier n\'a pas été trouvé sur le système de fichier local</string>
     <string name="confirmation_remove_files_alert">Souhaitez-vous vraiment supprimer les éléments sélectionnés ?</string>
     <string name="confirmation_remove_folders_alert">Souhaitez-vous vraiment supprimer les éléments sélectionnés ainsi que leurs contenus ?</string>
     <string name="maintenance_mode">Serveur en maintenance</string>

+ 1 - 1
src/main/res/values-hu-rHU/strings.xml

@@ -520,7 +520,7 @@ Lépj kapcsolatba a rendszergazdával</string>
     <string name="manage_space_clear_data">Adatok törlése</string>
 
     <string name="permission_storage_access">További jogokra van szükség a fel és letöltéshez</string>
-    <string name="local_file_not_found_toast">Fájl nem található a helyi fájlrendszeren</string>
+    <string name="local_file_not_found_message">Fájl nem található a helyi fájlrendszeren</string>
     <string name="confirmation_remove_files_alert">Valóban törölni akarod a kijelölt elemeket?</string>
     <string name="confirmation_remove_folders_alert">Valóban törölni akarod a kijelölt elemeket és tartalmukat?</string>
     <string name="maintenance_mode">Szerver karbantartási üzemmódban</string>

+ 1 - 1
src/main/res/values-id/strings.xml

@@ -410,7 +410,7 @@
     <string name="manage_space_clear_data">Bersihkan data</string>
 
     <string name="permission_storage_access">Diperlukan ijin tambahan untuk mengunggah dan mengunduh berkas.</string>
-    <string name="local_file_not_found_toast">Berkas tidak ditemukan di sistem berkas lokal</string>
+    <string name="local_file_not_found_message">Berkas tidak ditemukan di sistem berkas lokal</string>
     <string name="maintenance_mode">Server dalam proses perawatan.</string>
 
     <string name="uploads_view_upload_status_waiting_for_charging">Menunggu dicas.</string>

+ 1 - 1
src/main/res/values-is/strings.xml

@@ -518,7 +518,7 @@
     <string name="manage_space_clear_data">Hreinsa gögn</string>
 
     <string name="permission_storage_access">Krafist er viðbótarheimilda til að senda inn og sækja skrár.</string>
-    <string name="local_file_not_found_toast">Skrá fannst ekki á staðværu skráakerfi</string>
+    <string name="local_file_not_found_message">Skrá fannst ekki á staðværu skráakerfi</string>
     <string name="confirmation_remove_files_alert">Ertu viss um að þú viljir eyða völdum atriðum?</string>
     <string name="confirmation_remove_folders_alert">Ertu viss um að þú viljir eyða völdum atriðum og innihaldi þeirra?</string>
     <string name="maintenance_mode">Þjónn í viðhaldsham</string>

+ 1 - 1
src/main/res/values-it/strings.xml

@@ -518,7 +518,7 @@
     <string name="manage_space_clear_data">Cancella i dati</string>
 
     <string name="permission_storage_access">Permessi aggiuntivi richiesti per caricare e scaricare i file.</string>
-    <string name="local_file_not_found_toast">File non trovato nel file system locale</string>
+    <string name="local_file_not_found_message">File non trovato nel file system locale</string>
     <string name="confirmation_remove_files_alert">Vuoi davvero eliminare gli elementi selezionati?</string>
     <string name="confirmation_remove_folders_alert">Vuoi davvero eliminare gli elementi selezionati e il loro contenuto?</string>
     <string name="maintenance_mode">Server in manutenzione</string>

+ 1 - 1
src/main/res/values-ka-rGE/strings.xml

@@ -514,7 +514,7 @@
     <string name="manage_space_clear_data">მონაცემების გასუფთავება</string>
 
     <string name="permission_storage_access">ფაილების ატვირთვისა და გადმოწერისათვის საჭიროა დამატებითი უფლებები.</string>
-    <string name="local_file_not_found_toast">ლოკალურ ფაილ-სისტემაში ფაილი ვერ მოიძებნა</string>
+    <string name="local_file_not_found_message">ლოკალურ ფაილ-სისტემაში ფაილი ვერ მოიძებნა</string>
     <string name="confirmation_remove_files_alert">დარწმუნებული ხართ, რომ გსურთ არჩეული ჩანაწერების გაუქმება?</string>
     <string name="confirmation_remove_folders_alert">დარწმუნებული ხართ, რომ გსურთ არჩეული ჩანაწერებისა და მათი შემცველობის გაუქმება?</string>
     <string name="maintenance_mode">სერვერი სარემონტო რეჟიმშია</string>

+ 1 - 1
src/main/res/values-ko/strings.xml

@@ -510,7 +510,7 @@
     <string name="manage_space_clear_data">데이터 지우기</string>
 
     <string name="permission_storage_access">파일을 업로드 및 다운로드하려면 추가 권한이 필요합니다.</string>
-    <string name="local_file_not_found_toast">로컬 파일 시스템에서 파일을 찾을 수 없음</string>
+    <string name="local_file_not_found_message">로컬 파일 시스템에서 파일을 찾을 수 없음</string>
     <string name="confirmation_remove_files_alert">선택한 항목을 삭제하시겠습니까?</string>
     <string name="confirmation_remove_folders_alert">선택한 항목과 포함된 내용을 삭제하시겠습니까?</string>
     <string name="maintenance_mode">서버가 유지 관리 모드임</string>

+ 1 - 1
src/main/res/values-lt-rLT/strings.xml

@@ -424,7 +424,7 @@
     <string name="manage_space_clear_data">Išvalyti duomenis</string>
 
     <string name="permission_storage_access">Papildomos teisės reikalingos įkelti šiuos atsisiųstus failus.</string>
-    <string name="local_file_not_found_toast">Failas nerastas vietinėje failų sistemoje </string>
+    <string name="local_file_not_found_message">Failas nerastas vietinėje failų sistemoje </string>
     <string name="maintenance_mode">Serveris yra techninės priežiūros veiksenoje</string>
 
     <string name="uploads_view_upload_status_waiting_for_charging">Laukiama mokėjimo </string>

+ 1 - 1
src/main/res/values-nb-rNO/strings.xml

@@ -517,7 +517,7 @@
     <string name="manage_space_clear_data">Nullstill data</string>
 
     <string name="permission_storage_access">Flere tillatelser trengs for å laste opp og ned filer.</string>
-    <string name="local_file_not_found_toast">Filen ble ikke funnet i lokalt filsystem</string>
+    <string name="local_file_not_found_message">Filen ble ikke funnet i lokalt filsystem</string>
     <string name="confirmation_remove_files_alert">Vil du virkelig fjerne de valgte elementene?</string>
     <string name="confirmation_remove_folders_alert">Vil du virkelig fjerne de valgte elementene og dets innhold?</string>
     <string name="maintenance_mode">Tjener i vedlikeholdsmodus</string>

+ 1 - 1
src/main/res/values-nl/strings.xml

@@ -518,7 +518,7 @@
     <string name="manage_space_clear_data">Gegevens verwijderen</string>
 
     <string name="permission_storage_access">Aanvullende permessies vereist voor het uploaden en downloaden van bestanden.</string>
-    <string name="local_file_not_found_toast">Het bestand is niet te vinden binnen het lokale bestandssysteem.</string>
+    <string name="local_file_not_found_message">Het bestand is niet te vinden binnen het lokale bestandssysteem.</string>
     <string name="confirmation_remove_files_alert">Wil je de geselecteerde objecten echt verwijderen?</string>
     <string name="confirmation_remove_folders_alert">Wil je de geselecteerde objecten en hun inhoud echt verwijderen?</string>
     <string name="maintenance_mode">Server in onderhoudsmodus</string>

+ 1 - 1
src/main/res/values-pl/strings.xml

@@ -478,7 +478,7 @@
     <string name="manage_space_clear_data">Wyczyść dane</string>
 
     <string name="permission_storage_access">Wymagane są dodatkowe uprawnienia do pobierania i wysyłania plików.</string>
-    <string name="local_file_not_found_toast">Plik nie został znaleziony w lokalnym systemie plików</string>
+    <string name="local_file_not_found_message">Plik nie został znaleziony w lokalnym systemie plików</string>
     <string name="confirmation_remove_files_alert">Czy naprawdę chcesz usunąć zaznaczone elementy?</string>
     <string name="confirmation_remove_folders_alert">Czy naprawdę chcesz usunąć zaznaczone elementy oraz ich zawartość?</string>
     <string name="maintenance_mode">Serwer w trybie konserwacji</string>

+ 1 - 1
src/main/res/values-pt-rBR/strings.xml

@@ -518,7 +518,7 @@
     <string name="manage_space_clear_data">Limpar dados</string>
 
     <string name="permission_storage_access">Permissões adicionais são necessárias para enviar ou baixar arquivos.</string>
-    <string name="local_file_not_found_toast">Arquivo não encontrado no sistema de arquivos local</string>
+    <string name="local_file_not_found_message">Arquivo não encontrado no sistema de arquivos local</string>
     <string name="confirmation_remove_files_alert">Quer realmente excluir os itens selecionados?</string>
     <string name="confirmation_remove_folders_alert">Quer realmente excluir os itens selecionados e seus conteúdos?</string>
     <string name="maintenance_mode">O servidor está em modo de manutenção</string>

+ 1 - 1
src/main/res/values-ru/strings.xml

@@ -526,7 +526,7 @@
     <string name="manage_space_clear_data">Очистить данные</string>
 
     <string name="permission_storage_access">Для загрузки и скачивания файлов требуются дополнительные права доступа.</string>
-    <string name="local_file_not_found_toast">Файл не был найден в локальной файловой системе</string>
+    <string name="local_file_not_found_message">Файл не был найден в локальной файловой системе</string>
     <string name="confirmation_remove_files_alert">Действительно удалить выбранные объекты?</string>
     <string name="confirmation_remove_folders_alert">Действительно удалить выбранные объекты и их содержимое?</string>
     <string name="maintenance_mode">Сервер находится в режиме обслуживания</string>

+ 1 - 1
src/main/res/values-sl/strings.xml

@@ -407,7 +407,7 @@
     <string name="manage_space_clear_data">Počisti podatke</string>
 
     <string name="permission_storage_access">Za prejemanje oziroma pošiljanje datotek v oblak so zahtevana dodatna dovoljenja.</string>
-    <string name="local_file_not_found_toast">Datoteke ni mogoče najti na krajevnem sistemu</string>
+    <string name="local_file_not_found_message">Datoteke ni mogoče najti na krajevnem sistemu</string>
     <string name="maintenance_mode">Strežnik je v načinu vzdrževanja</string>
 
     <string name="uploads_view_upload_status_waiting_for_charging">Čakam polnjenje</string>

+ 1 - 1
src/main/res/values-sq/strings.xml

@@ -517,7 +517,7 @@
     <string name="manage_space_clear_data">Pastroji të dhënat</string>
 
     <string name="permission_storage_access">Që të ngarkoni dhe shkarkoni skedar kërkohen leje shtesë.</string>
-    <string name="local_file_not_found_toast">Skedari s’u gjet te sistemi vendor i skedarëve</string>
+    <string name="local_file_not_found_message">Skedari s’u gjet te sistemi vendor i skedarëve</string>
     <string name="confirmation_remove_files_alert">Doni vërtetë të hiqni artikujt e zgjedhur?</string>
     <string name="confirmation_remove_folders_alert">Doni vërtet të hiqni artikujt e zgjedhur dhe përmbajtjen e tyre?</string>
     <string name="maintenance_mode">Shërbyesi në gjëndje mirëmbajtjeje</string>

+ 1 - 1
src/main/res/values-sr/strings.xml

@@ -522,7 +522,7 @@
     <string name="manage_space_clear_data">Очисти податке</string>
 
     <string name="permission_storage_access">Додатне дозволе потребне да се отпремају и скидају фајлови.</string>
-    <string name="local_file_not_found_toast">Фајл није нађен у локалном систему фајлова</string>
+    <string name="local_file_not_found_message">Фајл није нађен у локалном систему фајлова</string>
     <string name="confirmation_remove_files_alert">Да ли стварно желите да обришете означене ставке?</string>
     <string name="confirmation_remove_folders_alert">Да ли стварно желите да обришете означене ставке и сав њихов садржај?</string>
     <string name="maintenance_mode">Сервер у режиму одржавања</string>

+ 1 - 1
src/main/res/values-sv/strings.xml

@@ -457,7 +457,7 @@
     <string name="manage_space_clear_data">Rensa data</string>
 
     <string name="permission_storage_access">Ytterligare rättigheter krävs för att ladda upp eller ned filer</string>
-    <string name="local_file_not_found_toast">Fil kunde inte hittas i lokalt filsystem</string>
+    <string name="local_file_not_found_message">Fil kunde inte hittas i lokalt filsystem</string>
     <string name="confirmation_remove_files_alert">Vill du verkligen radera de valda objekten?</string>
     <string name="confirmation_remove_folders_alert">Vill du verkligen radera de valda objekten och dess innehåll?</string>
     <string name="maintenance_mode">Server i underhållsläge</string>

+ 1 - 1
src/main/res/values-tr/strings.xml

@@ -518,7 +518,7 @@
     <string name="manage_space_clear_data">Verileri temizle</string>
 
     <string name="permission_storage_access">Dosya yüklemek ve indirmek için ek izinler gerekli.</string>
-    <string name="local_file_not_found_toast">Dosya yerel dosya sisteminde bulunamadı</string>
+    <string name="local_file_not_found_message">Dosya yerel dosya sisteminde bulunamadı</string>
     <string name="confirmation_remove_files_alert">Seçilmiş ögeleri gerçekten silmek istiyor musunuz?</string>
     <string name="confirmation_remove_folders_alert">Seçilmiş ögeleri ve içindekileri gerçekten silmek istiyor musunuz?</string>
     <string name="maintenance_mode">Sunucu bakım kipinde</string>

+ 1 - 1
src/main/res/values-zh-rCN/strings.xml

@@ -513,7 +513,7 @@
     <string name="manage_space_clear_data">清除数据</string>
 
     <string name="permission_storage_access">上传和下载文件所需的其他权限。</string>
-    <string name="local_file_not_found_toast">在本地文件系统中找不到文件</string>
+    <string name="local_file_not_found_message">在本地文件系统中找不到文件</string>
     <string name="confirmation_remove_files_alert">确定删除选中项?</string>
     <string name="confirmation_remove_folders_alert">确定删除选中项及其内容?</string>
     <string name="maintenance_mode">服务器处于维护模式</string>

+ 1 - 1
src/main/res/values-zh-rTW/strings.xml

@@ -477,7 +477,7 @@
     <string name="manage_space_clear_data">清除資料</string>
 
     <string name="permission_storage_access">上傳與下載檔案需要額外的權限。</string>
-    <string name="local_file_not_found_toast">本地檔案系統中找不到檔案。</string>
+    <string name="local_file_not_found_message">本地檔案系統中找不到檔案。</string>
     <string name="confirmation_remove_files_alert">您真的想要刪除所選的項目嗎?</string>
     <string name="confirmation_remove_folders_alert">您真的想要刪除所選項目和裡面的內容嗎?</string>
     <string name="maintenance_mode">伺服器正處於維護模式</string>

+ 0 - 2
src/main/res/values/dims.xml

@@ -72,8 +72,6 @@
     <dimen name="file_download_fragment_display_text_margin">40dp</dimen>
     <dimen name="drawer_width">240dp</dimen>
     <dimen name="grid_item_text_size">16sp</dimen>
-    <dimen name="list_fragment_column_width">100dp</dimen>
-    <dimen name="list_fragment_spacing">2dp</dimen>
     <dimen name="seek_bar_height">32dp</dimen>
     <dimen name="search_users_groups_layout_width">200dp</dimen>
     <dimen name="search_users_groups_layout_list_view_margin">20dp</dimen>

+ 1 - 1
src/main/res/values/setup.xml

@@ -53,7 +53,7 @@
     <!-- Colors -->
     <color name="primary">#0082c9</color>
     <color name="primary_dark">#006AA3</color>
-    <color name="color_accent">#007CC2</color>
+    <color name="color_accent">#007cc2</color>
     <color name="login_text_color">@color/white</color>
     <color name="login_text_hint_color">#7fC0E3</color>
     <color name="background_color">#FFFFFF</color>

+ 2 - 1
src/main/res/values/strings.xml

@@ -519,7 +519,7 @@
     <string name="manage_space_clear_data">Clear data</string>
 
     <string name="permission_storage_access">Additional permissions required to upload and download files.</string>
-    <string name="local_file_not_found_toast">File not found in local file system</string>
+    <string name="local_file_not_found_message">File not found in local file system</string>
     <string name="confirmation_remove_files_alert">Do you really want to delete the selected items?</string>
     <string name="confirmation_remove_folders_alert">Do you really want to delete the selected items and their contents?</string>
     <string name="maintenance_mode">Server in maintenance mode</string>
@@ -771,6 +771,7 @@
     <string name="end_to_end_encryption_storing_keys">Storing keys</string>
     <string name="copy_move_to_encrypted_folder_not_supported">Copy/move into encrypted folder currently not supported.</string>
     <string name="untrusted_domain">Access through untrusted domain. Please see documentation for further info.</string>
+    <string name="upload_list_loading">Loading uploads…</string>
     <string name="notification_channel_push_name">Push notifications</string>
     <string name="notification_channel_push_description">Show push notifications sent by server, e.g. when mentioned in comments, you receive a new remote share or an announcement was posted by an admin.</string>
     <string name="sendbutton_description">Send button icon</string>