Răsfoiți Sursa

Merge pull request #896 from nextcloud/searchViewWrongFiles

Search view wrong files
Mario Đanić 8 ani în urmă
părinte
comite
cb33a35ee9

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

@@ -1,21 +1,20 @@
 /**
- *   ownCloud Android client application
- *
- *   Copyright (C) 2012  Bartek Przybylski
- *   Copyright (C) 2015 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
+ * ownCloud Android client application
+ * <p>
+ * Copyright (C) 2012  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.datamodel;
@@ -27,6 +26,7 @@ import android.content.ContentProviderResult;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
+import android.content.Context;
 import android.content.Intent;
 import android.content.OperationApplicationException;
 import android.database.Cursor;
@@ -36,7 +36,10 @@ import android.provider.MediaStore;
 
 import com.owncloud.android.MainApp;
 import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
+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.lib.resources.status.CapabilityBooleanType;
@@ -160,7 +163,7 @@ public class FileDataStorageManager {
             // 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++) {
+            for (int i = 0; i < tmp.size(); i++) {
                 current = tmp.get(i);
                 if (MimeTypeUtil.isImage(current)) {
                     ret.add(current);
@@ -247,6 +250,36 @@ public class FileDataStorageManager {
         return overriden;
     }
 
+    public OCFile saveFileWithParent(OCFile file, Context context) {
+        if (file.getParentId() == 0 && !file.getRemotePath().equals("/")) {
+            String remotePath = file.getRemotePath();
+            String parentPath = remotePath.substring(0, remotePath.lastIndexOf(file.getFileName()));
+
+            OCFile parentFile = getFileByPath(parentPath);
+
+            OCFile returnFile;
+            if (parentFile == null) {
+                // remote request
+                ReadRemoteFileOperation operation = new ReadRemoteFileOperation(parentPath);
+                RemoteOperationResult result = operation.execute(getAccount(), context);
+                if (result.isSuccess()) {
+                    OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
+
+                    returnFile = saveFileWithParent(remoteFolder, context);
+                } else {
+                    returnFile = null;
+                    Log_OC.e(TAG, "Error during saving file with parents: " + file.getRemotePath());
+                }
+            } else {
+                returnFile = saveFileWithParent(parentFile, context);
+            }
+
+            file.setParentId(returnFile.getFileId());
+            saveFile(file);
+        }
+
+        return file;
+    }
 
     public void saveNewFile(OCFile newFile) {
         String remoteParentPath = new File(newFile.getRemotePath()).getParent();
@@ -276,7 +309,7 @@ public class FileDataStorageManager {
             OCFile folder, Collection<OCFile> updatedFiles, Collection<OCFile> filesToRemove
     ) {
 
-        Log_OC.d(TAG,  "Saving folder " + folder.getRemotePath() + " with " + updatedFiles.size()
+        Log_OC.d(TAG, "Saving folder " + folder.getRemotePath() + " with " + updatedFiles.size()
                 + " children and " + filesToRemove.size() + " files to remove");
 
         ArrayList<ContentProviderOperation> operations = new ArrayList<>(updatedFiles.size());
@@ -301,8 +334,9 @@ public class FileDataStorageManager {
         }
 
         // prepare operations to remove files in the given folder
-        String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PATH + "=?";
-        String [] whereArgs = null;
+        String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + AND +
+                ProviderTableMeta.FILE_PATH + "=?";
+        String[] whereArgs = null;
         for (OCFile file : filesToRemove) {
             if (file.getParentId() == folder.getFileId()) {
                 whereArgs = new String[]{mAccount.name, file.getRemotePath()};
@@ -502,7 +536,7 @@ public class FileDataStorageManager {
         Uri folder_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, "" + folder.getFileId()); // URI
         // for recursive deletion
         String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PATH + "=?";
-        String [] whereArgs = new String[]{mAccount.name, folder.getRemotePath()};
+        String[] whereArgs = new String[]{mAccount.name, folder.getRemotePath()};
         int deleted = 0;
         if (getContentProviderClient() != null) {
             try {
@@ -796,9 +830,9 @@ public class FileDataStorageManager {
         if (c != null && c.moveToFirst()) {
             do {
                 OCFile child = createFileInstance(c);
-                 if (child.isFolder() || !onlyOnDevice || child.isDown()) {
-                     ret.add(child);
-                 }
+                if (child.isFolder() || !onlyOnDevice || child.isDown()) {
+                    ret.add(child);
+                }
             } while (c.moveToNext());
             c.close();
         }
@@ -871,6 +905,15 @@ public class FileDataStorageManager {
         return c;
     }
 
+    private OCFile createFileInstanceFromVirtual(Cursor c) {
+        OCFile file = null;
+        if (c != null) {
+            long fileId = c.getLong(c.getColumnIndex(ProviderTableMeta.VIRTUAL_OCFILE_ID));
+            file = getFileById(fileId);
+        }
+
+        return file;
+    }
 
     private OCFile createFileInstance(Cursor c) {
         OCFile file = null;
@@ -981,7 +1024,7 @@ public class FileDataStorageManager {
      * Retrieves an stored {@link OCShare} given its id.
      *
      * @param id    Identifier.
-     * @return      Stored {@link OCShare} given its id.
+     * @return Stored {@link OCShare} given its id.
      */
     public OCShare getShareById(long id) {
         OCShare share = null;
@@ -1039,7 +1082,7 @@ public class FileDataStorageManager {
                     .query(ProviderTableMeta.CONTENT_URI_SHARE,
                             null,
                             key + AND
-                                + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
+                                    + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
                             new String[]{value, mAccount.name},
                             null
                     );
@@ -1053,7 +1096,7 @@ public class FileDataStorageManager {
                         null
                 );
             } catch (RemoteException e) {
-                Log_OC.w(TAG, "Could not get details, assuming share does not exist: "+ e.getMessage());
+                Log_OC.w(TAG, "Could not get details, assuming share does not exist: " + e.getMessage());
                 c = null;
             }
         }
@@ -1061,14 +1104,13 @@ public class FileDataStorageManager {
     }
 
 
-
     /**
      * Get first share bound to a file with a known path and given {@link ShareType}.
      *
      * @param path          Path of the file.
      * @param type          Type of the share to get
      * @param shareWith     Target of the share. Ignored in type is {@link ShareType#PUBLIC_LINK}
-     * @return              First {@link OCShare} instance found in DB bound to the file in 'path'
+     * @return First {@link OCShare} instance found in DB bound to the file in 'path'
      */
     public OCShare getFirstShareByPathAndType(String path, ShareType type, String shareWith) {
         Cursor cursor = null;
@@ -1078,12 +1120,12 @@ public class FileDataStorageManager {
 
         String selection = ProviderTableMeta.OCSHARES_PATH + AND
                 + ProviderTableMeta.OCSHARES_SHARE_TYPE + AND
-                + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?" ;
+                + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
         if (!ShareType.PUBLIC_LINK.equals(type)) {
             selection += " AND " + ProviderTableMeta.OCSHARES_SHARE_WITH + "=?";
         }
 
-        String [] selectionArgs;
+        String[] selectionArgs;
         if (ShareType.PUBLIC_LINK.equals(type)) {
             selectionArgs = new String[]{
                     path,
@@ -1176,7 +1218,7 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, false);
         cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
         String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PARENT + "=?";
-        String [] whereArgs = new String[] { mAccount.name , String.valueOf(folder.getFileId()) };
+        String[] whereArgs = new String[]{mAccount.name, String.valueOf(folder.getFileId())};
 
         if (getContentResolver() != null) {
             getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
@@ -1190,13 +1232,13 @@ public class FileDataStorageManager {
         }
     }
 
-    private void resetShareFlagInAFile(String filePath){
+    private void resetShareFlagInAFile(String filePath) {
         ContentValues cv = new ContentValues();
         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, false);
         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, false);
         cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
-        String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PATH+ "=?";
-        String [] whereArgs = new String[] { mAccount.name , filePath };
+        String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PATH + "=?";
+        String[] whereArgs = new String[]{mAccount.name, filePath};
 
         if (getContentResolver() != null) {
             getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
@@ -1383,7 +1425,7 @@ public class FileDataStorageManager {
         Uri share_uri = ProviderTableMeta.CONTENT_URI_SHARE;
         String where = ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + AND +
                 ProviderTableMeta._ID + "=?";
-        String [] whereArgs = new String[]{mAccount.name, Long.toString(share.getId())};
+        String[] whereArgs = new String[]{mAccount.name, Long.toString(share.getId())};
         if (getContentProviderClient() != null) {
             try {
                 getContentProviderClient().delete(share_uri, where, whereArgs);
@@ -1400,16 +1442,16 @@ public class FileDataStorageManager {
 
         // Reset flags & Remove shares for this files
         String filePath = "";
-        for (OCShare share: shares) {
-            if (filePath != share.getPath()){
+        for (OCShare share : shares) {
+            if (filePath != share.getPath()) {
                 filePath = share.getPath();
                 resetShareFlagInAFile(filePath);
                 operations = prepareRemoveSharesInFile(filePath, operations);
             }
         }
 
-       // Add operations to insert shares
-       operations = prepareInsertShares(shares, operations);
+        // Add operations to insert shares
+        operations = prepareInsertShares(shares, operations);
 
         // apply operations in batch
         if (operations.size() > 0) {
@@ -1550,7 +1592,7 @@ public class FileDataStorageManager {
         if (folder != null) {
             String where = ProviderTableMeta.OCSHARES_PATH + AND
                     + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
-            String [] whereArgs = new String[]{ "", mAccount.name };
+            String[] whereArgs = new String[]{"", mAccount.name};
 
             Vector<OCFile> files = getFolderContent(folder, false);
 
@@ -1584,15 +1626,15 @@ public class FileDataStorageManager {
 
     }
 
-    public ArrayList<OCShare> getSharesWithForAFile(String filePath, String accountName){
+    public ArrayList<OCShare> getSharesWithForAFile(String filePath, String accountName) {
         // Condition
         String where = ProviderTableMeta.OCSHARES_PATH + AND
                 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + AND
                 + " (" + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? OR "
-                + ProviderTableMeta.OCSHARES_SHARE_TYPE +  "=? OR "
-                + ProviderTableMeta.OCSHARES_SHARE_TYPE +  "=? OR "
+                + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? OR "
+                + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? OR "
                 + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? ) ";
-        String [] whereArgs = new String[]{ filePath, accountName ,
+        String[] whereArgs = new String[]{filePath, accountName,
                 Integer.toString(ShareType.USER.getValue()),
                 Integer.toString(ShareType.GROUP.getValue()),
                 Integer.toString(ShareType.EMAIL.getValue()),
@@ -1691,7 +1733,7 @@ public class FileDataStorageManager {
                     ProviderTableMeta.CONTENT_URI_FILE,
                     cv,
                     ProviderTableMeta._ID + "=?",
-                    new String[] { String.valueOf(file.getFileId())}
+                    new String[]{String.valueOf(file.getFileId())}
             );
         } else {
             try {
@@ -1828,7 +1870,7 @@ public class FileDataStorageManager {
 
     }
 
-    public OCCapability saveCapabilities(OCCapability capability){
+    public OCCapability saveCapabilities(OCCapability capability) {
 
         // Prepare capabilities data
         ContentValues cv = new ContentValues();
@@ -1915,7 +1957,7 @@ public class FileDataStorageManager {
         return exists;
     }
 
-    private Cursor getCapabilityCursorForAccount(String accountName){
+    private Cursor getCapabilityCursorForAccount(String accountName) {
         Cursor c = null;
         if (getContentResolver() != null) {
             c = getContentResolver()
@@ -1938,7 +1980,8 @@ public class FileDataStorageManager {
         return c;
 
     }
-    public OCCapability getCapability(String accountName){
+
+    public OCCapability getCapability(String accountName) {
         OCCapability capability = null;
         Cursor c = getCapabilityCursorForAccount(accountName);
 
@@ -2008,4 +2051,102 @@ public class FileDataStorageManager {
         return capability;
     }
 
+    public void deleteVirtuals(VirtualFolderType type) {
+        if (getContentResolver() != null) {
+            getContentResolver().delete(ProviderTableMeta.CONTENT_URI_VIRTUAL,
+                    ProviderTableMeta.VIRTUAL_TYPE + "=?", new String[]{String.valueOf(type)});
+        } else {
+            try {
+                getContentProviderClient().delete(ProviderTableMeta.CONTENT_URI_VIRTUAL,
+                        ProviderTableMeta.VIRTUAL_TYPE + "=?", new String[]{String.valueOf(type)});
+            } catch (RemoteException e) {
+                Log_OC.e(TAG, FAILED_TO_INSERT_MSG + e.getMessage(), e);
+            }
+        }
+    }
+
+    public void saveVirtuals(VirtualFolderType type, List<ContentValues> values) {
+
+        if (getContentResolver() != null) {
+            getContentResolver().bulkInsert(ProviderTableMeta.CONTENT_URI_VIRTUAL, values.toArray(new ContentValues[values.size()]));
+        } else {
+            try {
+                getContentProviderClient().bulkInsert(ProviderTableMeta.CONTENT_URI_VIRTUAL, values.toArray(new ContentValues[values.size()]));
+            } catch (RemoteException e) {
+                Log_OC.e(TAG, FAILED_TO_INSERT_MSG + e.getMessage(), e);
+            }
+        }
+    }
+
+    public void saveVirtual(VirtualFolderType type, OCFile file) {
+        ContentValues cv = new ContentValues();
+        cv.put(ProviderTableMeta.VIRTUAL_TYPE, type.toString());
+        cv.put(ProviderTableMeta.VIRTUAL_OCFILE_ID, file.getFileId());
+
+        if (getContentResolver() != null) {
+            getContentResolver().insert(ProviderTableMeta.CONTENT_URI_VIRTUAL, cv);
+        } else {
+            try {
+                getContentProviderClient().insert(ProviderTableMeta.CONTENT_URI_VIRTUAL, cv);
+            } catch (RemoteException e) {
+                Log_OC.e(TAG, FAILED_TO_INSERT_MSG + e.getMessage(), e);
+            }
+        }
+    }
+
+    public Vector<OCFile> getVirtualFolderContent(VirtualFolderType type, boolean onlyImages) {
+        Vector<OCFile> ocFiles = new Vector<>();
+        Uri req_uri = ProviderTableMeta.CONTENT_URI_VIRTUAL;
+        Cursor c;
+
+        if (getContentProviderClient() != null) {
+            try {
+                c = getContentProviderClient().query(
+                        req_uri,
+                        null,
+                        ProviderTableMeta.VIRTUAL_TYPE + "=?",
+                        new String[]{String.valueOf(type)},
+                        null
+                );
+            } catch (RemoteException e) {
+                Log_OC.e(TAG, e.getMessage(), e);
+                return ocFiles;
+            }
+        } else {
+            c = getContentResolver().query(
+                    req_uri,
+                    null,
+                    ProviderTableMeta.VIRTUAL_TYPE + "=?",
+                    new String[]{String.valueOf(type)},
+                    null
+            );
+        }
+
+        if (c != null && c.moveToFirst()) {
+            do {
+                OCFile child = createFileInstanceFromVirtual(c);
+                ocFiles.add(child);
+            } while (c.moveToNext());
+            c.close();
+        }
+
+        if (onlyImages) {
+            OCFile current = null;
+            Vector<OCFile> temp = new Vector<>();
+            for (int i=0; i < ocFiles.size(); i++) {
+                current = ocFiles.get(i);
+                if (MimeTypeUtil.isImage(current)) {
+                    temp.add(current);
+                }
+            }
+            ocFiles = temp;
+        }
+
+        if (ocFiles.size() > 0) {
+            Collections.sort(ocFiles);
+        }
+
+        return ocFiles;
+    }
+
 }

+ 30 - 0
src/main/java/com/owncloud/android/datamodel/VirtualFolderType.java

@@ -0,0 +1,30 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2017 Tobias Kaminsky
+ * 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.datamodel;
+
+/**
+ * Type for virtual folders
+ */
+
+public enum VirtualFolderType {
+    FAVORITE, PHOTOS, NONE
+}

+ 7 - 1
src/main/java/com/owncloud/android/db/ProviderMeta.java

@@ -32,7 +32,7 @@ import com.owncloud.android.MainApp;
 public class ProviderMeta {
 
     public static final String DB_NAME = "filelist";
-    public static final int DB_VERSION = 20;
+    public static final int DB_VERSION = 21;
 
     private ProviderMeta() {
     }
@@ -45,6 +45,7 @@ public class ProviderMeta {
         public static final String SYNCED_FOLDERS_TABLE_NAME = "synced_folders";
         public static final String EXTERNAL_LINKS_TABLE_NAME = "external_links";
         public static final String ARBITRARY_DATA_TABLE_NAME = "arbitrary_data";
+        public static final String VIRTUAL_TABLE_NAME = "virtual";
 
         private static final String CONTENT_PREFIX = "content://";
 
@@ -66,6 +67,7 @@ public class ProviderMeta {
                 + MainApp.getAuthority() + "/external_links");
         public static final Uri CONTENT_URI_ARBITRARY_DATA = Uri.parse(CONTENT_PREFIX
                 + MainApp.getAuthority() + "/arbitrary_data");
+        public static final Uri CONTENT_URI_VIRTUAL = Uri.parse(CONTENT_PREFIX + MainApp.getAuthority() + "/virtual");
 
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.owncloud.file";
         public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.owncloud.file";
@@ -185,5 +187,9 @@ public class ProviderMeta {
         public static final String ARBITRARY_DATA_CLOUD_ID = "cloud_id";
         public static final String ARBITRARY_DATA_KEY = "key";
         public static final String ARBITRARY_DATA_VALUE = "value";
+
+        // Columns of virtual
+        public static final String VIRTUAL_TYPE = "type";
+        public static final String VIRTUAL_OCFILE_ID = "ocfile_id";
     }
 }

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

@@ -73,6 +73,7 @@ public class FileContentProvider extends ContentProvider {
     private static final int SYNCED_FOLDERS = 7;
     private static final int EXTERNAL_LINKS = 8;
     private static final int ARBITRARY_DATA = 9;
+    private static final int VIRTUAL = 10;
 
     private static final String TAG = FileContentProvider.class.getSimpleName();
 
@@ -205,6 +206,9 @@ public class FileContentProvider extends ContentProvider {
             case ARBITRARY_DATA:
                 count = db.delete(ProviderTableMeta.ARBITRARY_DATA_TABLE_NAME, where, whereArgs);
                 break;
+            case VIRTUAL:
+                count = db.delete(ProviderTableMeta.VIRTUAL_TABLE_NAME, where, whereArgs);
+                break;
             default:
                 //Log_OC.e(TAG, "Unknown uri " + uri);
                 throw new IllegalArgumentException("Unknown uri: " + uri.toString());
@@ -350,7 +354,17 @@ public class FileContentProvider extends ContentProvider {
 
                 }
                 return insertedArbitraryDataUri;
+            case VIRTUAL:
+                Uri insertedVirtualUri;
+                long virtualId = db.insert(ProviderTableMeta.VIRTUAL_TABLE_NAME, null, values);
 
+                if (virtualId > 0) {
+                    insertedVirtualUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_VIRTUAL, virtualId);
+                } else {
+                    throw new SQLException("ERROR " + uri);
+                }
+
+                return insertedVirtualUri;
             default:
                 throw new IllegalArgumentException("Unknown uri id: " + uri);
         }
@@ -402,6 +416,7 @@ public class FileContentProvider extends ContentProvider {
         mUriMatcher.addURI(authority, "synced_folders", SYNCED_FOLDERS);
         mUriMatcher.addURI(authority, "external_links", EXTERNAL_LINKS);
         mUriMatcher.addURI(authority, "arbitrary_data", ARBITRARY_DATA);
+        mUriMatcher.addURI(authority, "virtual", VIRTUAL);
 
         return true;
     }
@@ -497,6 +512,12 @@ public class FileContentProvider extends ContentProvider {
                             + uri.getPathSegments().get(1));
                 }
                 break;
+            case VIRTUAL:
+                sqlQuery.setTables(ProviderTableMeta.VIRTUAL_TABLE_NAME);
+                if (uri.getPathSegments().size() > 1) {
+                    sqlQuery.appendWhere(ProviderTableMeta._ID + "=" + uri.getPathSegments().get(1));
+                }
+                break;
             default:
                 throw new IllegalArgumentException("Unknown uri id: " + uri);
         }
@@ -522,6 +543,9 @@ public class FileContentProvider extends ContentProvider {
                 case ARBITRARY_DATA:
                     order = ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID;
                     break;
+                case VIRTUAL:
+                    order = ProviderTableMeta.VIRTUAL_TYPE;
+                    break;
                 default: // Files
                     order = ProviderTableMeta.FILE_DEFAULT_SORT_ORDER;
                     break;
@@ -635,6 +659,9 @@ public class FileContentProvider extends ContentProvider {
 
             // Create arbitrary data table
             createArbitraryData(db);
+
+            // Create virtual table
+            createVirtualTable(db);
         }
 
         @Override
@@ -881,7 +908,7 @@ public class FileContentProvider extends ContentProvider {
             }
 
             if (oldVersion < 16 && newVersion >= 16) {
-                Log_OC.i("SQL", "Entering in the #16 ADD synced folders table");
+                Log_OC.i(SQL, "Entering in the #16 ADD synced folders table");
                 db.beginTransaction();
                 try {
                     // Create synced folders table
@@ -898,7 +925,7 @@ public class FileContentProvider extends ContentProvider {
             }
 
             if (oldVersion < 17 && newVersion >= 17) {
-                Log_OC.i(SQL, "Entering in the #4 ADD in onUpgrade");
+                Log_OC.i(SQL, "Entering in the #17 ADD in onUpgrade");
                 db.beginTransaction();
                 try {
                     db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME +
@@ -918,7 +945,7 @@ public class FileContentProvider extends ContentProvider {
             }
 
             if (oldVersion < 18 && newVersion >= 18) {
-                Log_OC.i(SQL, "Adding external link column to capabilities");
+                Log_OC.i(SQL, "Entering in the #18 Adding external link column to capabilities");
                 db.beginTransaction();
                 try {
                     db.execSQL(ALTER_TABLE + ProviderTableMeta.CAPABILITIES_TABLE_NAME +
@@ -937,7 +964,7 @@ public class FileContentProvider extends ContentProvider {
             }
 
             if (oldVersion < 19 && newVersion >= 19) {
-                Log_OC.i(SQL, "Adding external link column to capabilities");
+                Log_OC.i(SQL, "Entering in the #19 Adding external link column to capabilities");
                 db.beginTransaction();
                 try {
                     createExternalLinksTable(db);
@@ -953,7 +980,7 @@ public class FileContentProvider extends ContentProvider {
             }
 
             if (oldVersion < 20 && newVersion >= 20) {
-                Log_OC.i(SQL, "Adding arbitrary data table");
+                Log_OC.i(SQL, "Entering in the #20 Adding arbitrary data table");
                 db.beginTransaction();
                 try {
                     createArbitraryData(db);
@@ -967,6 +994,22 @@ public class FileContentProvider extends ContentProvider {
             if (!upgraded) {
                 Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
             }
+
+            if (oldVersion < 21 && newVersion >= 21) {
+                Log_OC.i(SQL, "Entering in the #21 Adding virtual table");
+                db.beginTransaction();
+                try {
+                    createVirtualTable(db);
+                    upgraded = true;
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                }
+            }
+
+            if (!upgraded) {
+                Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
+            }
         }
     }
 
@@ -1047,7 +1090,7 @@ public class FileContentProvider extends ContentProvider {
                 + ProviderTableMeta.CAPABILITIES_FILES_UNDELETE + INTEGER  // boolean
                 + ProviderTableMeta.CAPABILITIES_FILES_VERSIONING + INTEGER   // boolean
                 + ProviderTableMeta.CAPABILITIES_FILES_DROP + INTEGER  // boolean
-                + ProviderTableMeta.CAPABILITIES_EXTERNAL_LINKS + " INTEGER );" );   // boolean
+                + ProviderTableMeta.CAPABILITIES_EXTERNAL_LINKS + " INTEGER );");   // boolean
     }
 
     private void createUploadsTable(SQLiteDatabase db) {
@@ -1113,6 +1156,14 @@ public class FileContentProvider extends ContentProvider {
         );
     }
 
+    private void createVirtualTable(SQLiteDatabase db) {
+        db.execSQL("CREATE TABLE " + ProviderTableMeta.VIRTUAL_TABLE_NAME + "("
+                + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "          // id
+                + ProviderTableMeta.VIRTUAL_TYPE + " TEXT, "                // type
+                + ProviderTableMeta.VIRTUAL_OCFILE_ID + " INTEGER )"        // file id
+        );
+    }
+
     /**
      * Version 10 of database does not modify its scheme. It coincides with the upgrade of the ownCloud account names
      * structure to include in it the path to the server instance. Updating the account names and path to local files
@@ -1120,7 +1171,7 @@ public class FileContentProvider extends ContentProvider {
      *
      * See {@link com.owncloud.android.authentication.AccountUtils#updateAccountVersion(android.content.Context)}
      *
-     * @param db        Database where table of files is included.
+     * @param db Database where table of files is included.
      */
     private void updateAccountName(SQLiteDatabase db) {
         Log_OC.d(SQL, "THREAD:  " + Thread.currentThread().getName());
@@ -1177,9 +1228,9 @@ public class FileContentProvider extends ContentProvider {
      * Rename the local ownCloud folder of one account to match the a rename of the account itself. Updates the
      * table of files in database so that the paths to the local files keep being the same.
      *
-     * @param db                    Database where table of files is included.
-     * @param newAccountName        New name for the target OC account.
-     * @param oldAccountName        Old name of the target OC account.
+     * @param db             Database where table of files is included.
+     * @param newAccountName New name for the target OC account.
+     * @param oldAccountName Old name of the target OC account.
      */
     private void updateDownloadedFiles(SQLiteDatabase db, String newAccountName,
                                        String oldAccountName) {

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

@@ -62,6 +62,7 @@ import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.VirtualFolderType;
 import com.owncloud.android.db.PreferenceManager;
 import com.owncloud.android.files.services.FileDownloader;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
@@ -231,6 +232,7 @@ public class FileDisplayActivity extends HookActivity
     protected void onPostCreate(Bundle savedInstanceState) {
         super.onPostCreate(savedInstanceState);
 
+
         if (!PermissionUtil.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
             // Check if we should show an explanation
             if (PermissionUtil.shouldShowRequestPermissionRationale(this,
@@ -255,22 +257,20 @@ public class FileDisplayActivity extends HookActivity
         }
 
         if (getIntent().getParcelableExtra(OCFileListFragment.SEARCH_EVENT) != null) {
-            switchToSearchFragment();
+            switchToSearchFragment(savedInstanceState);
 
             int menuId = getIntent().getIntExtra(DRAWER_MENU_ID, -1);
             if (menuId != -1) {
                 setupDrawer(menuId);
             }
-        } else if (savedInstanceState == null) {
-            createMinFragments();
+        } else {
+            createMinFragments(savedInstanceState);
             refreshList(true);
         }
 
         setIndeterminate(mSyncInProgress);
         // always AFTER setContentView(...) in onCreate(); to work around bug in its implementation
 
-        setBackgroundText();
-
         upgradeNotificationForInstantUpload();
     }
 
@@ -395,28 +395,36 @@ public class FileDisplayActivity extends HookActivity
         }
     }
 
-    private void switchToSearchFragment() {
-        OCFileListFragment listOfFiles = new OCFileListFragment();
-        Bundle args = new Bundle();
+    private void switchToSearchFragment(Bundle savedInstanceState) {
+        if (savedInstanceState == null) {
+            OCFileListFragment listOfFiles = new OCFileListFragment();
+            Bundle args = new Bundle();
 
-        args.putParcelable(OCFileListFragment.SEARCH_EVENT,
-                getIntent().getParcelableExtra(OCFileListFragment.SEARCH_EVENT));
-        args.putBoolean(OCFileListFragment.ARG_ALLOW_CONTEXTUAL_ACTIONS, true);
+            args.putParcelable(OCFileListFragment.SEARCH_EVENT,
+                    getIntent().getParcelableExtra(OCFileListFragment.SEARCH_EVENT));
+            args.putBoolean(OCFileListFragment.ARG_ALLOW_CONTEXTUAL_ACTIONS, true);
 
-        listOfFiles.setArguments(args);
-        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-        transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES);
-        transaction.commit();
+            listOfFiles.setArguments(args);
+            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+            transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES);
+            transaction.commit();
+        } else {
+            getSupportFragmentManager().findFragmentByTag(TAG_LIST_OF_FILES);
+        }
     }
 
-    private void createMinFragments() {
-        OCFileListFragment listOfFiles = new OCFileListFragment();
-        Bundle args = new Bundle();
-        args.putBoolean(OCFileListFragment.ARG_ALLOW_CONTEXTUAL_ACTIONS, true);
-        listOfFiles.setArguments(args);
-        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-        transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES);
-        transaction.commit();
+    private void createMinFragments(Bundle savedInstanceState) {
+        if (savedInstanceState == null) {
+            OCFileListFragment listOfFiles = new OCFileListFragment();
+            Bundle args = new Bundle();
+            args.putBoolean(OCFileListFragment.ARG_ALLOW_CONTEXTUAL_ACTIONS, true);
+            listOfFiles.setArguments(args);
+            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+            transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES);
+            transaction.commit();
+        } else {
+            getSupportFragmentManager().findFragmentByTag(TAG_LIST_OF_FILES);
+        }
     }
 
     private void initFragmentsWithFile() {
@@ -566,7 +574,7 @@ public class FileDisplayActivity extends HookActivity
             tr.commit();
         }
         updateFragmentsVisibility(false);
-        updateActionBarTitleAndHomeButton(null);
+        //updateActionBarTitleAndHomeButton(null);
     }
 
     public void refreshListOfFilesFragment(boolean fromSearch) {
@@ -576,6 +584,14 @@ public class FileDisplayActivity extends HookActivity
         }
     }
 
+    public void resetSearchView() {
+        OCFileListFragment fileListFragment = getListOfFilesFragment();
+
+        if (fileListFragment != null) {
+            fileListFragment.setSearchFragment(false);
+        }
+    }
+
     protected void refreshSecondFragment(String downloadEvent, String downloadedRemotePath,
                                          boolean success) {
         FileFragment secondFragment = getSecondFragment();
@@ -1053,20 +1069,22 @@ public class FileDisplayActivity extends HookActivity
 
         if (searchView != null && !TextUtils.isEmpty(searchQuery)) {
             searchView.setQuery(searchQuery, true);
-        } else {
+        } else if (getListOfFilesFragment() != null && !getListOfFilesFragment().getIsSearchFragment()) {
             refreshListOfFilesFragment(false);
         }
 
         // Listen for sync messages
-        IntentFilter syncIntentFilter = new IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START);
-        syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_END);
-        syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED);
-        syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED);
-        syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED);
-        mSyncBroadcastReceiver = new SyncBroadcastReceiver();
-        registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
-        //LocalBroadcastManager.getInstance(this).registerReceiver(mSyncBroadcastReceiver,
-        // syncIntentFilter);
+        if (getListOfFilesFragment() != null && !getListOfFilesFragment().getIsSearchFragment()) {
+            IntentFilter syncIntentFilter = new IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START);
+            syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_END);
+            syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED);
+            syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED);
+            syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED);
+            mSyncBroadcastReceiver = new SyncBroadcastReceiver();
+            registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
+            //LocalBroadcastManager.getInstance(this).registerReceiver(mSyncBroadcastReceiver,
+            // syncIntentFilter);
+        }
 
         // Listen for upload messages
         IntentFilter uploadIntentFilter = new IntentFilter(FileUploader.getUploadFinishMessage());
@@ -1257,7 +1275,7 @@ public class FileDisplayActivity extends HookActivity
      * loading or folder is empty
      */
     private void setBackgroundText() {
-        OCFileListFragment ocFileListFragment = getListOfFilesFragment();
+        final OCFileListFragment ocFileListFragment = getListOfFilesFragment();
         if (ocFileListFragment != null) {
             if (!mSyncInProgress) {
                 ocFileListFragment.setEmptyListMessage(ExtendedListFragment.SearchType.NO_SEARCH);
@@ -1928,6 +1946,19 @@ public class FileDisplayActivity extends HookActivity
         showDetailsIntent.putExtra(EXTRA_FILE, file);
         showDetailsIntent.putExtra(EXTRA_ACCOUNT, getAccount());
         startActivity(showDetailsIntent);
+    }
+
+    /**
+     * Opens the image gallery showing the image {@link OCFile} received as parameter.
+     *
+     * @param file Image {@link OCFile} to show.
+     */
+    public void startImagePreview(OCFile file, VirtualFolderType type) {
+        Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class);
+        showDetailsIntent.putExtra(PreviewImageActivity.EXTRA_FILE, file);
+        showDetailsIntent.putExtra(EXTRA_ACCOUNT, getAccount());
+        showDetailsIntent.putExtra(PreviewImageActivity.EXTRA_VIRTUAL_TYPE, type);
+        startActivity(showDetailsIntent);
 
     }
 
@@ -2032,7 +2063,7 @@ public class FileDisplayActivity extends HookActivity
 
     private void refreshList(boolean ignoreETag) {
         OCFileListFragment listOfFiles = getListOfFilesFragment();
-        if (listOfFiles != null) {
+        if (listOfFiles != null && !listOfFiles.getIsSearchFragment()) {
             OCFile folder = listOfFiles.getCurrentFile();
             if (folder != null) {
                 /*mFile = mContainerActivity.getStorageManager().getFileById(mFile.getFileId());

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

@@ -24,6 +24,7 @@ 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;
@@ -46,7 +47,9 @@ 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;
@@ -54,6 +57,7 @@ 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.services.OperationsService.OperationsServiceBinder;
 import com.owncloud.android.ui.activity.ComponentsGetter;
 import com.owncloud.android.ui.fragment.ExtendedListFragment;
@@ -477,39 +481,18 @@ public class FileListListAdapter extends BaseAdapter {
         }
     }
 
-    public void setData(ArrayList<Object> objects, ExtendedListFragment.SearchType searchType) {
+    public void setData(ArrayList<Object> objects, ExtendedListFragment.SearchType searchType, FileDataStorageManager storageManager) {
+        if (storageManager != null && mStorageManager == null) {
+            mStorageManager = storageManager;
+        }
         mFiles = new Vector<>();
-        if (searchType.equals(ExtendedListFragment.SearchType.SHARED_FILTER)) {
-            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));
 
-                        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);
-        } else {
-            for (int i = 0; i < objects.size(); i++) {
-                OCFile ocFile = FileStorageUtils.fillOCFile((RemoteFile) objects.get(i));
-                searchForLocalFileInDefaultPath(ocFile);
-                mFiles.add(ocFile);
+        // early exit
+        if (objects.size() > 0 && mStorageManager != null) {
+            if (searchType.equals(ExtendedListFragment.SearchType.SHARED_FILTER)) {
+                parseShares(objects);
+            } else {
+                parseVirtuals(objects, searchType);
             }
         }
 
@@ -534,6 +517,81 @@ public class FileListListAdapter extends BaseAdapter {
         });
     }
 
+    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));
+
+                    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);
+            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);
+        }
+
+        mStorageManager.saveVirtuals(type, contentValues);
+    }
+
     /**
      * Filter for getting only the folders
      *

+ 19 - 18
src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java

@@ -5,16 +5,16 @@
  * Copyright (C) 2017 Mario Danic
  * Copyright (C) 2012 Bartek Przybylski
  * Copyright (C) 2012-2016 ownCloud Inc.
- *
+ * <p>
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2,
  * as published by the Free Software Foundation.
- *
+ * <p>
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
+ * <p>
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -94,7 +94,7 @@ public class ExtendedListFragment extends Fragment
     private static final String KEY_IS_GRID_VISIBLE = "IS_GRID_VISIBLE";
 
     protected SwipeRefreshLayout mRefreshListLayout;
-    private SwipeRefreshLayout mRefreshGridLayout;
+    protected SwipeRefreshLayout mRefreshGridLayout;
     protected SwipeRefreshLayout mRefreshEmptyLayout;
     protected LinearLayout mEmptyListContainer;
     protected TextView mEmptyListMessage;
@@ -294,13 +294,8 @@ public class ExtendedListFragment extends Fragment
 
     @Override
     public boolean onQueryTextSubmit(String query) {
-        if (getFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_SECOND_FRAGMENT)
-                instanceof ExtendedListFragment){
-            performSearch(query, true);
-            return true;
-        } else {
-            return false;
-        }
+        performSearch(query, true);
+        return true;
     }
 
     private void performSearch(final String query, boolean isSubmit) {
@@ -345,7 +340,9 @@ public class ExtendedListFragment extends Fragment
             Activity activity;
             if ((activity = getActivity()) != null) {
                 if (activity instanceof FileDisplayActivity) {
-                    ((FileDisplayActivity) activity).refreshListOfFilesFragment(true);
+                    FileDisplayActivity fileDisplayActivity = (FileDisplayActivity) activity;
+                    fileDisplayActivity.resetSearchView();
+                    fileDisplayActivity.refreshListOfFilesFragment(true);
                 } else if (activity instanceof UploadFilesActivity) {
                     LocalFileListAdapter localFileListAdapter = (LocalFileListAdapter) mAdapter;
                     localFileListAdapter.filter(query);
@@ -429,6 +426,10 @@ public class ExtendedListFragment extends Fragment
         return v;
     }
 
+    public void setEmptyListVisible() {
+        mEmptyListContainer.setVisibility(View.VISIBLE);
+    }
+
     protected void setupEmptyList(View view) {
         mEmptyListContainer = (LinearLayout) view.findViewById(R.id.empty_list_view);
         mEmptyListMessage = (TextView) view.findViewById(R.id.empty_list_view_text);
@@ -450,7 +451,6 @@ public class ExtendedListFragment extends Fragment
             mTops = savedInstanceState.getIntegerArrayList(KEY_TOPS);
             mHeightCell = savedInstanceState.getInt(KEY_HEIGHT_CELL);
             setMessageForEmptyList(savedInstanceState.getString(KEY_EMPTY_LIST_MESSAGE));
-
         } else {
             mIndexes = new ArrayList<>();
             mFirstPositions = new ArrayList<>();
@@ -477,7 +477,7 @@ public class ExtendedListFragment extends Fragment
      * 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.
      *
@@ -582,9 +582,9 @@ public class ExtendedListFragment extends Fragment
 
     /**
      * Disables swipe gesture.
-     *
+     * <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,
      *
      * @param enabled Desired state for capturing swipe gesture.
@@ -597,7 +597,7 @@ public class ExtendedListFragment extends Fragment
 
     /**
      * Sets the 'visibility' state of the FAB contained in the fragment.
-     *
+     * <p>
      * When 'false' is set, FAB visibility is set to View.GONE programmatically,
      *
      * @param enabled Desired visibility for the FAB.
@@ -646,6 +646,7 @@ public class ExtendedListFragment extends Fragment
 
                     mEmptyListIcon.setVisibility(View.VISIBLE);
                     mEmptyListProgress.setVisibility(View.GONE);
+                    mEmptyListMessage.setVisibility(View.VISIBLE);
                 }
             }
         });
@@ -656,7 +657,7 @@ public class ExtendedListFragment extends Fragment
             @Override
             public void run() {
 
-                if (searchType == SearchType.NO_SEARCH) {
+                if (searchType == SearchType.NO_SEARCH && mEmptyListProgress.getVisibility() == View.GONE) {
                     setMessageForEmptyList(
                             R.string.file_list_empty_headline,
                             R.string.file_list_empty,

+ 211 - 111
src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -59,6 +59,7 @@ 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.VirtualFolderType;
 import com.owncloud.android.files.FileMenuFilter;
 import com.owncloud.android.lib.common.OwnCloudAccount;
 import com.owncloud.android.lib.common.OwnCloudClient;
@@ -178,7 +179,13 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         mProgressBarActionModeColor = getResources().getColor(R.color.action_mode_background);
         mProgressBarColor = getResources().getColor(R.color.primary);
         mMultiChoiceModeListener = new MultiChoiceModeListener();
-        searchFragment = false;
+
+        if (savedInstanceState != null) {
+            currentSearchType = Parcels.unwrap(savedInstanceState.getParcelable(KEY_CURRENT_SEARCH_TYPE));
+            searchEvent = Parcels.unwrap(savedInstanceState.getParcelable(OCFileListFragment.SEARCH_EVENT));
+        }
+
+        searchFragment = currentSearchType != null;
     }
 
     /**
@@ -219,6 +226,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             currentSearchType = SearchType.NO_SEARCH;
         }
 
+        searchFragment = savedInstanceState != null;
 
         if (getResources().getBoolean(R.bool.bottom_toolbar_enabled)) {
             bottomNavigationView.setVisibility(View.VISIBLE);
@@ -254,10 +262,6 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
     public void onResume() {
         super.onResume();
 
-        if (remoteOperationAsyncTask != null) {
-            remoteOperationAsyncTask.cancel(true);
-        }
-
         if (getActivity() != null) {
             AnalyticsUtils.setCurrentScreenName(getActivity(), SCREEN_NAME, TAG);
         }
@@ -282,6 +286,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         super.onActivityCreated(savedInstanceState);
         Log_OC.i(TAG, "onActivityCreated() start");
 
+
         if (savedInstanceState != null) {
             mFile = savedInstanceState.getParcelable(KEY_FILE);
         }
@@ -325,9 +330,38 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         }
 
         searchEvent = Parcels.unwrap(getArguments().getParcelable(OCFileListFragment.SEARCH_EVENT));
-        if (searchEvent != null) {
+        if (searchEvent != null && searchFragment && savedInstanceState == null) {
             onMessageEvent(searchEvent);
         }
+
+        prepareCurrentSearch(searchEvent);
+        setTitle();
+
+    }
+
+    private void prepareCurrentSearch(SearchEvent event) {
+        if (event != null) {
+            if (event.getSearchType().equals(SearchOperation.SearchType.FILE_SEARCH)) {
+                currentSearchType = SearchType.FILE_SEARCH;
+
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.CONTENT_TYPE_SEARCH)) {
+                if (event.getSearchQuery().equals("image/%")) {
+                    currentSearchType = SearchType.PHOTO_SEARCH;
+                } else if (event.getSearchQuery().equals("video/%")) {
+                    currentSearchType = SearchType.VIDEO_SEARCH;
+                }
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.FAVORITE_SEARCH)) {
+                currentSearchType = SearchType.FAVORITE_SEARCH;
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_ADDED_SEARCH)) {
+                currentSearchType = SearchType.RECENTLY_ADDED_SEARCH;
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_MODIFIED_SEARCH)) {
+                currentSearchType = SearchType.RECENTLY_MODIFIED_SEARCH;
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.SHARED_SEARCH)) {
+                currentSearchType = SearchType.SHARED_FILTER;
+            }
+
+            prepareActionBarItems(event);
+        }
     }
 
     /**
@@ -611,7 +645,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             DisplayUtils.colorToolbarProgressBar(getActivity(), mProgressBarColor);
 
             // show FAB on multi selection mode exit
-            if (!mHideFab) {
+            if (!mHideFab && !searchFragment) {
                 setFabEnabled(true);
             }
         }
@@ -673,7 +707,10 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         outState.putParcelable(KEY_FILE, mFile);
-        outState.putParcelable(KEY_CURRENT_SEARCH_TYPE, Parcels.wrap(currentSearchType));
+        if (searchFragment) {
+            outState.putParcelable(KEY_CURRENT_SEARCH_TYPE, Parcels.wrap(currentSearchType));
+            outState.putParcelable(OCFileListFragment.SEARCH_EVENT, Parcels.wrap(searchEvent));
+        }
         mMultiChoiceModeListener.storeStateIn(outState);
     }
 
@@ -796,6 +833,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         if (file != null) {
             if (file.isFolder()) {
                 // update state and view of this fragment
+                searchFragment = false;
                 listDirectory(file, MainApp.isOnlyOnDevice(), false);
                 // then, notify parent activity to let it update its state and view
                 mContainerActivity.onBrowsedDownTo(file);
@@ -805,8 +843,24 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             } else { /// Click on a file
                 if (PreviewImageFragment.canBePreviewed(file)) {
                     // preview image - it handles the download, if needed
-                    ((FileDisplayActivity) mContainerActivity).startImagePreview(file);
-                } else if (file.isDown() && MimeTypeUtil.isVCard(file)){
+                    if (searchFragment) {
+                        VirtualFolderType type;
+                        switch (currentSearchType) {
+                            case FAVORITE_SEARCH:
+                                type = VirtualFolderType.FAVORITE;
+                                break;
+                            case PHOTO_SEARCH:
+                                type = VirtualFolderType.PHOTOS;
+                                break;
+                            default:
+                                type = VirtualFolderType.NONE;
+                                break;
+                        }
+                        ((FileDisplayActivity) mContainerActivity).startImagePreview(file, type);
+                    } else {
+                        ((FileDisplayActivity) mContainerActivity).startImagePreview(file);
+                    }
+                } else if (file.isDown() && MimeTypeUtil.isVCard(file)) {
                     ((FileDisplayActivity) mContainerActivity).startContactListFragment(file);
                 } else if (PreviewTextFragment.canBePreviewed(file)) {
                     ((FileDisplayActivity) mContainerActivity).startTextPreview(file);
@@ -959,10 +1013,6 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
     public void refreshDirectory() {
         searchFragment = false;
 
-        if (remoteOperationAsyncTask != null) {
-            remoteOperationAsyncTask.cancel(true);
-        }
-
         setFabEnabled(true);
         listDirectory(getCurrentFile(), MainApp.isOnlyOnDevice(), false);
     }
@@ -1236,6 +1286,92 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
 
     }
 
+    public void setTitleFromSearchEvent(SearchEvent event) {
+        prepareCurrentSearch(event);
+        setTitle();
+    }
+
+    private void setTitle() {
+        // set title
+
+        if (getActivity() instanceof FileDisplayActivity && currentSearchType != null) {
+            switch (currentSearchType) {
+                case FAVORITE_SEARCH:
+                    setTitle(R.string.drawer_item_favorites);
+                    break;
+                case PHOTO_SEARCH:
+                    setTitle(R.string.drawer_item_photos);
+                    break;
+                case VIDEO_SEARCH:
+                    setTitle(R.string.drawer_item_videos);
+                    break;
+                case RECENTLY_ADDED_SEARCH:
+                    setTitle(R.string.drawer_item_recently_added);
+                    break;
+                case RECENTLY_MODIFIED_SEARCH:
+                    setTitle(R.string.drawer_item_recently_modified);
+                    break;
+                case SHARED_FILTER:
+                    setTitle(R.string.drawer_item_shared);
+                    break;
+                default:
+                    setTitle(R.string.default_display_name_for_root_folder);
+                    break;
+            }
+        }
+
+    }
+
+    private void prepareActionBarItems(SearchEvent event) {
+        if (event != null) {
+            if (event.getSearchType().equals(SearchOperation.SearchType.CONTENT_TYPE_SEARCH)) {
+                if (event.getSearchQuery().equals("image/%")) {
+                    menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_GRID_AND_SORT;
+                } else if (event.getSearchQuery().equals("video/%")) {
+                    menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SEARCH;
+                }
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.FAVORITE_SEARCH)) {
+                menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT;
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_ADDED_SEARCH)) {
+                menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT;
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_MODIFIED_SEARCH)) {
+                menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT;
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.SHARED_SEARCH)) {
+                menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SEARCH;
+            }
+
+        }
+
+        if (currentSearchType != null && !currentSearchType.equals(SearchType.FILE_SEARCH) && getActivity() != null) {
+            getActivity().invalidateOptionsMenu();
+        }
+    }
+
+    private void setEmptyView(SearchEvent event) {
+
+        if (event != null) {
+            if (event.getSearchType().equals(SearchOperation.SearchType.FILE_SEARCH)) {
+                setEmptyListMessage(SearchType.FILE_SEARCH);
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.CONTENT_TYPE_SEARCH)) {
+                if (event.getSearchQuery().equals("image/%")) {
+                    setEmptyListMessage(SearchType.PHOTO_SEARCH);
+                } else if (event.getSearchQuery().equals("video/%")) {
+                    setEmptyListMessage(SearchType.VIDEO_SEARCH);
+                }
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.FAVORITE_SEARCH)) {
+                setEmptyListMessage(SearchType.FAVORITE_SEARCH);
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_ADDED_SEARCH)) {
+                setEmptyListMessage(SearchType.RECENTLY_ADDED_SEARCH);
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_MODIFIED_SEARCH)) {
+                setEmptyListMessage(SearchType.RECENTLY_MODIFIED_SEARCH);
+            } else if (event.getSearchType().equals(SearchOperation.SearchType.SHARED_SEARCH)) {
+                setEmptyListMessage(SearchType.SHARED_FILTER);
+            }
+
+        }
+
+    }
+
     @Subscribe(threadMode = ThreadMode.MAIN)
     public void onMessageEvent(ChangeMenuEvent changeMenuEvent) {
         menuItemAddRemoveValue = MenuItemAddRemove.ADD_GRID_AND_SORT_WITH_SEARCH;
@@ -1244,6 +1380,9 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             setTitle(R.string.default_display_name_for_root_folder);
         }
 
+        getActivity().getIntent().removeExtra(OCFileListFragment.SEARCH_EVENT);
+        getArguments().putParcelable(OCFileListFragment.SEARCH_EVENT, null);
+
         setFabEnabled(true);
     }
 
@@ -1281,10 +1420,10 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
     }
 
     @Subscribe(threadMode = ThreadMode.BACKGROUND)
-    public void onMessageEvent(SearchEvent event) {
+    public void onMessageEvent(final SearchEvent event) {
         searchFragment = true;
         setEmptyListLoadingMessage();
-        mAdapter.setData(new ArrayList<>(), SearchType.NO_SEARCH);
+        mAdapter.setData(new ArrayList<>(), SearchType.NO_SEARCH, mContainerActivity.getStorageManager());
 
         setFabEnabled(false);
 
@@ -1294,51 +1433,6 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             unsetAllMenuItems(true);
         }
 
-        if (event.getSearchType().equals(SearchOperation.SearchType.FILE_SEARCH)) {
-            currentSearchType = SearchType.FILE_SEARCH;
-
-        } else if (event.getSearchType().equals(SearchOperation.SearchType.CONTENT_TYPE_SEARCH)) {
-            if (event.getSearchQuery().equals("image/%")) {
-                currentSearchType = SearchType.PHOTO_SEARCH;
-            } else if (event.getSearchQuery().equals("video/%")) {
-                currentSearchType = SearchType.VIDEO_SEARCH;
-            }
-        } else if (event.getSearchType().equals(SearchOperation.SearchType.FAVORITE_SEARCH)) {
-            currentSearchType = SearchType.FAVORITE_SEARCH;
-        } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_ADDED_SEARCH)) {
-            currentSearchType = SearchType.RECENTLY_ADDED_SEARCH;
-        } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_MODIFIED_SEARCH)) {
-            currentSearchType = SearchType.RECENTLY_MODIFIED_SEARCH;
-        } else if (event.getSearchType().equals(SearchOperation.SearchType.SHARED_SEARCH)) {
-            currentSearchType = SearchType.SHARED_FILTER;
-        }
-
-        // set title
-        if (getActivity() instanceof FileDisplayActivity) {
-            switch (currentSearchType) {
-                case FAVORITE_SEARCH:
-                    setTitle(R.string.drawer_item_favorites);
-                    break;
-                case PHOTO_SEARCH:
-                    setTitle(R.string.drawer_item_photos);
-                    break;
-                case VIDEO_SEARCH:
-                    setTitle(R.string.drawer_item_videos);
-                    break;
-                case RECENTLY_ADDED_SEARCH:
-                    setTitle(R.string.drawer_item_recently_added);
-                    break;
-                case RECENTLY_MODIFIED_SEARCH:
-                    setTitle(R.string.drawer_item_recently_modified);
-                    break;
-                case SHARED_FILTER:
-                    setTitle(R.string.drawer_item_shared);
-                    break;
-                default:
-                    setTitle(R.string.default_display_name_for_root_folder);
-                    break;
-            }
-        }
 
         if (bottomNavigationView != null && searchEvent != null) {
             switch (currentSearchType) {
@@ -1366,6 +1460,20 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             }
         };
 
+        if (currentSearchType.equals(SearchType.PHOTO_SEARCH)) {
+            new Handler(Looper.getMainLooper()).post(new Runnable() {
+                @Override
+                public void run() {
+                    switchToGridView();
+                }
+            });
+        } else if (currentSearchType.equals(SearchType.NO_SEARCH) || currentSearchType.equals(
+                SearchType.REGULAR_FILTER)) {
+            new Handler(Looper.getMainLooper()).post(switchViewsRunnable);
+        } else {
+            new Handler(Looper.getMainLooper()).post(switchViewsRunnable);
+        }
+
         final RemoteOperation remoteOperation;
         if (!currentSearchType.equals(SearchType.SHARED_FILTER)) {
             remoteOperation = new SearchOperation(event.getSearchQuery(), event.getSearchType());
@@ -1381,9 +1489,26 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
                 if (getContext() != null && !isCancelled()) {
                     RemoteOperationResult remoteOperationResult = remoteOperation.execute(currentAccount, getContext());
 
+                    FileDataStorageManager storageManager = null;
+                    if (mContainerActivity != null && mContainerActivity.getStorageManager() != null) {
+                        storageManager = mContainerActivity.getStorageManager();
+                    }
+
                     if (remoteOperationResult.isSuccess() && remoteOperationResult.getData() != null
                             && !isCancelled() && searchFragment) {
-                        mAdapter.setData(remoteOperationResult.getData(), currentSearchType);
+                        if (remoteOperationResult.getData() == null || remoteOperationResult.getData().size() == 0) {
+                            setEmptyView(event);
+                        } else {
+                            mAdapter.setData(remoteOperationResult.getData(), currentSearchType, storageManager);
+                        }
+
+                        final FileDisplayActivity fileDisplayActivity = (FileDisplayActivity) getActivity();
+                        fileDisplayActivity.runOnUiThread(new Runnable() {
+                            @Override
+                            public void run() {
+                                fileDisplayActivity.setIndeterminate(false);
+                            }
+                        });
                     }
 
                     return remoteOperationResult.isSuccess();
@@ -1402,54 +1527,17 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
 
         remoteOperationAsyncTask.execute(true);
 
-        if (event.getSearchType().equals(SearchOperation.SearchType.FILE_SEARCH)) {
-            setEmptyListMessage(SearchType.FILE_SEARCH);
-
-        } else if (event.getSearchType().equals(SearchOperation.SearchType.CONTENT_TYPE_SEARCH)) {
-            if (event.getSearchQuery().equals("image/%")) {
-                setEmptyListMessage(SearchType.PHOTO_SEARCH);
-                menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_GRID_AND_SORT;
-            } else if (event.getSearchQuery().equals("video/%")) {
-                setEmptyListMessage(SearchType.VIDEO_SEARCH);
-                menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SEARCH;
-            }
-        } else if (event.getSearchType().equals(SearchOperation.SearchType.FAVORITE_SEARCH)) {
-            setEmptyListMessage(SearchType.FAVORITE_SEARCH);
-            menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT;
-        } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_ADDED_SEARCH)) {
-            setEmptyListMessage(SearchType.RECENTLY_ADDED_SEARCH);
-            menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT;
-        } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_MODIFIED_SEARCH)) {
-            setEmptyListMessage(SearchType.RECENTLY_MODIFIED_SEARCH);
-            menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT;
-        } else if (event.getSearchType().equals(SearchOperation.SearchType.SHARED_SEARCH)) {
-            setEmptyListMessage(SearchType.SHARED_FILTER);
-            menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SEARCH;
-        }
-
-        if (!currentSearchType.equals(SearchType.FILE_SEARCH) && getActivity() != null) {
-            getActivity().invalidateOptionsMenu();
-        }
-
-        if (currentSearchType.equals(SearchType.PHOTO_SEARCH)) {
-            new Handler(Looper.getMainLooper()).post(new Runnable() {
-                @Override
-                public void run() {
-                    switchToGridView();
-                }
-            });
-        } else if (currentSearchType.equals(SearchType.NO_SEARCH) || currentSearchType.equals(
-                SearchType.REGULAR_FILTER)) {
-            new Handler(Looper.getMainLooper()).post(switchViewsRunnable);
-        } else {
-            new Handler(Looper.getMainLooper()).post(switchViewsRunnable);
-        }
-
-        searchEvent = null;
     }
 
-    private void setTitle(@StringRes int title) {
-        ((FileDisplayActivity) getActivity()).getSupportActionBar().setTitle(title);
+    private void setTitle(@StringRes final int title) {
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (getActivity() != null && ((FileDisplayActivity) getActivity()).getSupportActionBar() != null) {
+                    ((FileDisplayActivity) getActivity()).getSupportActionBar().setTitle(title);
+                }
+            }
+        });
     }
 
     @Override
@@ -1467,10 +1555,22 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
 
     @Override
     public void onRefresh() {
-        super.onRefresh();
-
-        if (searchEvent != null) {
+        if (searchEvent != null && searchFragment) {
             onMessageEvent(searchEvent);
+
+            mRefreshListLayout.setRefreshing(false);
+            mRefreshGridLayout.setRefreshing(false);
+            mRefreshEmptyLayout.setRefreshing(false);
+        } else {
+            super.onRefresh();
         }
     }
+
+    public void setSearchFragment(boolean searchFragment) {
+        this.searchFragment = searchFragment;
+    }
+
+    public boolean getIsSearchFragment() {
+        return searchFragment;
+    }
 }

+ 28 - 14
src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java

@@ -40,6 +40,7 @@ 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.VirtualFolderType;
 import com.owncloud.android.files.services.FileDownloader;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader;
@@ -72,6 +73,8 @@ public class PreviewImageActivity extends FileActivity implements
     private static final String KEY_WAITING_FOR_BINDER = "WAITING_FOR_BINDER";
     private static final String KEY_SYSTEM_VISIBLE = "TRUE";
 
+    public static final String EXTRA_VIRTUAL_TYPE = "EXTRA_VIRTUAL_TYPE";
+
     private ExtendedViewPager mViewPager;
     private PreviewImagePagerAdapter mPreviewImagePagerAdapter;
     private int mSavedPosition = 0;
@@ -128,25 +131,36 @@ public class PreviewImageActivity extends FileActivity implements
     }
 
     private void initViewPager() {
-        // get parent from path
-        String parentPath = getFile().getRemotePath().substring(0,
-                getFile().getRemotePath().lastIndexOf(getFile().getFileName()));
-        OCFile parentFolder = getStorageManager().getFileByPath(parentPath);
-        if (parentFolder == null) {
-            // should not be necessary
-            parentFolder = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
-        }
+        // virtual folder
+        if (getIntent().getSerializableExtra(EXTRA_VIRTUAL_TYPE) != null) {
+            VirtualFolderType type = (VirtualFolderType) getIntent().getSerializableExtra(EXTRA_VIRTUAL_TYPE);
 
-        mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(),
-                parentFolder, getAccount(), getStorageManager(), MainApp.isOnlyOnDevice());
+            mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(),
+                    type, getAccount(), getStorageManager());
+        } else {
+            // get parent from path
+            String parentPath = getFile().getRemotePath().substring(0,
+                    getFile().getRemotePath().lastIndexOf(getFile().getFileName()));
+            OCFile parentFolder = getStorageManager().getFileByPath(parentPath);
+
+            if (parentFolder == null) {
+                // should not be necessary
+                parentFolder = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
+            }
+
+            mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(),
+                    parentFolder, getAccount(), getStorageManager(), MainApp.isOnlyOnDevice());
+        }
 
         mViewPager = (ExtendedViewPager) findViewById(R.id.fragmentPager);
-        int position = mHasSavedPosition ? mSavedPosition :
-                mPreviewImagePagerAdapter.getFilePosition(getFile());
+
+        int position = mHasSavedPosition ? mSavedPosition : mPreviewImagePagerAdapter.getFilePosition(getFile());
         position = (position >= 0) ? position : 0;
-        mViewPager.setAdapter(mPreviewImagePagerAdapter); 
+
+        mViewPager.setAdapter(mPreviewImagePagerAdapter);
         mViewPager.setOnPageChangeListener(this);
         mViewPager.setCurrentItem(position);
+
         if (position == 0 && !getFile().isDown()) {
             // this is necessary because mViewPager.setCurrentItem(0) just after setting the
             // adapter does not result in a call to #onPageSelected(0)
@@ -446,7 +460,7 @@ public class PreviewImageActivity extends FileActivity implements
         super.onAccountSet(stateWasRecovered);
         if (getAccount() != null) {
             OCFile file = getFile();
-            /// Validate handled file  (first image to preview)
+            /// Validate handled file (first image to preview)
             if (file == null) {
                 throw new IllegalStateException("Instanced with a NULL OCFile");
             }

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

@@ -27,6 +27,7 @@ import android.view.ViewGroup;
 
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.VirtualFolderType;
 import com.owncloud.android.ui.fragment.FileFragment;
 import com.owncloud.android.utils.FileStorageUtils;
 
@@ -87,7 +88,44 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
         //mFragmentManager = fragmentManager;
         mCachedFragments = new HashMap<Integer, FileFragment>();
     }
-    
+
+    /**
+     * Constructor.
+     *
+     * @param fragmentManager {@link FragmentManager} instance that will handle
+     *                        the {@link Fragment}s provided by the adapter.
+     * @param type            Type of virtual folder, e.g. favorite or photos
+     * @param storageManager  Bridge to database.
+     */
+    public PreviewImagePagerAdapter(FragmentManager fragmentManager, VirtualFolderType type,
+                                    Account account, FileDataStorageManager storageManager) {
+        super(fragmentManager);
+
+        if (fragmentManager == null) {
+            throw new IllegalArgumentException("NULL FragmentManager instance");
+        }
+        if (type == null) {
+            throw new IllegalArgumentException("NULL parent folder");
+        }
+        if (storageManager == null) {
+            throw new IllegalArgumentException("NULL storage manager");
+        }
+
+        mAccount = account;
+        mStorageManager = storageManager;
+        mImageFiles = mStorageManager.getVirtualFolderContent(type, true);
+
+        if (type == VirtualFolderType.PHOTOS) {
+            mImageFiles = FileStorageUtils.sortOcFolderDescDateModified(mImageFiles);
+        }
+
+        mObsoleteFragments = new HashSet<Object>();
+        mObsoletePositions = new HashSet<Integer>();
+        mDownloadErrors = new HashSet<Integer>();
+        //mFragmentManager = fragmentManager;
+        mCachedFragments = new HashMap<Integer, FileFragment>();
+    }
+
     /**
      * Returns the image files handled by the adapter.
      * 
@@ -195,7 +233,7 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
     public void resetZoom() {
         Iterator<FileFragment> entries = mCachedFragments.values().iterator();
         while (entries.hasNext()) {
-        FileFragment fileFragment = (FileFragment) entries.next();
+        FileFragment fileFragment = entries.next();
             if (fileFragment instanceof PreviewImageFragment) {
                 ((PreviewImageFragment) fileFragment).getImageView().resetZoom();
             }

+ 8 - 7
src/main/res/layout/empty_list.xml

@@ -18,8 +18,8 @@
   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/>.
 -->
-<LinearLayout android:id="@+id/empty_list_view"
-              xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/empty_list_view"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:layout_gravity="center"
@@ -34,14 +34,14 @@
         android:layout_height="72dp"
         android:contentDescription="@string/file_list_folder"
         android:src="@drawable/ic_list_empty_folder"
-        android:tint="@color/primary"/>
+        android:tint="@color/primary"
+        android:visibility="gone"/>
 
     <ProgressBar
         android:id="@+id/empty_list_progress"
         android:layout_width="72dp"
         android:layout_height="72dp"
-        android:layout_gravity="center_horizontal"
-        android:visibility="gone"/>
+        android:layout_gravity="center_horizontal"/>
 
     <TextView
         android:id="@+id/empty_list_view_headline"
@@ -53,7 +53,7 @@
         android:maxLines="2"
         android:paddingBottom="@dimen/standard_padding"
         android:paddingTop="@dimen/standard_padding"
-        android:text="@string/file_list_empty_headline"
+        android:text="@string/file_list_loading"
         android:textSize="26sp"/>
 
     <TextView
@@ -64,6 +64,7 @@
         android:ellipsize="end"
         android:gravity="center"
         android:maxLines="3"
-        android:text="@string/file_list_empty"/>
+        android:text="@string/file_list_empty"
+        android:visibility="gone"/>
 
 </LinearLayout>

+ 1 - 1
src/main/res/layout/list_fragment.xml

@@ -33,7 +33,7 @@
         android:layout_height="match_parent"
         android:footerDividersEnabled="false"
         android:visibility="visible" >
-        
+
         <com.owncloud.android.ui.ExtendedListView
             android:id="@+id/list_root"
             android:layout_width="match_parent"