Эх сурвалжийг харах

Merge pull request #9783 from nextcloud/mediaEnhancement

Enhance media tab
Tobias Kaminsky 3 жил өмнө
parent
commit
1e33020f80

+ 1 - 1
scripts/analysis/lint-results.txt

@@ -1,2 +1,2 @@
 DO NOT TOUCH; GENERATED BY DRONE
-      <span class="mdl-layout-title">Lint Report: 103 warnings</span>
+      <span class="mdl-layout-title">Lint Report: 102 warnings</span>

+ 5 - 5
src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.java

@@ -64,7 +64,7 @@ public class ConflictsResolveActivityIT extends AbstractIT {
         newFile.setModificationTimestamp(1522019340);
         newFile.setStoragePath(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt");
 
-        OCFile existingFile = new OCFile("/newFile.txt");
+        OCFile existingFile = new OCFile("/newFile.txt", "4546546");
         existingFile.setFileLength(1024000);
         existingFile.setModificationTimestamp(1582019340);
 
@@ -157,7 +157,7 @@ public class ConflictsResolveActivityIT extends AbstractIT {
                                           "/newFile.txt",
                                           user.getAccountName());
 
-        OCFile existingFile = new OCFile("/newFile.txt");
+        OCFile existingFile = new OCFile("/newFile.txt", "123221");
         existingFile.setFileLength(1024000);
         existingFile.setModificationTimestamp(1582019340);
 
@@ -198,7 +198,7 @@ public class ConflictsResolveActivityIT extends AbstractIT {
                                           "/newFile.txt",
                                           user.getAccountName());
 
-        OCFile existingFile = new OCFile("/newFile.txt");
+        OCFile existingFile = new OCFile("/newFile.txt", "121312");
         existingFile.setFileLength(1024000);
         existingFile.setModificationTimestamp(1582019340);
 
@@ -243,7 +243,7 @@ public class ConflictsResolveActivityIT extends AbstractIT {
                                           "/newFile.txt",
                                           user.getAccountName());
 
-        OCFile existingFile = new OCFile("/newFile.txt");
+        OCFile existingFile = new OCFile("/newFile.txt", "12354455");
         existingFile.setFileLength(1024000);
         existingFile.setModificationTimestamp(1582019340);
         existingFile.setRemoteId("123abc");
@@ -289,7 +289,7 @@ public class ConflictsResolveActivityIT extends AbstractIT {
                                           "/newFile.txt",
                                           user.getAccountName());
 
-        OCFile existingFile = new OCFile("/newFile.txt");
+        OCFile existingFile = new OCFile("/newFile.txt", "564564");
         existingFile.setFileLength(1024000);
         existingFile.setModificationTimestamp(1582019340);
 

+ 80 - 0
src/androidTest/java/com/owncloud/android/ui/adapter/OCFileListAdapterIT.kt

@@ -0,0 +1,80 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2022 Tobias Kaminsky
+ * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.adapter
+
+import com.owncloud.android.AbstractIT
+import com.owncloud.android.datamodel.OCFile
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@Suppress("MagicNumber")
+class OCFileListAdapterIT : AbstractIT() {
+    @Test
+    fun testParseMedia() {
+        // empty start
+        storageManager.deleteAllFiles()
+
+        val startDate: Long = 0
+        val endDate: Long = 3642752043
+        assertEquals(0, storageManager.getGalleryItems(startDate, endDate).size)
+
+        // create dummy files
+        OCFile("/test.txt", "01").apply {
+            mimeType = "text/plain"
+        }.let {
+            storageManager.saveFile(it)
+        }
+
+        OCFile("/image.png", "02").apply {
+            mimeType = "image/png"
+            modificationTimestamp = 1000000
+        }.let {
+            storageManager.saveFile(it)
+        }
+
+        OCFile("/image2.png", "03").apply {
+            mimeType = "image/png"
+            modificationTimestamp = 1000050
+        }.let {
+            storageManager.saveFile(it)
+        }
+
+        OCFile("/video.mpg", "04").apply {
+            mimeType = "video/mpg"
+            modificationTimestamp = 1000045
+        }.let {
+            storageManager.saveFile(it)
+        }
+
+        OCFile("/video2.avi", "05").apply {
+            mimeType = "video/avi"
+            modificationTimestamp = endDate + 10
+        }.let {
+            storageManager.saveFile(it)
+        }
+
+        // list of remoteFiles
+        assertEquals(5, storageManager.allFiles.size)
+        assertEquals(3, storageManager.getGalleryItems(startDate, endDate).size)
+        assertEquals(4, storageManager.allGalleryItems.size)
+    }
+}

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

@@ -2274,6 +2274,72 @@ public class FileDataStorageManager {
         }
     }
 
+    public List<OCFile> getAllGalleryItems() {
+        return getGalleryItems(0, Long.MAX_VALUE);
+    }
+
+    public List<OCFile> getGalleryItems(long startDate, long endDate) {
+        List<OCFile> files = new ArrayList<>();
+
+        Uri requestURI = ProviderTableMeta.CONTENT_URI;
+        Cursor cursor;
+
+        if (getContentProviderClient() != null) {
+            try {
+                cursor = getContentProviderClient().query(
+                    requestURI,
+                    null,
+                    ProviderTableMeta.FILE_ACCOUNT_OWNER + AND +
+                        ProviderTableMeta.FILE_MODIFIED + ">=? AND " +
+                        ProviderTableMeta.FILE_MODIFIED + "<? AND (" +
+                        ProviderTableMeta.FILE_CONTENT_TYPE + " LIKE ? OR " +
+                        ProviderTableMeta.FILE_CONTENT_TYPE + " LIKE ? )",
+                    new String[]{
+                        user.getAccountName(),
+                        String.valueOf(startDate),
+                        String.valueOf(endDate),
+                        "image/%",
+                        "video/%"
+                    },
+                    null
+                                                         );
+            } catch (RemoteException e) {
+                Log_OC.e(TAG, e.getMessage(), e);
+                return files;
+            }
+        } else {
+            cursor = getContentResolver().query(
+                requestURI,
+                null,
+                ProviderTableMeta.FILE_ACCOUNT_OWNER + AND +
+                    ProviderTableMeta.FILE_MODIFIED + ">=? AND " +
+                    ProviderTableMeta.FILE_MODIFIED + "<? AND (" +
+                    ProviderTableMeta.FILE_CONTENT_TYPE + " LIKE ? OR " +
+                    ProviderTableMeta.FILE_CONTENT_TYPE + " LIKE ? )",
+                new String[]{
+                    user.getAccountName(),
+                    String.valueOf(startDate),
+                    String.valueOf(endDate),
+                    "image/%",
+                    "video/%"
+                },
+                null
+                                               );
+        }
+
+        if (cursor != null) {
+            if (cursor.moveToFirst()) {
+                do {
+                    OCFile child = createFileInstance(cursor);
+                    files.add(child);
+                } while (cursor.moveToNext());
+            }
+            cursor.close();
+        }
+
+        return files;
+    }
+
     public List<OCFile> getVirtualFolderContent(VirtualFolderType type, boolean onlyImages) {
         List<OCFile> ocFiles = new ArrayList<>();
         Uri req_uri = ProviderTableMeta.CONTENT_URI_VIRTUAL;

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

@@ -605,6 +605,9 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
         return this.creationTimestamp;
     }
 
+    /**
+     * @return unix timestamp in milliseconds
+     */
     public long getModificationTimestamp() {
         return this.modificationTimestamp;
     }

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

@@ -916,7 +916,7 @@ public class FileContentProvider extends ContentProvider {
                        + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "          // id
                        + ProviderTableMeta.VIRTUAL_TYPE + " TEXT, "                // type
                        + ProviderTableMeta.VIRTUAL_OCFILE_ID + " INTEGER )"        // file id
-        );
+                  );
     }
 
     private void createFileSystemTable(SQLiteDatabase db) {

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

@@ -137,6 +137,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
     private boolean onlyOnDevice;
     private boolean showShareAvatar = false;
     private OCFile highlightedItem;
+    private boolean showMetadata = true;
 
     public OCFileListAdapter(
         Activity activity,
@@ -300,6 +301,10 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
         }
     }
 
+    public boolean isEmpty() {
+        return mFiles.size() == 0;
+    }
+
     @NonNull
     @Override
     public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -469,35 +474,37 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
 
             gridViewHolder.getLocalFileIndicator().setVisibility(View.INVISIBLE);   // default first
 
-            OperationsService.OperationsServiceBinder operationsServiceBinder = transferServiceGetter.getOperationsServiceBinder();
-            FileDownloader.FileDownloaderBinder fileDownloaderBinder = transferServiceGetter.getFileDownloaderBinder();
-            FileUploader.FileUploaderBinder fileUploaderBinder = transferServiceGetter.getFileUploaderBinder();
-            if (operationsServiceBinder != null && operationsServiceBinder.isSynchronizing(user, file)) {
-                //synchronizing
-                gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing);
-                gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
-
-            } else if (fileDownloaderBinder != null && fileDownloaderBinder.isDownloading(user, file)) {
-                // downloading
-                gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing);
-                gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
-
-            } else if (fileUploaderBinder != null && fileUploaderBinder.isUploading(user, file)) {
-                //uploading
-                gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing);
-                gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
-
-            } else if (file.getEtagInConflict() != null) {
-                // conflict
-                gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing_error);
-                gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
-
-            } else if (file.isDown()) {
-                gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synced);
-                gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
-            }
+            if (showMetadata) {
+                OperationsService.OperationsServiceBinder operationsServiceBinder = transferServiceGetter.getOperationsServiceBinder();
+                FileDownloader.FileDownloaderBinder fileDownloaderBinder = transferServiceGetter.getFileDownloaderBinder();
+                FileUploader.FileUploaderBinder fileUploaderBinder = transferServiceGetter.getFileUploaderBinder();
+                if (operationsServiceBinder != null && operationsServiceBinder.isSynchronizing(user, file)) {
+                    //synchronizing
+                    gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing);
+                    gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
+
+                } else if (fileDownloaderBinder != null && fileDownloaderBinder.isDownloading(user, file)) {
+                    // downloading
+                    gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing);
+                    gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
+
+                } else if (fileUploaderBinder != null && fileUploaderBinder.isUploading(user, file)) {
+                    //uploading
+                    gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing);
+                    gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
+
+                } else if (file.getEtagInConflict() != null) {
+                    // conflict
+                    gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing_error);
+                    gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
+
+                } else if (file.isDown()) {
+                    gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synced);
+                    gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
+                }
 
-            gridViewHolder.getFavorite().setVisibility(file.isFavorite() ? View.VISIBLE : View.GONE);
+                gridViewHolder.getFavorite().setVisibility(file.isFavorite() ? View.VISIBLE : View.GONE);
+            }
 
             if (multiSelect) {
                 gridViewHolder.getCheckbox().setVisibility(View.VISIBLE);
@@ -753,7 +760,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
             return false;
         }
 
-        if (MainApp.isOnlyOnDevice()) {
+        if (MainApp.isOnlyOnDevice() || ocFileListFragmentInterface.isSearchFragment()) {
             return false;
         }
 
@@ -857,8 +864,6 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
         notifyDataSetChanged();
     }
 
-
-
     public void setData(List<Object> objects,
                         ExtendedListFragment.SearchType searchType,
                         FileDataStorageManager storageManager,
@@ -891,7 +896,9 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
                     break;
             }
 
-            mStorageManager.deleteVirtuals(type);
+            if (type != VirtualFolderType.GALLERY) {
+                mStorageManager.deleteVirtuals(type);
+            }
         }
 
         // early exit
@@ -899,7 +906,9 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
             if (searchType == ExtendedListFragment.SearchType.SHARED_FILTER) {
                 parseShares(objects);
             } else {
-                parseVirtuals(objects, searchType);
+                if (searchType != ExtendedListFragment.SearchType.GALLERY_SEARCH) {
+                    parseVirtuals(objects, searchType);
+                }
             }
         }
 
@@ -1031,6 +1040,19 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
         mStorageManager.saveVirtuals(contentValues);
     }
 
+    public void showAllGalleryItems(FileDataStorageManager storageManager) {
+        if (mStorageManager == null) {
+            mStorageManager = storageManager;
+        }
+        mFiles = mStorageManager.getAllGalleryItems();
+        FileStorageUtils.sortOcFolderDescDateModifiedWithoutFavoritesFirst(mFiles);
+
+        mFilesAll.clear();
+        mFilesAll.addAll(mFiles);
+
+        new Handler(Looper.getMainLooper()).post(this::notifyDataSetChanged);
+    }
+
     public void showVirtuals(VirtualFolderType type, boolean onlyImages, FileDataStorageManager storageManager) {
         mFiles = storageManager.getVirtualFolderContent(type, onlyImages);
 
@@ -1187,6 +1209,10 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
         gridView = bool;
     }
 
+    public void setShowMetadata(boolean bool) {
+        showMetadata = bool;
+    }
+
     @VisibleForTesting
     public void setShowShareAvatar(boolean bool) {
         showShareAvatar = bool;

+ 132 - 65
src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.java

@@ -25,110 +25,177 @@ import android.os.AsyncTask;
 
 import com.nextcloud.client.account.User;
 import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.files.SearchRemoteOperation;
-import com.owncloud.android.ui.adapter.OCFileListAdapter;
+import com.owncloud.android.lib.resources.files.model.RemoteFile;
+import com.owncloud.android.lib.resources.status.OCCapability;
+import com.owncloud.android.operations.RefreshFolderOperation;
 import com.owncloud.android.ui.fragment.ExtendedListFragment;
 import com.owncloud.android.ui.fragment.GalleryFragment;
+import com.owncloud.android.utils.FileStorageUtils;
 
 import java.lang.ref.WeakReference;
-
-public class GallerySearchTask extends AsyncTask<Void, Void, RemoteOperationResult> {
-
-    private int columnCount;
-    private User user;
-    private WeakReference<GalleryFragment> photoFragmentWeakReference;
-    private SearchRemoteOperation searchRemoteOperation;
-    private FileDataStorageManager storageManager;
-    private int limit;
-
-    public GallerySearchTask(int columnsCount,
-                             GalleryFragment photoFragment,
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+public class GallerySearchTask extends AsyncTask<Void, Void, GallerySearchTask.Result> {
+
+    private final User user;
+    private final WeakReference<GalleryFragment> photoFragmentWeakReference;
+    private final FileDataStorageManager storageManager;
+    private final int limit;
+    private final long startDate;
+    private final long endDate;
+
+    public GallerySearchTask(GalleryFragment photoFragment,
                              User user,
-                             SearchRemoteOperation searchRemoteOperation,
-                             FileDataStorageManager storageManager) {
-        this.columnCount = columnsCount;
+                             FileDataStorageManager storageManager,
+                             long startDate,
+                             long endDate,
+                             int limit) {
         this.user = user;
         this.photoFragmentWeakReference = new WeakReference<>(photoFragment);
-        this.searchRemoteOperation = searchRemoteOperation;
         this.storageManager = storageManager;
+        this.startDate = startDate;
+        this.endDate = endDate;
+        this.limit = limit;
     }
 
     @Override
-    protected void onPreExecute() {
-        super.onPreExecute();
-
-        if (photoFragmentWeakReference.get() == null) {
-            return;
-        }
-        GalleryFragment photoFragment = photoFragmentWeakReference.get();
-        photoFragment.setPhotoSearchQueryRunning(true);
-    }
-
-    @Override
-    protected RemoteOperationResult doInBackground(Void... voids) {
+    protected GallerySearchTask.Result doInBackground(Void... voids) {
         if (photoFragmentWeakReference.get() == null) {
-            return new RemoteOperationResult(new Exception("Photo fragment is null"));
+            return new Result(false, false, -1);
         }
         GalleryFragment photoFragment = photoFragmentWeakReference.get();
-        OCFileListAdapter adapter = photoFragment.getAdapter();
 
         if (isCancelled()) {
-            return new RemoteOperationResult(new Exception("Cancelled"));
+            return new Result(false, false, -1);
         } else {
-            limit = 15 * columnCount;
+            OCCapability ocCapability = storageManager.getCapability(user.getAccountName());
+
+            SearchRemoteOperation searchRemoteOperation = new SearchRemoteOperation("",
+                                                                                    SearchRemoteOperation.SearchType.GALLERY_SEARCH,
+                                                                                    false,
+                                                                                    ocCapability);
 
-            long timestamp = -1;
-            if (adapter.getLastTimestamp() > 0) {
-                timestamp = adapter.getLastTimestamp();
-            }
 
             searchRemoteOperation.setLimit(limit);
-            searchRemoteOperation.setTimestamp(timestamp);
+            searchRemoteOperation.setStartDate(startDate);
+            searchRemoteOperation.setEndDate(endDate);
 
             if (photoFragment.getContext() != null) {
-                return searchRemoteOperation.execute(user.toPlatformAccount(), photoFragment.getContext());
+                Log_OC.d(this,
+                         "Start gallery search with " + new Date(startDate * 1000L) +
+                             " - " + new Date(endDate * 1000L) +
+                             " with limit: " + limit);
+
+                RemoteOperationResult result = searchRemoteOperation.execute(user.toPlatformAccount(),
+                                                                             photoFragment.getContext());
+
+                boolean emptySearch = parseMedia(startDate, endDate, result.getData());
+                long lastTimeStamp = findLastTimestamp(result.getData());
+
+                photoFragment.getAdapter().showAllGalleryItems(storageManager);
+
+                return new Result(result.isSuccess(), emptySearch, lastTimeStamp);
             } else {
-                return new RemoteOperationResult(new IllegalStateException("No context available"));
+                return new Result(false, false, -1);
             }
         }
     }
 
     @Override
-    protected void onPostExecute(RemoteOperationResult result) {
+    protected void onPostExecute(GallerySearchTask.Result result) {
         if (photoFragmentWeakReference.get() != null) {
             GalleryFragment photoFragment = photoFragmentWeakReference.get();
 
-            if (result.isSuccess() && result.getData() != null && !isCancelled()) {
-                if (result.getData() == null || result.getData().size() == 0) {
-                    photoFragment.setSearchDidNotFindNewPhotos(true);
-                    photoFragment.setEmptyListMessage(ExtendedListFragment.SearchType.GALLERY_SEARCH);
-                } else {
-                    OCFileListAdapter adapter = photoFragment.getAdapter();
-
-                    if (result.getData().size() < limit) {
-                        // stop loading spinner
-                        photoFragment.setSearchDidNotFindNewPhotos(true);
-                    }
-
-                    adapter.setData(result.getData(),
-                                    ExtendedListFragment.SearchType.GALLERY_SEARCH,
-                                    storageManager,
-                                    null,
-                                    false);
-                    adapter.notifyDataSetChanged();
-                    Log_OC.d(this, "Search: count: " + result.getData().size() + " total: " + adapter.getFiles().size());
-                }
-            }
-
             photoFragment.setLoading(false);
+            photoFragment.searchCompleted(result.emptySearch, result.lastTimestamp);
 
-            if (!result.isSuccess() && !isCancelled()) {
+            if (!result.success) {
                 photoFragment.setEmptyListMessage(ExtendedListFragment.SearchType.GALLERY_SEARCH);
             }
+        }
+    }
+
+    private long findLastTimestamp(ArrayList<RemoteFile> remoteFiles) {
+        int lastPosition = remoteFiles.size() - 1;
+
+        if (lastPosition < 0) {
+            return -1;
+        }
+
+        RemoteFile lastFile = remoteFiles.get(lastPosition);
+        return lastFile.getModifiedTimestamp() / 1000;
+    }
+
+    private boolean parseMedia(long startDate, long endDate, List<Object> remoteFiles) {
+        // retrieve all between startDate and endDate
+        Map<String, OCFile> localFilesMap = RefreshFolderOperation.prefillLocalFilesMap(null,
+                                                                                        storageManager.getGalleryItems(startDate * 1000L,
+                                                                                                                       endDate * 1000L));
+        List<OCFile> filesToAdd = new ArrayList<>();
+        List<OCFile> filesToUpdate = new ArrayList<>();
+
+        OCFile localFile;
+        for (Object file : remoteFiles) {
+            OCFile ocFile = FileStorageUtils.fillOCFile((RemoteFile) file);
+
+            localFile = localFilesMap.remove(ocFile.getRemotePath());
+
+            if (localFile == null) {
+                // add new file
+                filesToAdd.add(ocFile);
+            } else if (!localFile.getEtag().equals(ocFile.getEtag())) {
+                // update file
+                ocFile.setLastSyncDateForData(System.currentTimeMillis());
+                filesToUpdate.add(ocFile);
+            }
+        }
+
+        // add new files
+        for (OCFile file : filesToAdd) {
+            storageManager.saveFile(file);
+        }
+
+        // update existing files
+        for (OCFile file : filesToUpdate) {
+            storageManager.saveFile(file);
+        }
+
+        // existing files to remove
+        for (OCFile file : localFilesMap.values()) {
+            storageManager.removeFile(file, true, true);
+        }
+
+        Log_OC.d(this, "Gallery search result:" +
+            " new: " + filesToAdd.size() +
+            " updated: " + filesToUpdate.size() +
+            " deleted: " + localFilesMap.size());
+
+        return didNotFindNewResults(filesToAdd, filesToUpdate, localFilesMap.values());
+    }
+
+    private boolean didNotFindNewResults(List<OCFile> filesToAdd,
+                                         List<OCFile> filesToUpdate,
+                                         Collection<OCFile> filesToRemove) {
+        return filesToAdd.isEmpty() && filesToUpdate.isEmpty() && filesToRemove.isEmpty();
+    }
+
+    public static class Result {
+        public boolean success;
+        public boolean emptySearch;
+        public long lastTimestamp;
 
-            photoFragment.setPhotoSearchQueryRunning(false);
+        public Result(boolean success, boolean emptySearch, long lastTimestamp) {
+            this.success = success;
+            this.emptySearch = emptySearch;
+            this.lastTimestamp = lastTimestamp;
         }
     }
 }

+ 77 - 62
src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java

@@ -27,52 +27,31 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
-import com.owncloud.android.datamodel.VirtualFolderType;
+import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.lib.resources.files.SearchRemoteOperation;
-import com.owncloud.android.lib.resources.status.OCCapability;
 import com.owncloud.android.ui.asynctasks.GallerySearchTask;
 import com.owncloud.android.ui.events.ChangeMenuEvent;
-import com.owncloud.android.ui.events.SearchEvent;
-
-import java.util.ArrayList;
 
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
 /**
- * A Fragment that lists all files and folders in a given path. TODO refactor to get rid of direct dependency on
- * FileDisplayActivity
+ * A Fragment that lists all files and folders in a given path
  */
 public class GalleryFragment extends OCFileListFragment {
     private static final int MAX_ITEMS_PER_ROW = 10;
     private boolean photoSearchQueryRunning = false;
-    private boolean photoSearchNoNew = false;
-    private SearchRemoteOperation searchRemoteOperation;
-    private AsyncTask photoSearchTask;
-    private SearchEvent searchEvent;
-    private boolean refresh;
-
-    private void createOperation() {
-        if(searchEvent == null) {
-            searchEvent = new SearchEvent("", SearchRemoteOperation.SearchType.GALLERY_SEARCH);
-        }
-        if(searchRemoteOperation == null) {
-            OCCapability ocCapability = mContainerActivity.getStorageManager()
-                .getCapability(accountManager.getUser().getAccountName());
-
-            searchRemoteOperation = new SearchRemoteOperation(searchEvent.getSearchQuery(),
-                                                              searchEvent.getSearchType(),
-                                                              false,
-                                                              ocCapability);
-        }
-    }
+    private AsyncTask<Void, Void, GallerySearchTask.Result> photoSearchTask;
+    private long startDate;
+    private long endDate;
+    private long daySpan = 30;
+    private int limit = 300;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        refresh = true;
+        searchFragment = true;
     }
 
     @Override
@@ -89,7 +68,6 @@ public class GalleryFragment extends OCFileListFragment {
      */
     @Override
     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        createOperation();
         View v = super.onCreateView(inflater, container, savedInstanceState);
 
         getRecyclerView().addOnScrollListener(new RecyclerView.OnScrollListener() {
@@ -106,6 +84,7 @@ public class GalleryFragment extends OCFileListFragment {
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
+        mAdapter.setShowMetadata(false);
 
         currentSearchType = SearchType.GALLERY_SEARCH;
 
@@ -121,7 +100,6 @@ public class GalleryFragment extends OCFileListFragment {
     public void onRefresh() {
         super.onRefresh();
 
-        refresh = true;
         handleSearchEvent();
     }
 
@@ -138,25 +116,10 @@ public class GalleryFragment extends OCFileListFragment {
 
     private void handleSearchEvent() {
         prepareCurrentSearch(searchEvent);
-        searchFragment = true;
         setEmptyListLoadingMessage();
 
-        if (refresh || preferences.getPhotoSearchTimestamp() == 0 ||
-            System.currentTimeMillis() - preferences.getPhotoSearchTimestamp() >= 30 * 1000) {
-            mAdapter.setData(
-                new ArrayList<>(),
-                SearchType.GALLERY_SEARCH,
-                mContainerActivity.getStorageManager(),
-                mFile,
-                true);
-            photoSearchNoNew = false;
-            refresh = false;
-        } else {
-            mAdapter.showVirtuals(VirtualFolderType.GALLERY, true, mContainerActivity.getStorageManager());
-            preferences.setPhotoSearchTimestamp(System.currentTimeMillis());
-
-            return;
-        }
+        // always show first stored items
+        mAdapter.showAllGalleryItems(mContainerActivity.getStorageManager());
 
         setFabVisible(false);
 
@@ -164,27 +127,66 @@ public class GalleryFragment extends OCFileListFragment {
     }
 
     private void searchAndDisplay() {
-        if (!photoSearchQueryRunning && !photoSearchNoNew) {
-            photoSearchTask = new GallerySearchTask(getColumnsCount(),
-                                                    this,
+        // first: always search from now to -30 days
+        if (!photoSearchQueryRunning) {
+            photoSearchQueryRunning = true;
+
+            startDate = (System.currentTimeMillis() / 1000) - 30 * 24 * 60 * 60;
+            endDate = System.currentTimeMillis() / 1000;
+
+            photoSearchTask = new GallerySearchTask(this,
                                                     accountManager.getUser(),
-                                                    searchRemoteOperation,
-                                                    mContainerActivity.getStorageManager())
+                                                    mContainerActivity.getStorageManager(),
+                                                    startDate,
+                                                    endDate,
+                                                    limit)
                 .execute();
         }
     }
 
-    public void setPhotoSearchQueryRunning(boolean bool) {
-        photoSearchQueryRunning = bool;
-    }
+    public void searchCompleted(boolean emptySearch, long lastTimeStamp) {
+        photoSearchQueryRunning = false;
+        mAdapter.notifyDataSetChanged();
+
+        if (mAdapter.isEmpty()) {
+            setEmptyListMessage(ExtendedListFragment.SearchType.GALLERY_SEARCH);
+        }
+
+        if (emptySearch && getAdapter().getItemCount() > 0) {
+            Log_OC.d(this, "End gallery search");
+            return;
+        }
+        if (daySpan == 30) {
+            daySpan = 90;
+        } else if (daySpan == 90) {
+            daySpan = 180;
+        } else if (daySpan == 180) {
+            daySpan = 999;
+        } else if (daySpan == 999 && limit > 0) {
+            limit = -1; // no limit
+        } else {
+            Log_OC.d(this, "End gallery search");
+            return;
+        }
+
+        if (lastTimeStamp > -1) {
+            endDate = lastTimeStamp;
+        }
+
+        startDate = endDate - (daySpan * 24 * 60 * 60);
 
-    public void setSearchDidNotFindNewPhotos(boolean noNewPhotos) {
-        photoSearchNoNew = noNewPhotos;
+        photoSearchTask = new GallerySearchTask(this,
+                                                accountManager.getUser(),
+                                                mContainerActivity.getStorageManager(),
+                                                startDate,
+                                                endDate,
+                                                limit)
+            .execute();
     }
 
     @Override
     public boolean isLoading() {
-        return !photoSearchNoNew;
+        return photoSearchQueryRunning;
     }
 
     private void loadMoreWhenEndReached(@NonNull RecyclerView recyclerView, int dy) {
@@ -195,12 +197,25 @@ public class GalleryFragment extends OCFileListFragment {
             if (dy > 0 && !photoSearchQueryRunning) {
                 int visibleItemCount = gridLayoutManager.getChildCount();
                 int totalItemCount = gridLayoutManager.getItemCount();
-                int firstVisibleItem = gridLayoutManager.findFirstCompletelyVisibleItemPosition();
+                int lastVisibleItem = gridLayoutManager.findLastCompletelyVisibleItemPosition();
 
-                if ((totalItemCount - visibleItemCount) <= (firstVisibleItem + MAX_ITEMS_PER_ROW)
+                if ((totalItemCount - visibleItemCount) <= (lastVisibleItem + MAX_ITEMS_PER_ROW)
                     && (totalItemCount - visibleItemCount) > 0) {
                     // Almost reached the end, continue to load new photos
-                    searchAndDisplay();
+                    OCFile lastFile = mAdapter.getItem(lastVisibleItem - 1);
+
+                    daySpan = 30;
+                    endDate = lastFile.getModificationTimestamp() / 1000;
+                    startDate = endDate - (daySpan * 24 * 60 * 60);
+
+                    photoSearchQueryRunning = true;
+                    photoSearchTask = new GallerySearchTask(this,
+                                                            accountManager.getUser(),
+                                                            mContainerActivity.getStorageManager(),
+                                                            startDate,
+                                                            endDate,
+                                                            limit)
+                        .execute();
                 }
             }
         }

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

@@ -1501,7 +1501,11 @@ public class OCFileListFragment extends ExtendedListFragment implements
         prepareCurrentSearch(event);
         searchFragment = true;
         setEmptyListLoadingMessage();
-        mAdapter.setData(new ArrayList<>(), SearchType.NO_SEARCH, mContainerActivity.getStorageManager(), mFile, true);
+        mAdapter.setData(new ArrayList<>(),
+                         SearchType.NO_SEARCH,
+                         mContainerActivity.getStorageManager(),
+                         mFile,
+                         true);
 
         setFabVisible(false);
 

+ 2 - 0
src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java

@@ -48,4 +48,6 @@ public interface OCFileListFragmentInterface {
     boolean isLoading();
 
     void onHeaderClicked();
+
+    boolean isSearchFragment();
 }

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

@@ -116,10 +116,12 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
 
         this.user = user;
         mStorageManager = storageManager;
-        mImageFiles = mStorageManager.getVirtualFolderContent(type, true);
 
         if (type == VirtualFolderType.GALLERY) {
+            mImageFiles = mStorageManager.getAllGalleryItems();
             mImageFiles = FileStorageUtils.sortOcFolderDescDateModifiedWithoutFavoritesFirst(mImageFiles);
+        } else {
+            mImageFiles = mStorageManager.getVirtualFolderContent(type, true);
         }
 
         mObsoleteFragments = new HashSet<>();

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

@@ -52,6 +52,7 @@
         android:layout_gravity="top|end"
         android:layout_margin="@dimen/standard_quarter_margin"
         android:contentDescription="@string/favorite_icon"
+        android:visibility="gone"
         android:src="@drawable/favorite" />
 
     <ImageView

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

@@ -975,7 +975,6 @@
     <string name="share_settings">Settings</string>
     <string name="common_confirm">Confirm</string>
     <string name="strict_mode">Strict mode: no HTTP connection allowed!</string>
-    <string name="fullscreen">Fullscreen</string>
     <string name="destination_filename">Destination filename</string>
     <string name="suggest">Suggest</string>
     <string name="enter_destination_filename">Enter destination filename</string>