Browse Source

Merge branch 'develop' into clean_up

David A. Velasco 11 years ago
parent
commit
4160e7e256
37 changed files with 1372 additions and 865 deletions
  1. 3 1
      README.md
  2. 4 2
      SETUP.md
  3. BIN
      libs/commons-httpclient-3.1.jar
  4. BIN
      libs/jackrabbit-webdav-2.2.5-jar-with-dependencies.jar
  5. BIN
      libs/jackrabbit-webdav-2.6.4.jar
  6. 1 1
      pom.xml
  7. 1 0
      res/values/strings.xml
  8. 5 6
      src/com/owncloud/android/Uploader.java
  9. 0 52
      src/com/owncloud/android/datamodel/DataStorageManager.java
  10. 339 322
      src/com/owncloud/android/datamodel/FileDataStorageManager.java
  11. 21 12
      src/com/owncloud/android/datamodel/OCFile.java
  12. 2 1
      src/com/owncloud/android/db/ProviderMeta.java
  13. 1 4
      src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java
  14. 0 1
      src/com/owncloud/android/files/OwnCloudFileObserver.java
  15. 5 5
      src/com/owncloud/android/files/services/FileDownloader.java
  16. 4 11
      src/com/owncloud/android/files/services/FileObserverService.java
  17. 3 3
      src/com/owncloud/android/files/services/FileUploader.java
  18. 3 3
      src/com/owncloud/android/operations/CreateFolderOperation.java
  19. 0 6
      src/com/owncloud/android/operations/RemoteOperation.java
  20. 4 8
      src/com/owncloud/android/operations/RemoveFileOperation.java
  21. 6 6
      src/com/owncloud/android/operations/RenameFileOperation.java
  22. 10 9
      src/com/owncloud/android/operations/SynchronizeFileOperation.java
  23. 319 127
      src/com/owncloud/android/operations/SynchronizeFolderOperation.java
  24. 257 34
      src/com/owncloud/android/providers/FileContentProvider.java
  25. 18 57
      src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java
  26. 95 46
      src/com/owncloud/android/syncadapter/FileSyncAdapter.java
  27. 9 5
      src/com/owncloud/android/syncadapter/FileSyncService.java
  28. 1 2
      src/com/owncloud/android/ui/activity/ConflictsResolveActivity.java
  29. 180 80
      src/com/owncloud/android/ui/activity/FileDisplayActivity.java
  30. 5 5
      src/com/owncloud/android/ui/adapter/FileListListAdapter.java
  31. 4 8
      src/com/owncloud/android/ui/fragment/FileDetailFragment.java
  32. 46 26
      src/com/owncloud/android/ui/fragment/OCFileListFragment.java
  33. 6 13
      src/com/owncloud/android/ui/preview/PreviewImageActivity.java
  34. 4 4
      src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java
  35. 1 2
      src/com/owncloud/android/ui/preview/PreviewVideoActivity.java
  36. 1 1
      src/eu/alefzero/webdav/WebdavClient.java
  37. 14 2
      src/eu/alefzero/webdav/WebdavEntry.java

+ 3 - 1
README.md

@@ -1,4 +1,6 @@
-This is the android client for [owncloud][0].
+This is the Android client for [ownCloud][0].
+
+The app performs file synchronization with an ownCloud server. Other ownCloud features may be added in the future, but they are not a priority right now.
 
 
 Make sure you read [SETUP.md][1] when you start working on this project.
 Make sure you read [SETUP.md][1] when you start working on this project.
 
 

+ 4 - 2
SETUP.md

@@ -1,5 +1,5 @@
   
   
-  If you want to start development of ownCloud first download required files, then compile using console or Eclipse, finally create pull request:
+  If you want to start help developing ownCloud please follow the [contribution guidlines][0] and observe these instructions:
   
   
   1. Fork and download android/develop repository:
   1. Fork and download android/develop repository:
 
 
@@ -42,6 +42,7 @@
 
 
   4. Create pull request:
   4. Create pull request:
   
   
+  -  NOTE: You must sign the [Contributor Agreement][1] before your changes can be accepted!
   -  Commit your changes locally: "git commit -a"
   -  Commit your changes locally: "git commit -a"
   -  Push your changes to your Github repo: "git push"
   -  Push your changes to your Github repo: "git push"
   -  Browse to https://github.com/YOURGITHUBNAME/android/pulls and issue pull request
   -  Browse to https://github.com/YOURGITHUBNAME/android/pulls and issue pull request
@@ -50,5 +51,6 @@
   -  Enter description and send pull request.
   -  Enter description and send pull request.
 
 
 
 
-
+[0]: https://github.com/owncloud/android/blob/master/CONTRIBUTING.md
+[1]: http://owncloud.org/about/contributor-agreement/
 
 

BIN
libs/commons-httpclient-3.1.jar


BIN
libs/jackrabbit-webdav-2.2.5-jar-with-dependencies.jar


BIN
libs/jackrabbit-webdav-2.6.4.jar


+ 1 - 1
pom.xml

@@ -49,7 +49,7 @@
         <dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>jackrabbit-webdav</artifactId>
             <artifactId>jackrabbit-webdav</artifactId>
-            <version>2.5.2</version>
+            <version>2.6.4</version>
         </dependency>
         </dependency>
 
 
     </dependencies>
     </dependencies>

+ 1 - 0
res/values/strings.xml

@@ -99,6 +99,7 @@
     <string name="sync_foreign_files_forgotten_ticker">Some local files were forgotten</string>
     <string name="sync_foreign_files_forgotten_ticker">Some local files were forgotten</string>
     <string name="sync_foreign_files_forgotten_content">%1$d files out of the %2$s directory could not be copied into</string>
     <string name="sync_foreign_files_forgotten_content">%1$d files out of the %2$s directory could not be copied into</string>
     <string name="sync_foreign_files_forgotten_explanation">As of version 1.3.16, files uploaded from this device are copied into the local %1$s folder to prevent data loss when a single file is synced with multiple accounts.\n\nDue to this change, all files uploaded in previous versions of this app were copied into the %2$s folder. However, an error prevented the completion of this operation during account synchronization. You may either leave the file(s) as is and remove the link to %3$s, or move the file(s) into the %1$s directory and retain the link to %4$s.\n\nListed below are the local file(s), and the the remote file(s) in %5$s they were linked to.</string>
     <string name="sync_foreign_files_forgotten_explanation">As of version 1.3.16, files uploaded from this device are copied into the local %1$s folder to prevent data loss when a single file is synced with multiple accounts.\n\nDue to this change, all files uploaded in previous versions of this app were copied into the %2$s folder. However, an error prevented the completion of this operation during account synchronization. You may either leave the file(s) as is and remove the link to %3$s, or move the file(s) into the %1$s directory and retain the link to %4$s.\n\nListed below are the local file(s), and the the remote file(s) in %5$s they were linked to.</string>
+	<string name="sync_current_folder_was_removed">Folder %1$s does not exist anymore</string>    
     <string name="foreign_files_move">"Move all"</string>
     <string name="foreign_files_move">"Move all"</string>
     <string name="foreign_files_success">"All files were moved"</string>
     <string name="foreign_files_success">"All files were moved"</string>
     <string name="foreign_files_fail">"Some files could not be moved"</string>
     <string name="foreign_files_fail">"Some files could not be moved"</string>

+ 5 - 6
src/com/owncloud/android/Uploader.java

@@ -55,7 +55,6 @@ import android.widget.SimpleAdapter;
 import android.widget.Toast;
 import android.widget.Toast;
 
 
 import com.owncloud.android.authentication.AccountAuthenticator;
 import com.owncloud.android.authentication.AccountAuthenticator;
-import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.files.services.FileUploader;
@@ -77,7 +76,7 @@ public class Uploader extends ListActivity implements OnItemClickListener, andro
     private ArrayList<Parcelable> mStreamsToUpload;
     private ArrayList<Parcelable> mStreamsToUpload;
     private boolean mCreateDir;
     private boolean mCreateDir;
     private String mUploadPath;
     private String mUploadPath;
-    private DataStorageManager mStorageManager;
+    private FileDataStorageManager mStorageManager;
     private OCFile mFile;
     private OCFile mFile;
 
 
     private final static int DIALOG_NO_ACCOUNT = 0;
     private final static int DIALOG_NO_ACCOUNT = 0;
@@ -230,12 +229,12 @@ public class Uploader extends ListActivity implements OnItemClickListener, andro
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         // click on folder in the list
         // click on folder in the list
         Log_OC.d(TAG, "on item click");
         Log_OC.d(TAG, "on item click");
-        Vector<OCFile> tmpfiles = mStorageManager.getDirectoryContent(mFile);
+        Vector<OCFile> tmpfiles = mStorageManager.getFolderContent(mFile);
         if (tmpfiles.size() <= 0) return;
         if (tmpfiles.size() <= 0) return;
         // filter on dirtype
         // filter on dirtype
         Vector<OCFile> files = new Vector<OCFile>();
         Vector<OCFile> files = new Vector<OCFile>();
         for (OCFile f : tmpfiles)
         for (OCFile f : tmpfiles)
-            if (f.isDirectory())
+            if (f.isFolder())
                 files.add(f);
                 files.add(f);
         if (files.size() < position) {
         if (files.size() < position) {
             throw new IndexOutOfBoundsException("Incorrect item selected");
             throw new IndexOutOfBoundsException("Incorrect item selected");
@@ -295,11 +294,11 @@ public class Uploader extends ListActivity implements OnItemClickListener, andro
         
         
         mFile = mStorageManager.getFileByPath(full_path);
         mFile = mStorageManager.getFileByPath(full_path);
         if (mFile != null) {
         if (mFile != null) {
-            Vector<OCFile> files = mStorageManager.getDirectoryContent(mFile);
+            Vector<OCFile> files = mStorageManager.getFolderContent(mFile);
             List<HashMap<String, Object>> data = new LinkedList<HashMap<String,Object>>();
             List<HashMap<String, Object>> data = new LinkedList<HashMap<String,Object>>();
             for (OCFile f : files) {
             for (OCFile f : files) {
                 HashMap<String, Object> h = new HashMap<String, Object>();
                 HashMap<String, Object> h = new HashMap<String, Object>();
-                if (f.isDirectory()) {
+                if (f.isFolder()) {
                     h.put("dirname", f.getFileName());
                     h.put("dirname", f.getFileName());
                     data.add(h);
                     data.add(h);
                 }
                 }

+ 0 - 52
src/com/owncloud/android/datamodel/DataStorageManager.java

@@ -1,52 +0,0 @@
-/* ownCloud Android client application
- *   Copyright (C) 2012  Bartek Przybylski
- *   Copyright (C) 2012-2013 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.datamodel;
-
-import java.util.List;
-import java.util.Vector;
-
-public interface DataStorageManager {
-
-    public static final int ROOT_PARENT_ID = 0;
-    
-    public OCFile getFileByPath(String path);
-
-    public OCFile getFileById(long id);
-
-    public boolean fileExists(String path);
-
-    public boolean fileExists(long id);
-
-    public boolean saveFile(OCFile file);
-
-    public void saveFiles(List<OCFile> files);
-
-    public Vector<OCFile> getDirectoryContent(OCFile f);
-    
-    public void removeFile(OCFile file, boolean removeLocalCopy);
-    
-    public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent);
-
-    public void moveDirectory(OCFile dir, String newPath);
-
-    public Vector<OCFile> getDirectoryImages(OCFile mParentFolder);
-    
-    public void calculateFolderSize(long id);
-    
-}

+ 339 - 322
src/com/owncloud/android/datamodel/FileDataStorageManager.java

@@ -20,9 +20,9 @@ package com.owncloud.android.datamodel;
 
 
 import java.io.File;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Vector;
 import java.util.Vector;
 
 
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.Log_OC;
@@ -35,33 +35,62 @@ import android.content.ContentProviderClient;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
 import android.content.ContentProviderResult;
 import android.content.ContentResolver;
 import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.ContentValues;
 import android.content.OperationApplicationException;
 import android.content.OperationApplicationException;
 import android.database.Cursor;
 import android.database.Cursor;
 import android.net.Uri;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.os.RemoteException;
 
 
-public class FileDataStorageManager implements DataStorageManager {
+public class FileDataStorageManager {
+
+    public static final int ROOT_PARENT_ID = 0;
 
 
     private ContentResolver mContentResolver;
     private ContentResolver mContentResolver;
-    private ContentProviderClient mContentProvider;
+    private ContentProviderClient mContentProviderClient;
     private Account mAccount;
     private Account mAccount;
 
 
-    private static String TAG = "FileDataStorageManager";
+    private static String TAG = FileDataStorageManager.class.getSimpleName();
 
 
+    
     public FileDataStorageManager(Account account, ContentResolver cr) {
     public FileDataStorageManager(Account account, ContentResolver cr) {
-        mContentProvider = null;
+        mContentProviderClient = null;
         mContentResolver = cr;
         mContentResolver = cr;
         mAccount = account;
         mAccount = account;
     }
     }
 
 
     public FileDataStorageManager(Account account, ContentProviderClient cp) {
     public FileDataStorageManager(Account account, ContentProviderClient cp) {
-        mContentProvider = cp;
+        mContentProviderClient = cp;
         mContentResolver = null;
         mContentResolver = null;
         mAccount = account;
         mAccount = account;
     }
     }
 
 
-    @Override
+    
+    public void setAccount(Account account) {
+        mAccount = account;
+    }
+
+    public Account getAccount() {
+        return mAccount;
+    }
+
+    public void setContentResolver(ContentResolver cr) {
+        mContentResolver = cr;
+    }
+
+    public ContentResolver getContentResolver() {
+        return mContentResolver;
+    }
+
+    public void setContentProviderClient(ContentProviderClient cp) {
+        mContentProviderClient = cp;
+    }
+
+    public ContentProviderClient getContentProviderClient() {
+        return mContentProviderClient;
+    }
+    
+
     public OCFile getFileByPath(String path) {
     public OCFile getFileByPath(String path) {
         Cursor c = getCursorForValue(ProviderTableMeta.FILE_PATH, path);
         Cursor c = getCursorForValue(ProviderTableMeta.FILE_PATH, path);
         OCFile file = null;
         OCFile file = null;
@@ -69,22 +98,13 @@ public class FileDataStorageManager implements DataStorageManager {
             file = createFileInstance(c);
             file = createFileInstance(c);
         }
         }
         c.close();
         c.close();
-        if (file == null && OCFile.PATH_SEPARATOR.equals(path)) {
+        if (file == null && OCFile.ROOT_PATH.equals(path)) {
             return createRootDir(); // root should always exist
             return createRootDir(); // root should always exist
         }
         }
         return file;
         return file;
     }
     }
 
 
 
 
-    private OCFile createRootDir() {
-        OCFile file = new OCFile(OCFile.PATH_SEPARATOR);
-        file.setMimetype("DIR");
-        file.setParentId(DataStorageManager.ROOT_PARENT_ID);
-        saveFile(file);
-        return file;
-    }
-
-    @Override
     public OCFile getFileById(long id) {
     public OCFile getFileById(long id) {
         Cursor c = getCursorForValue(ProviderTableMeta._ID, String.valueOf(id));
         Cursor c = getCursorForValue(ProviderTableMeta._ID, String.valueOf(id));
         OCFile file = null;
         OCFile file = null;
@@ -105,17 +125,42 @@ public class FileDataStorageManager implements DataStorageManager {
         return file;
         return file;
     }
     }
 
 
-    @Override
     public boolean fileExists(long id) {
     public boolean fileExists(long id) {
         return fileExists(ProviderTableMeta._ID, String.valueOf(id));
         return fileExists(ProviderTableMeta._ID, String.valueOf(id));
     }
     }
 
 
-    @Override
     public boolean fileExists(String path) {
     public boolean fileExists(String path) {
         return fileExists(ProviderTableMeta.FILE_PATH, path);
         return fileExists(ProviderTableMeta.FILE_PATH, path);
     }
     }
 
 
-    @Override
+    
+    public Vector<OCFile> getFolderContent(OCFile f) {
+        if (f != null && f.isFolder() && f.getFileId() != -1) {
+            return getFolderContent(f.getFileId());
+
+        } else {
+            return new Vector<OCFile>();
+        }
+    }
+    
+    
+    public Vector<OCFile> getFolderImages(OCFile folder) {
+        Vector<OCFile> ret = new Vector<OCFile>(); 
+        if (folder != null) {
+            // TODO better implementation, filtering in the access to database (if possible) instead of here 
+            Vector<OCFile> tmp = getFolderContent(folder);
+            OCFile current = null; 
+            for (int i=0; i<tmp.size(); i++) {
+                current = tmp.get(i);
+                if (current.isImage()) {
+                    ret.add(current);
+                }
+            }
+        }
+        return ret;
+    }
+
+    
     public boolean saveFile(OCFile file) {
     public boolean saveFile(OCFile file) {
         boolean overriden = false;
         boolean overriden = false;
         ContentValues cv = new ContentValues();
         ContentValues cv = new ContentValues();
@@ -125,18 +170,18 @@ public class FileDataStorageManager implements DataStorageManager {
         cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
         cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
         cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
         cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
         cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
         cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
-        if (file.getParentId() != 0)
+        //if (file.getParentId() != DataStorageManager.ROOT_PARENT_ID)
             cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
             cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
         cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
         cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
-        if (!file.isDirectory())
+        if (!file.isFolder())
             cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
             cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
         cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
         cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
         cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
+        cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
 
 
         boolean sameRemotePath = fileExists(file.getRemotePath());
         boolean sameRemotePath = fileExists(file.getRemotePath());
-        boolean changesSizeOfAncestors = false;
         if (sameRemotePath ||
         if (sameRemotePath ||
                 fileExists(file.getFileId())        ) {           // for renamed files; no more delete and create
                 fileExists(file.getFileId())        ) {           // for renamed files; no more delete and create
 
 
@@ -147,7 +192,6 @@ public class FileDataStorageManager implements DataStorageManager {
             } else {
             } else {
                 oldFile = getFileById(file.getFileId());
                 oldFile = getFileById(file.getFileId());
             }
             }
-            changesSizeOfAncestors = (oldFile.getFileLength() != file.getFileLength());
 
 
             overriden = true;
             overriden = true;
             if (getContentResolver() != null) {
             if (getContentResolver() != null) {
@@ -156,7 +200,7 @@ public class FileDataStorageManager implements DataStorageManager {
                         new String[] { String.valueOf(file.getFileId()) });
                         new String[] { String.valueOf(file.getFileId()) });
             } else {
             } else {
                 try {
                 try {
-                    getContentProvider().update(ProviderTableMeta.CONTENT_URI,
+                    getContentProviderClient().update(ProviderTableMeta.CONTENT_URI,
                             cv, ProviderTableMeta._ID + "=?",
                             cv, ProviderTableMeta._ID + "=?",
                             new String[] { String.valueOf(file.getFileId()) });
                             new String[] { String.valueOf(file.getFileId()) });
                 } catch (RemoteException e) {
                 } catch (RemoteException e) {
@@ -166,14 +210,13 @@ public class FileDataStorageManager implements DataStorageManager {
                 }
                 }
             }
             }
         } else {
         } else {
-            changesSizeOfAncestors = true;
             Uri result_uri = null;
             Uri result_uri = null;
             if (getContentResolver() != null) {
             if (getContentResolver() != null) {
                 result_uri = getContentResolver().insert(
                 result_uri = getContentResolver().insert(
                         ProviderTableMeta.CONTENT_URI_FILE, cv);
                         ProviderTableMeta.CONTENT_URI_FILE, cv);
             } else {
             } else {
                 try {
                 try {
-                    result_uri = getContentProvider().insert(
+                    result_uri = getContentProviderClient().insert(
                             ProviderTableMeta.CONTENT_URI_FILE, cv);
                             ProviderTableMeta.CONTENT_URI_FILE, cv);
                 } catch (RemoteException e) {
                 } catch (RemoteException e) {
                     Log_OC.e(TAG,
                     Log_OC.e(TAG,
@@ -188,32 +231,34 @@ public class FileDataStorageManager implements DataStorageManager {
             }            
             }            
         }
         }
 
 
-        if (file.isDirectory()) {
-            calculateFolderSize(file.getFileId());
-            if (file.needsUpdatingWhileSaving()) {
-                for (OCFile f : getDirectoryContent(file))
-                    saveFile(f);
-            }
-        }
-        
-        if (changesSizeOfAncestors || file.isDirectory()) {
-            updateSizesToTheRoot(file.getParentId());
+        if (file.isFolder()) {
+            updateFolderSize(file.getFileId());
+        } else {
+            updateFolderSize(file.getParentId());
         }
         }
         
         
         return overriden;
         return overriden;
     }
     }
 
 
 
 
-    @Override
-    public void saveFiles(List<OCFile> files) {
+    /**
+     * Inserts or updates the list of files contained in a given folder.
+     * 
+     * CALLER IS THE RESPONSIBLE FOR GRANTING RIGHT UPDATE OF INFORMATION, NOT THIS METHOD.
+     * HERE ONLY DATA CONSISTENCY SHOULD BE GRANTED
+     *  
+     * @param folder
+     * @param files
+     * @param removeNotUpdated
+     */
+    public void saveFolder(OCFile folder, Collection<OCFile> updatedFiles, Collection<OCFile> filesToRemove) {
+        
+        Log_OC.d(TAG,  "Saving folder " + folder.getRemotePath() + " with " + updatedFiles.size() + " children and " + filesToRemove.size() + " files to remove");
 
 
-        Iterator<OCFile> filesIt = files.iterator();
-        ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(files.size());
-        OCFile file = null;
+        ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(updatedFiles.size());
 
 
-        // prepare operations to perform
-        while (filesIt.hasNext()) {
-            file = filesIt.next();
+        // prepare operations to insert or update files to save in the given folder
+        for (OCFile file : updatedFiles) {
             ContentValues cv = new ContentValues();
             ContentValues cv = new ContentValues();
             cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
             cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
             cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData());
             cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData());
@@ -221,43 +266,21 @@ public class FileDataStorageManager implements DataStorageManager {
             cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
             cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
             cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
             cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
             cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
             cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
-            if (file.getParentId() != 0)
-                cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
+            //cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
+            cv.put(ProviderTableMeta.FILE_PARENT, folder.getFileId());
             cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
             cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
-            if (!file.isDirectory())
+            if (!file.isFolder()) {
                 cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
                 cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
+            }
             cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
             cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
             cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
             cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
             cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
             cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
             cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
             cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
+            cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
 
 
-            if (fileExists(file.getRemotePath())) {
-                OCFile oldFile = getFileByPath(file.getRemotePath());
-                file.setFileId(oldFile.getFileId());
-               
-                if (file.isDirectory()) {
-                    cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, oldFile.getFileLength());
-                    file.setFileLength(oldFile.getFileLength());
-                }
-                
-                operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
-                        withValues(cv).
-                        withSelection(  ProviderTableMeta._ID + "=?", 
-                                new String[] { String.valueOf(file.getFileId()) })
-                                .build());
-
-            } else if (fileExists(file.getFileId())) {
-                OCFile oldFile = getFileById(file.getFileId());
-                if (file.getStoragePath() == null && oldFile.getStoragePath() != null)
-                    file.setStoragePath(oldFile.getStoragePath());
-                
-                if (!file.isDirectory())
-                    cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
-                else {
-                    cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, oldFile.getFileLength());
-                    file.setFileLength(oldFile.getFileLength());
-                }
-                
+            boolean existsByPath = fileExists(file.getRemotePath());
+            if (existsByPath || fileExists(file.getFileId())) {
+                // updating an existing file
                 operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
                 operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
                         withValues(cv).
                         withValues(cv).
                         withSelection(  ProviderTableMeta._ID + "=?", 
                         withSelection(  ProviderTableMeta._ID + "=?", 
@@ -265,81 +288,277 @@ public class FileDataStorageManager implements DataStorageManager {
                                 .build());
                                 .build());
 
 
             } else {
             } else {
+                // adding a new file
                 operations.add(ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).withValues(cv).build());
                 operations.add(ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).withValues(cv).build());
             }
             }
         }
         }
+        
+        // prepare operations to remove files in the given folder
+        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()};
+                //Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, "" + file.getFileId());
+                if (file.isFolder()) {
+                    operations.add(ContentProviderOperation
+                                    .newDelete(ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_DIR, file.getFileId())).withSelection(where, whereArgs)
+                                        .build());
+                    // TODO remove local folder
+                } else {
+                    operations.add(ContentProviderOperation
+                                    .newDelete(ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, file.getFileId())).withSelection(where, whereArgs)
+                                        .build());
+                    if (file.isDown()) {
+                        new File(file.getStoragePath()).delete();
+                        // TODO move the deletion of local contents after success of deletions
+                    }
+                }
+            }
+        }
+        
+        // update metadata of folder
+        ContentValues cv = new ContentValues();
+        cv.put(ProviderTableMeta.FILE_MODIFIED, folder.getModificationTimestamp());
+        cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, folder.getModificationTimestampAtLastSyncForData());
+        cv.put(ProviderTableMeta.FILE_CREATION, folder.getCreationTimestamp());
+        cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, 0);   // FileContentProvider calculates the right size
+        cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, folder.getMimetype());
+        cv.put(ProviderTableMeta.FILE_NAME, folder.getFileName());
+        cv.put(ProviderTableMeta.FILE_PARENT, folder.getParentId());
+        cv.put(ProviderTableMeta.FILE_PATH, folder.getRemotePath());
+        cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
+        cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, folder.getLastSyncDateForProperties());
+        cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, folder.getLastSyncDateForData());
+        cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, folder.keepInSync() ? 1 : 0);
+        cv.put(ProviderTableMeta.FILE_ETAG, folder.getEtag());
+        operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+                withValues(cv).
+                withSelection(  ProviderTableMeta._ID + "=?", 
+                        new String[] { String.valueOf(folder.getFileId()) })
+                        .build());
 
 
         // apply operations in batch
         // apply operations in batch
         ContentProviderResult[] results = null;
         ContentProviderResult[] results = null;
+        Log_OC.d(TAG, "Sending " + operations.size() + " operations to FileContentProvider");
         try {
         try {
             if (getContentResolver() != null) {
             if (getContentResolver() != null) {
                 results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
                 results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
 
 
             } else {
             } else {
-                results = getContentProvider().applyBatch(operations);
+                results = getContentProviderClient().applyBatch(operations);
             }
             }
 
 
         } catch (OperationApplicationException e) {
         } catch (OperationApplicationException e) {
-            Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
+            Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
 
 
         } catch (RemoteException e) {
         } catch (RemoteException e) {
-            Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
+            Log_OC.e(TAG, "Exception in batch of operations  " + e.getMessage());
         }
         }
 
 
         // update new id in file objects for insertions
         // update new id in file objects for insertions
         if (results != null) {
         if (results != null) {
             long newId;
             long newId;
+            Iterator<OCFile> filesIt = updatedFiles.iterator();
+            OCFile file = null;
             for (int i=0; i<results.length; i++) {
             for (int i=0; i<results.length; i++) {
+                if (filesIt.hasNext()) {
+                    file = filesIt.next();
+                } else {
+                    file = null;
+                }
                 if (results[i].uri != null) {
                 if (results[i].uri != null) {
                     newId = Long.parseLong(results[i].uri.getPathSegments().get(1));
                     newId = Long.parseLong(results[i].uri.getPathSegments().get(1));
-                    files.get(i).setFileId(newId);
-                    //Log_OC.v(TAG, "Found and added id in insertion for " + files.get(i).getRemotePath());
+                    //updatedFiles.get(i).setFileId(newId);
+                    if (file != null) {
+                        file.setFileId(newId);
+                    }
                 }
                 }
             }
             }
         }
         }
+        
+        updateFolderSize(folder.getFileId());
+        
+    }
 
 
-        for (OCFile aFile : files) {
-            if (aFile.isDirectory() && aFile.needsUpdatingWhileSaving())
-                saveFiles(getDirectoryContent(aFile));
-        }
 
 
+    /**
+     * 
+     * @param id
+     */
+    private void updateFolderSize(long id) {
+        if (id > FileDataStorageManager.ROOT_PARENT_ID) {
+            Log_OC.d(TAG, "Updating size of " + id);
+            if (getContentResolver() != null) {
+                getContentResolver().update(ProviderTableMeta.CONTENT_URI_DIR, null,
+                        ProviderTableMeta._ID + "=?",
+                        new String[] { String.valueOf(id) });
+            } else {
+                try {
+                    getContentProviderClient().update(ProviderTableMeta.CONTENT_URI_DIR, null,
+                        ProviderTableMeta._ID + "=?",
+                        new String[] { String.valueOf(id) });
+                    
+                } catch (RemoteException e) {
+                    Log_OC.e(TAG, "Exception in update of folder size through compatibility patch " + e.getMessage());
+                }
+            }
+        } else {
+            Log_OC.e(TAG,  "not updating size for folder " + id);
+        }
     }
     }
+    
 
 
-    public void setAccount(Account account) {
-        mAccount = account;
+    public void removeFile(OCFile file, boolean removeDBData, boolean removeLocalCopy) {
+        if (file != null) {
+            if (file.isFolder()) {
+                removeFolder(file, removeDBData, removeLocalCopy);
+                
+            } else {
+                if (removeDBData) {
+                    //Uri file_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, ""+file.getFileId());
+                    Uri file_uri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, file.getFileId());
+                    String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?" + " AND " + ProviderTableMeta.FILE_PATH + "=?";
+                    String [] whereArgs = new String[]{mAccount.name, file.getRemotePath()};
+                    if (getContentProviderClient() != null) {
+                        try {
+                            getContentProviderClient().delete(file_uri, where, whereArgs);
+                        } catch (RemoteException e) {
+                            e.printStackTrace();
+                        }
+                    } else {
+                        getContentResolver().delete(file_uri, where, whereArgs);
+                    }
+                    updateFolderSize(file.getParentId());
+                }
+                if (removeLocalCopy && file.isDown()) {
+                    boolean success = new File(file.getStoragePath()).delete();
+                    if (!removeDBData && success) {
+                        // maybe unnecessary, but should be checked TODO remove if unnecessary
+                        file.setStoragePath(null);
+                        saveFile(file);
+                    }
+                }
+            }
+        }
     }
     }
+    
 
 
-    public Account getAccount() {
-        return mAccount;
+    public void removeFolder(OCFile folder, boolean removeDBData, boolean removeLocalContent) {
+        if (folder != null && folder.isFolder()) {
+            if (removeDBData &&  folder.getFileId() != -1) {
+                removeFolderInDb(folder);
+            }
+            if (removeLocalContent) {
+                File localFolder = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, folder));
+                removeLocalFolder(localFolder);
+            }
+        }
     }
     }
 
 
-    public void setContentResolver(ContentResolver cr) {
-        mContentResolver = cr;
+    private void removeFolderInDb(OCFile folder) {
+        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()};
+        if (getContentProviderClient() != null) {
+            try {
+                getContentProviderClient().delete(folder_uri, where, whereArgs);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+        } else {
+            getContentResolver().delete(folder_uri, where, whereArgs); 
+        }
+        updateFolderSize(folder.getParentId());
     }
     }
 
 
-    public ContentResolver getContentResolver() {
-        return mContentResolver;
+    private void removeLocalFolder(File folder) {
+        if (folder.exists()) {
+            File[] files = folder.listFiles();
+            if (files != null) {
+                for (File file : files) {
+                    if (file.isDirectory()) {
+                        removeLocalFolder(file);
+                    } else {
+                        file.delete();
+                    }
+                }
+            }
+            folder.delete();
+        }
     }
     }
 
 
-    public void setContentProvider(ContentProviderClient cp) {
-        mContentProvider = cp;
-    }
+    /**
+     * Updates database for a folder that was moved to a different location.
+     * 
+     * TODO explore better (faster) implementations
+     * TODO throw exceptions up !
+     */
+    public void moveFolder(OCFile folder, String newPath) {
+        // TODO check newPath
 
 
-    public ContentProviderClient getContentProvider() {
-        return mContentProvider;
-    }
-    
-    @Override
-    public Vector<OCFile> getDirectoryContent(OCFile f) {
-        if (f != null && f.isDirectory() && f.getFileId() != -1) {
-            return getDirectoryContent(f.getFileId());
+        if (folder != null && folder.isFolder() && folder.fileExists() && !OCFile.ROOT_PATH.equals(folder.getFileName())) {
+            /// 1. get all the descendants of 'dir' in a single QUERY (including 'dir')
+            Cursor c = null;
+            if (getContentProviderClient() != null) {
+                try {
+                    c = getContentProviderClient().query(ProviderTableMeta.CONTENT_URI, 
+                            null,
+                            ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
+                            new String[] { mAccount.name, folder.getRemotePath() + "%"  }, ProviderTableMeta.FILE_PATH + " ASC ");
+                } catch (RemoteException e) {
+                    Log_OC.e(TAG, e.getMessage());
+                }
+            } else {
+                c = getContentResolver().query(ProviderTableMeta.CONTENT_URI, 
+                        null,
+                        ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
+                        new String[] { mAccount.name, folder.getRemotePath() + "%"  }, ProviderTableMeta.FILE_PATH + " ASC ");
+            }
+
+            /// 2. prepare a batch of update operations to change all the descendants
+            ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(c.getCount());
+            int lengthOfOldPath = folder.getRemotePath().length();
+            String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name);
+            int lengthOfOldStoragePath = defaultSavePath.length() + lengthOfOldPath;
+            if (c.moveToFirst()) {
+                do {
+                    ContentValues cv = new ContentValues(); // don't take the constructor out of the loop and clear the object
+                    OCFile child = createFileInstance(c);
+                    cv.put(ProviderTableMeta.FILE_PATH, newPath + child.getRemotePath().substring(lengthOfOldPath));
+                    if (child.getStoragePath() != null && child.getStoragePath().startsWith(defaultSavePath)) {
+                        cv.put(ProviderTableMeta.FILE_STORAGE_PATH, defaultSavePath + newPath + child.getStoragePath().substring(lengthOfOldStoragePath));
+                    }
+                    operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+                            withValues(cv).
+                            withSelection(  ProviderTableMeta._ID + "=?", 
+                                    new String[] { String.valueOf(child.getFileId()) })
+                                    .build());
+                } while (c.moveToNext());
+            }
+            c.close();
+
+            /// 3. apply updates in batch
+            try {
+                if (getContentResolver() != null) {
+                    getContentResolver().applyBatch(MainApp.getAuthority(), operations);
+
+                } else {
+                    getContentProviderClient().applyBatch(operations);
+                }
+
+            } catch (OperationApplicationException e) {
+                Log_OC.e(TAG, "Fail to update descendants of " + folder.getFileId() + " in database", e);
+
+            } catch (RemoteException e) {
+                Log_OC.e(TAG, "Fail to update desendants of " + folder.getFileId() + " in database", e);
+            }
 
 
-        } else {
-            return new Vector<OCFile>();
         }
         }
     }
     }
 
 
-    private Vector<OCFile> getDirectoryContent(long parentId) {
+    
+    private Vector<OCFile> getFolderContent(long parentId) {
 
 
         Vector<OCFile> ret = new Vector<OCFile>();
         Vector<OCFile> ret = new Vector<OCFile>();
 
 
@@ -348,9 +567,9 @@ public class FileDataStorageManager implements DataStorageManager {
                 String.valueOf(parentId));
                 String.valueOf(parentId));
         Cursor c = null;
         Cursor c = null;
 
 
-        if (getContentProvider() != null) {
+        if (getContentProviderClient() != null) {
             try {
             try {
-                c = getContentProvider().query(req_uri, null, 
+                c = getContentProviderClient().query(req_uri, null, 
                         ProviderTableMeta.FILE_PARENT + "=?" ,
                         ProviderTableMeta.FILE_PARENT + "=?" ,
                         new String[] { String.valueOf(parentId)}, null);
                         new String[] { String.valueOf(parentId)}, null);
             } catch (RemoteException e) {
             } catch (RemoteException e) {
@@ -378,6 +597,13 @@ public class FileDataStorageManager implements DataStorageManager {
     }
     }
     
     
     
     
+    private OCFile createRootDir() {
+        OCFile file = new OCFile(OCFile.ROOT_PATH);
+        file.setMimetype("DIR");
+        file.setParentId(FileDataStorageManager.ROOT_PARENT_ID);
+        saveFile(file);
+        return file;
+    }
 
 
     private boolean fileExists(String cmp_key, String value) {
     private boolean fileExists(String cmp_key, String value) {
         Cursor c;
         Cursor c;
@@ -391,7 +617,7 @@ public class FileDataStorageManager implements DataStorageManager {
                                     new String[] { value, mAccount.name }, null);
                                     new String[] { value, mAccount.name }, null);
         } else {
         } else {
             try {
             try {
-                c = getContentProvider().query(
+                c = getContentProviderClient().query(
                         ProviderTableMeta.CONTENT_URI,
                         ProviderTableMeta.CONTENT_URI,
                         null,
                         null,
                         cmp_key + "=? AND "
                         cmp_key + "=? AND "
@@ -421,7 +647,7 @@ public class FileDataStorageManager implements DataStorageManager {
                                     new String[] { value, mAccount.name }, null);
                                     new String[] { value, mAccount.name }, null);
         } else {
         } else {
             try {
             try {
-                c = getContentProvider().query(
+                c = getContentProviderClient().query(
                         ProviderTableMeta.CONTENT_URI,
                         ProviderTableMeta.CONTENT_URI,
                         null,
                         null,
                         key + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER
                         key + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER
@@ -445,7 +671,7 @@ public class FileDataStorageManager implements DataStorageManager {
                     .getColumnIndex(ProviderTableMeta.FILE_PARENT)));
                     .getColumnIndex(ProviderTableMeta.FILE_PARENT)));
             file.setMimetype(c.getString(c
             file.setMimetype(c.getString(c
                     .getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)));
                     .getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)));
-            if (!file.isDirectory()) {
+            if (!file.isFolder()) {
                 file.setStoragePath(c.getString(c
                 file.setStoragePath(c.getString(c
                         .getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)));
                         .getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)));
                 if (file.getStoragePath() == null) {
                 if (file.getStoragePath() == null) {
@@ -471,219 +697,10 @@ public class FileDataStorageManager implements DataStorageManager {
                     getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA)));
                     getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA)));
             file.setKeepInSync(c.getInt(
             file.setKeepInSync(c.getInt(
                     c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false);
                     c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false);
+            file.setEtag(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG)));
+                    
         }
         }
         return file;
         return file;
     }
     }
 
 
-    @Override
-    public void removeFile(OCFile file, boolean removeLocalCopy) {
-        Uri file_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, ""+file.getFileId());
-        if (getContentProvider() != null) {
-            try {
-                getContentProvider().delete(file_uri,
-                        ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
-                        new String[]{mAccount.name});
-            } catch (RemoteException e) {
-                e.printStackTrace();
-            }
-        } else {
-            getContentResolver().delete(file_uri,
-                    ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
-                    new String[]{mAccount.name});
-        }
-        if (file.isDown() && removeLocalCopy) {
-            new File(file.getStoragePath()).delete();
-        }
-        if (file.isDirectory() && removeLocalCopy) {
-            File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
-            if (f.exists() && f.isDirectory() && (f.list() == null || f.list().length == 0)) {
-                f.delete();
-            }
-        }
-        
-        if (file.getFileLength() > 0) {
-            updateSizesToTheRoot(file.getParentId());
-        }
-    }
-
-    @Override
-    public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent) {
-        // TODO consider possible failures
-        if (dir != null && dir.isDirectory() && dir.getFileId() != -1) {
-            Vector<OCFile> children = getDirectoryContent(dir);
-            if (children.size() > 0) {
-                OCFile child = null;
-                for (int i=0; i<children.size(); i++) {
-                    child = children.get(i);
-                    if (child.isDirectory()) {
-                        removeDirectory(child, removeDBData, removeLocalContent);
-                    } else {
-                        if (removeDBData) {
-                            removeFile(child, removeLocalContent);
-                        } else if (removeLocalContent) {
-                            if (child.isDown()) {
-                                new File(child.getStoragePath()).delete();
-                            }
-                        }
-                    }
-                }
-            }
-            if (removeDBData) {
-                removeFile(dir, true);
-            }
-            
-            if (dir.getFileLength() > 0) {
-                updateSizesToTheRoot(dir.getParentId());
-            }
-        }
-    }
-
-
-    /**
-     * Updates database for a folder that was moved to a different location.
-     * 
-     * TODO explore better (faster) implementations
-     * TODO throw exceptions up !
-     */
-    @Override
-    public void moveDirectory(OCFile dir, String newPath) {
-        // TODO check newPath
-
-        if (dir != null && dir.isDirectory() && dir.fileExists() && !dir.getFileName().equals(OCFile.PATH_SEPARATOR)) {
-            /// 1. get all the descendants of 'dir' in a single QUERY (including 'dir')
-            Cursor c = null;
-            if (getContentProvider() != null) {
-                try {
-                    c = getContentProvider().query(ProviderTableMeta.CONTENT_URI, 
-                            null,
-                            ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
-                            new String[] { mAccount.name, dir.getRemotePath() + "%"  }, ProviderTableMeta.FILE_PATH + " ASC ");
-                } catch (RemoteException e) {
-                    Log_OC.e(TAG, e.getMessage());
-                }
-            } else {
-                c = getContentResolver().query(ProviderTableMeta.CONTENT_URI, 
-                        null,
-                        ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
-                        new String[] { mAccount.name, dir.getRemotePath() + "%"  }, ProviderTableMeta.FILE_PATH + " ASC ");
-            }
-
-            /// 2. prepare a batch of update operations to change all the descendants
-            ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(c.getCount());
-            int lengthOfOldPath = dir.getRemotePath().length();
-            String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name);
-            int lengthOfOldStoragePath = defaultSavePath.length() + lengthOfOldPath;
-            if (c.moveToFirst()) {
-                do {
-                    ContentValues cv = new ContentValues(); // don't take the constructor out of the loop and clear the object
-                    OCFile child = createFileInstance(c);
-                    cv.put(ProviderTableMeta.FILE_PATH, newPath + child.getRemotePath().substring(lengthOfOldPath));
-                    if (child.getStoragePath() != null && child.getStoragePath().startsWith(defaultSavePath)) {
-                        cv.put(ProviderTableMeta.FILE_STORAGE_PATH, defaultSavePath + newPath + child.getStoragePath().substring(lengthOfOldStoragePath));
-                    }
-                    operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
-                            withValues(cv).
-                            withSelection(  ProviderTableMeta._ID + "=?", 
-                                    new String[] { String.valueOf(child.getFileId()) })
-                                    .build());
-                } while (c.moveToNext());
-            }
-            c.close();
-
-            /// 3. apply updates in batch
-            try {
-                if (getContentResolver() != null) {
-                    getContentResolver().applyBatch(MainApp.getAuthority(), operations);
-
-                } else {
-                    getContentProvider().applyBatch(operations);
-                }
-
-            } catch (OperationApplicationException e) {
-                Log_OC.e(TAG, "Fail to update descendants of " + dir.getFileId() + " in database", e);
-
-            } catch (RemoteException e) {
-                Log_OC.e(TAG, "Fail to update desendants of " + dir.getFileId() + " in database", e);
-            }
-
-        }
-    }
-
-    @Override
-    public Vector<OCFile> getDirectoryImages(OCFile directory) {
-        Vector<OCFile> ret = new Vector<OCFile>(); 
-        if (directory != null) {
-            // TODO better implementation, filtering in the access to database (if possible) instead of here 
-            Vector<OCFile> tmp = getDirectoryContent(directory);
-            OCFile current = null; 
-            for (int i=0; i<tmp.size(); i++) {
-                current = tmp.get(i);
-                if (current.isImage()) {
-                    ret.add(current);
-                }
-            }
-        }
-        return ret;
-    }
-
-    /**
-     * Calculate and save the folderSize on DB
-     * @param id
-     */
-    @Override
-    public void calculateFolderSize(long id) {
-        long folderSize = 0;
-        
-        Vector<OCFile> files = getDirectoryContent(id);
-        
-        for (OCFile f: files)
-        {
-            folderSize = folderSize + f.getFileLength();
-        }
-        
-        updateSize(id, folderSize);
-    }
-
-    /**
-     * Update the size value of an OCFile in DB
-     */
-    private int updateSize(long id, long size) {
-        ContentValues cv = new ContentValues();
-        cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, size);
-        int result = -1;
-        if (getContentResolver() != null) {
-             result = getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, ProviderTableMeta._ID + "=?", 
-                     new String[] { String.valueOf(id) });
-        } else {
-            try {
-                result = getContentProvider().update(ProviderTableMeta.CONTENT_URI, cv, ProviderTableMeta._ID + "=?", 
-                        new String[] { String.valueOf(id) });
-            } catch (RemoteException e) {
-                Log_OC.e(TAG,"Fail to update size column into database " + e.getMessage());
-            }
-        }
-        return result;
-    }
-
-    /** 
-     * Update the size of a subtree of folder from a file to the root
-     * @param parentId: parent of the file
-     */
-    private void updateSizesToTheRoot(long parentId) {
-        
-        OCFile file; 
-
-        while (parentId != 0) {
-            
-            // Update the size of the parent
-            calculateFolderSize(parentId);
-            
-            // search the next parent
-            file = getFileById(parentId);            
-            parentId = file.getParentId();
-            
-        }              
-        
-    }
-    
 }
 }

+ 21 - 12
src/com/owncloud/android/datamodel/OCFile.java

@@ -41,6 +41,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
     };
     };
 
 
     public static final String PATH_SEPARATOR = "/";
     public static final String PATH_SEPARATOR = "/";
+    public static final String ROOT_PATH = PATH_SEPARATOR;
 
 
     private static final String TAG = OCFile.class.getSimpleName();
     private static final String TAG = OCFile.class.getSimpleName();
     
     
@@ -60,6 +61,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
 
 
     private String mEtag;
     private String mEtag;
 
 
+
     /**
     /**
      * Create new {@link OCFile} with given path.
      * Create new {@link OCFile} with given path.
      * 
      * 
@@ -95,6 +97,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         mKeepInSync = source.readInt() == 1;
         mKeepInSync = source.readInt() == 1;
         mLastSyncDateForProperties = source.readLong();
         mLastSyncDateForProperties = source.readLong();
         mLastSyncDateForData = source.readLong();
         mLastSyncDateForData = source.readLong();
+        mEtag = source.readString();
     }
     }
 
 
     @Override
     @Override
@@ -112,6 +115,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         dest.writeInt(mKeepInSync ? 1 : 0);
         dest.writeInt(mKeepInSync ? 1 : 0);
         dest.writeLong(mLastSyncDateForProperties);
         dest.writeLong(mLastSyncDateForProperties);
         dest.writeLong(mLastSyncDateForData);
         dest.writeLong(mLastSyncDateForData);
+        dest.writeString(mEtag);
     }
     }
     
     
     /**
     /**
@@ -143,11 +147,11 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
     }
     }
 
 
     /**
     /**
-     * Use this to find out if this file is a Directory
+     * Use this to find out if this file is a folder.
      * 
      * 
-     * @return true if it is a directory
+     * @return true if it is a folder
      */
      */
-    public boolean isDirectory() {
+    public boolean isFolder() {
         return mMimeType != null && mMimeType.equals("DIR");
         return mMimeType != null && mMimeType.equals("DIR");
     }
     }
 
 
@@ -254,7 +258,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
      */
      */
     public String getFileName() {
     public String getFileName() {
         File f = new File(getRemotePath());
         File f = new File(getRemotePath());
-        return f.getName().length() == 0 ? PATH_SEPARATOR : f.getName();
+        return f.getName().length() == 0 ? ROOT_PATH : f.getName();
     }
     }
     
     
     /**
     /**
@@ -264,11 +268,11 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
      */
      */
     public void setFileName(String name) {
     public void setFileName(String name) {
         Log_OC.d(TAG, "OCFile name changin from " + mRemotePath);
         Log_OC.d(TAG, "OCFile name changin from " + mRemotePath);
-        if (name != null && name.length() > 0 && !name.contains(PATH_SEPARATOR) && !mRemotePath.equals(PATH_SEPARATOR)) {
+        if (name != null && name.length() > 0 && !name.contains(PATH_SEPARATOR) && !mRemotePath.equals(ROOT_PATH)) {
             String parent = (new File(getRemotePath())).getParent();
             String parent = (new File(getRemotePath())).getParent();
             parent = (parent.endsWith(PATH_SEPARATOR)) ? parent : parent + PATH_SEPARATOR;
             parent = (parent.endsWith(PATH_SEPARATOR)) ? parent : parent + PATH_SEPARATOR;
             mRemotePath =  parent + name;
             mRemotePath =  parent + name;
-            if (isDirectory()) {
+            if (isFolder()) {
                 mRemotePath += PATH_SEPARATOR;
                 mRemotePath += PATH_SEPARATOR;
             }
             }
             Log_OC.d(TAG, "OCFile name changed to " + mRemotePath);
             Log_OC.d(TAG, "OCFile name changed to " + mRemotePath);
@@ -293,7 +297,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
      *             not a directory
      *             not a directory
      */
      */
     public void addFile(OCFile file) throws IllegalStateException {
     public void addFile(OCFile file) throws IllegalStateException {
-        if (isDirectory()) {
+        if (isFolder()) {
             file.mParentId = mId;
             file.mParentId = mId;
             mNeedsUpdating = true;
             mNeedsUpdating = true;
             return;
             return;
@@ -319,6 +323,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         mLastSyncDateForData = 0;
         mLastSyncDateForData = 0;
         mKeepInSync = false;
         mKeepInSync = false;
         mNeedsUpdating = false;
         mNeedsUpdating = false;
+        mEtag = null;
     }
     }
 
 
     /**
     /**
@@ -415,11 +420,11 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
 
 
     @Override
     @Override
     public int compareTo(OCFile another) {
     public int compareTo(OCFile another) {
-        if (isDirectory() && another.isDirectory()) {
+        if (isFolder() && another.isFolder()) {
             return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
             return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
-        } else if (isDirectory()) {
+        } else if (isFolder()) {
             return -1;
             return -1;
-        } else if (another.isDirectory()) {
+        } else if (another.isFolder()) {
             return 1;
             return 1;
         }
         }
         return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
         return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
@@ -439,8 +444,8 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
 
 
     @Override
     @Override
     public String toString() {
     public String toString() {
-        String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, parentId=%s, keepInSinc=%s]";
-        asString = String.format(asString, Long.valueOf(mId), getFileName(), mMimeType, isDown(), mLocalPath, mRemotePath, Long.valueOf(mParentId), Boolean.valueOf(mKeepInSync));
+        String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, parentId=%s, keepInSinc=%s etag=%s]";
+        asString = String.format(asString, Long.valueOf(mId), getFileName(), mMimeType, isDown(), mLocalPath, mRemotePath, Long.valueOf(mParentId), Boolean.valueOf(mKeepInSync), mEtag);
         return asString;
         return asString;
     }
     }
 
 
@@ -448,6 +453,10 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         return mEtag;
         return mEtag;
     }
     }
 
 
+    public void setEtag(String etag) {
+        this.mEtag = etag;
+    }
+    
     public long getLocalModificationTimestamp() {
     public long getLocalModificationTimestamp() {
         if (mLocalPath != null && mLocalPath.length() > 0) {
         if (mLocalPath != null && mLocalPath.length() > 0) {
             File f = new File(mLocalPath);
             File f = new File(mLocalPath);

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

@@ -35,7 +35,7 @@ public class ProviderMeta {
         public static final String DB_FILE = "owncloud.db";
         public static final String DB_FILE = "owncloud.db";
     */
     */
     public static final String DB_NAME = "filelist";
     public static final String DB_NAME = "filelist";
-    public static final int DB_VERSION = 4;
+    public static final int DB_VERSION = 5;
 
 
     private ProviderMeta() {
     private ProviderMeta() {
     }
     }
@@ -65,6 +65,7 @@ public class ProviderMeta {
         public static final String FILE_LAST_SYNC_DATE = "last_sync_date";  // _for_properties, but let's keep it as it is
         public static final String FILE_LAST_SYNC_DATE = "last_sync_date";  // _for_properties, but let's keep it as it is
         public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data";
         public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data";
         public static final String FILE_KEEP_IN_SYNC = "keep_in_sync";
         public static final String FILE_KEEP_IN_SYNC = "keep_in_sync";
+        public static final String FILE_ETAG = "etag";
 
 
         public static final String DEFAULT_SORT_ORDER = FILE_NAME
         public static final String DEFAULT_SORT_ORDER = FILE_NAME
                 + " collate nocase asc";
                 + " collate nocase asc";

+ 1 - 4
src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java

@@ -52,9 +52,6 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
     @Override
     @Override
     public void onReceive(Context context, Intent intent) {
     public void onReceive(Context context, Intent intent) {
         Log_OC.d(TAG, "Received: " + intent.getAction());
         Log_OC.d(TAG, "Received: " + intent.getAction());
-        
-        FileUploader fileUploader = new FileUploader();
-        
         if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)) {
         if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)) {
             handleConnectivityAction(context, intent);
             handleConnectivityAction(context, intent);
         }else if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) {
         }else if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) {
@@ -63,7 +60,7 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
         } else if (intent.getAction().equals(NEW_PHOTO_ACTION)) {
         } else if (intent.getAction().equals(NEW_PHOTO_ACTION)) {
             handleNewPhotoAction(context, intent);
             handleNewPhotoAction(context, intent);
             Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_PICTURE");
             Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_PICTURE");
-        } else if (intent.getAction().equals(fileUploader.getUploadFinishMessage())) {
+        } else if (intent.getAction().equals(FileUploader.getUploadFinishMessage())) {
             handleUploadFinished(context, intent);
             handleUploadFinished(context, intent);
         } else {
         } else {
             Log_OC.e(TAG, "Incorrect intent sent: " + intent.getAction());
             Log_OC.e(TAG, "Incorrect intent sent: " + intent.getAction());

+ 0 - 1
src/com/owncloud/android/files/OwnCloudFileObserver.java

@@ -83,7 +83,6 @@ public class OwnCloudFileObserver extends FileObserver {
                                                                     storageManager, 
                                                                     storageManager, 
                                                                     mOCAccount, 
                                                                     mOCAccount, 
                                                                     true, 
                                                                     true, 
-                                                                    true, 
                                                                     mContext);
                                                                     mContext);
         RemoteOperationResult result = sfo.execute(mOCAccount, mContext);
         RemoteOperationResult result = sfo.execute(mOCAccount, mContext);
         if (result.getCode() == ResultCode.SYNC_CONFLICT) {
         if (result.getCode() == ResultCode.SYNC_CONFLICT) {

+ 5 - 5
src/com/owncloud/android/files/services/FileDownloader.java

@@ -92,12 +92,12 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     private int mLastPercent;
     private int mLastPercent;
     
     
     
     
-    public String getDownloadAddedMessage() {
-        return getClass().getName().toString() + DOWNLOAD_ADDED_MESSAGE;
+    public static String getDownloadAddedMessage() {
+        return FileDownloader.class.getName().toString() + DOWNLOAD_ADDED_MESSAGE;
     }
     }
     
     
-    public String getDownloadFinishMessage() {
-        return getClass().getName().toString() + DOWNLOAD_FINISH_MESSAGE;
+    public static String getDownloadFinishMessage() {
+        return FileDownloader.class.getName().toString() + DOWNLOAD_FINISH_MESSAGE;
     }
     }
     
     
     /**
     /**
@@ -239,7 +239,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
             if (account == null || file == null) return false;
             if (account == null || file == null) return false;
             String targetKey = buildRemoteName(account, file);
             String targetKey = buildRemoteName(account, file);
             synchronized (mPendingDownloads) {
             synchronized (mPendingDownloads) {
-                if (file.isDirectory()) {
+                if (file.isFolder()) {
                     // this can be slow if there are many downloads :(
                     // this can be slow if there are many downloads :(
                     Iterator<String> it = mPendingDownloads.keySet().iterator();
                     Iterator<String> it = mPendingDownloads.keySet().iterator();
                     boolean found = false;
                     boolean found = false;

+ 4 - 11
src/com/owncloud/android/files/services/FileObserverService.java

@@ -57,9 +57,6 @@ public class FileObserverService extends Service {
     private static DownloadCompletedReceiverBis mDownloadReceiver;
     private static DownloadCompletedReceiverBis mDownloadReceiver;
     private IBinder mBinder = new LocalBinder();
     private IBinder mBinder = new LocalBinder();
     
     
-    private String mDownloadAddedMessage;
-    private String mDownloadFinishMessage;
-
     public class LocalBinder extends Binder {
     public class LocalBinder extends Binder {
         FileObserverService getService() {
         FileObserverService getService() {
             return FileObserverService.this;
             return FileObserverService.this;
@@ -71,13 +68,9 @@ public class FileObserverService extends Service {
         super.onCreate();
         super.onCreate();
         mDownloadReceiver = new DownloadCompletedReceiverBis();
         mDownloadReceiver = new DownloadCompletedReceiverBis();
         
         
-        FileDownloader downloader = new FileDownloader();
-        mDownloadAddedMessage = downloader.getDownloadAddedMessage();
-        mDownloadFinishMessage= downloader.getDownloadFinishMessage();
-        
         IntentFilter filter = new IntentFilter();
         IntentFilter filter = new IntentFilter();
-        filter.addAction(mDownloadAddedMessage);
-        filter.addAction(mDownloadFinishMessage);        
+        filter.addAction(FileDownloader.getDownloadAddedMessage());
+        filter.addAction(FileDownloader.getDownloadFinishMessage());        
         registerReceiver(mDownloadReceiver, filter);
         registerReceiver(mDownloadReceiver, filter);
         
         
         mObserversMap = new HashMap<String, OwnCloudFileObserver>();
         mObserversMap = new HashMap<String, OwnCloudFileObserver>();
@@ -267,12 +260,12 @@ public class FileObserverService extends Service {
             String downloadPath = intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH);
             String downloadPath = intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH);
             OwnCloudFileObserver observer = mObserversMap.get(downloadPath);
             OwnCloudFileObserver observer = mObserversMap.get(downloadPath);
             if (observer != null) {
             if (observer != null) {
-                if (intent.getAction().equals(mDownloadFinishMessage) &&
+                if (intent.getAction().equals(FileDownloader.getDownloadFinishMessage()) &&
                         new File(downloadPath).exists()) {  // the download could be successful. not; in both cases, the file could be down, due to a former download or upload   
                         new File(downloadPath).exists()) {  // the download could be successful. not; in both cases, the file could be down, due to a former download or upload   
                     observer.startWatching();
                     observer.startWatching();
                     Log_OC.d(TAG, "Watching again " + downloadPath);
                     Log_OC.d(TAG, "Watching again " + downloadPath);
                 
                 
-                } else if (intent.getAction().equals(mDownloadAddedMessage)) {
+                } else if (intent.getAction().equals(FileDownloader.getDownloadAddedMessage())) {
                     observer.stopWatching();
                     observer.stopWatching();
                     Log_OC.d(TAG, "Disabling observance of " + downloadPath);
                     Log_OC.d(TAG, "Disabling observance of " + downloadPath);
                 } 
                 } 

+ 3 - 3
src/com/owncloud/android/files/services/FileUploader.java

@@ -129,8 +129,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
     private RemoteViews mDefaultNotificationContentView;
     private RemoteViews mDefaultNotificationContentView;
 
 
     
     
-    public String getUploadFinishMessage() {
-        return getClass().getName().toString() + UPLOAD_FINISH_MESSAGE;
+    public static String getUploadFinishMessage() {
+        return FileUploader.class.getName().toString() + UPLOAD_FINISH_MESSAGE;
     }
     }
     
     
     /**
     /**
@@ -383,7 +383,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
                 return false;
                 return false;
             String targetKey = buildRemoteName(account, file);
             String targetKey = buildRemoteName(account, file);
             synchronized (mPendingUploads) {
             synchronized (mPendingUploads) {
-                if (file.isDirectory()) {
+                if (file.isFolder()) {
                     // this can be slow if there are many uploads :(
                     // this can be slow if there are many uploads :(
                     Iterator<String> it = mPendingUploads.keySet().iterator();
                     Iterator<String> it = mPendingUploads.keySet().iterator();
                     boolean found = false;
                     boolean found = false;

+ 3 - 3
src/com/owncloud/android/operations/CreateFolderOperation.java

@@ -23,7 +23,7 @@ import org.apache.commons.httpclient.HttpStatus;
 import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
 import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
 
 
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.Log_OC;
-import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 
 
 import eu.alefzero.webdav.WebdavClient;
 import eu.alefzero.webdav.WebdavClient;
@@ -43,7 +43,7 @@ public class CreateFolderOperation extends RemoteOperation {
     
     
     protected String mRemotePath;
     protected String mRemotePath;
     protected boolean mCreateFullPath;
     protected boolean mCreateFullPath;
-    protected DataStorageManager mStorageManager;
+    protected FileDataStorageManager mStorageManager;
     
     
     /**
     /**
      * Constructor
      * Constructor
@@ -52,7 +52,7 @@ public class CreateFolderOperation extends RemoteOperation {
      * @param createFullPath        'True' means that all the ancestor folders should be created if don't exist yet.
      * @param createFullPath        'True' means that all the ancestor folders should be created if don't exist yet.
      * @param storageManager        Reference to the local database corresponding to the account where the file is contained. 
      * @param storageManager        Reference to the local database corresponding to the account where the file is contained. 
      */
      */
-    public CreateFolderOperation(String remotePath, boolean createFullPath, DataStorageManager storageManager) {
+    public CreateFolderOperation(String remotePath, boolean createFullPath, FileDataStorageManager storageManager) {
         mRemotePath = remotePath;
         mRemotePath = remotePath;
         mCreateFullPath = createFullPath;
         mCreateFullPath = createFullPath;
         mStorageManager = storageManager;
         mStorageManager = storageManager;

+ 0 - 6
src/com/owncloud/android/operations/RemoteOperation.java

@@ -136,14 +136,8 @@ public abstract class RemoteOperation implements Runnable {
         mCallerActivity = callerActivity;
         mCallerActivity = callerActivity;
         mClient = null;     // the client instance will be created from mAccount and mContext in the runnerThread to create below
         mClient = null;     // the client instance will be created from mAccount and mContext in the runnerThread to create below
         
         
-        if (listener == null) {
-            throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result");
-        }
         mListener = listener;
         mListener = listener;
         
         
-        if (listenerHandler == null) {
-            throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
-        }
         mListenerHandler = listenerHandler;
         mListenerHandler = listenerHandler;
         
         
         Thread runnerThread = new Thread(this);
         Thread runnerThread = new Thread(this);

+ 4 - 8
src/com/owncloud/android/operations/RemoveFileOperation.java

@@ -21,7 +21,7 @@ import org.apache.commons.httpclient.HttpStatus;
 import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
 import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
 
 
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.Log_OC;
-import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 
 
 import eu.alefzero.webdav.WebdavClient;
 import eu.alefzero.webdav.WebdavClient;
@@ -41,7 +41,7 @@ public class RemoveFileOperation extends RemoteOperation {
     
     
     OCFile mFileToRemove;
     OCFile mFileToRemove;
     boolean mDeleteLocalCopy;
     boolean mDeleteLocalCopy;
-    DataStorageManager mDataStorageManager;
+    FileDataStorageManager mDataStorageManager;
     
     
     
     
     /**
     /**
@@ -51,7 +51,7 @@ public class RemoveFileOperation extends RemoteOperation {
      * @param deleteLocalCopy       When 'true', and a local copy of the file exists, it is also removed.
      * @param deleteLocalCopy       When 'true', and a local copy of the file exists, it is also removed.
      * @param storageManager        Reference to the local database corresponding to the account where the file is contained. 
      * @param storageManager        Reference to the local database corresponding to the account where the file is contained. 
      */
      */
-    public RemoveFileOperation(OCFile fileToRemove, boolean deleteLocalCopy, DataStorageManager storageManager) {
+    public RemoveFileOperation(OCFile fileToRemove, boolean deleteLocalCopy, FileDataStorageManager storageManager) {
         mFileToRemove = fileToRemove;
         mFileToRemove = fileToRemove;
         mDeleteLocalCopy = deleteLocalCopy;
         mDeleteLocalCopy = deleteLocalCopy;
         mDataStorageManager = storageManager;
         mDataStorageManager = storageManager;
@@ -81,11 +81,7 @@ public class RemoveFileOperation extends RemoteOperation {
             delete = new DeleteMethod(client.getBaseUri() + WebdavUtils.encodePath(mFileToRemove.getRemotePath()));
             delete = new DeleteMethod(client.getBaseUri() + WebdavUtils.encodePath(mFileToRemove.getRemotePath()));
             int status = client.executeMethod(delete, REMOVE_READ_TIMEOUT, REMOVE_CONNECTION_TIMEOUT);
             int status = client.executeMethod(delete, REMOVE_READ_TIMEOUT, REMOVE_CONNECTION_TIMEOUT);
             if (delete.succeeded() || status == HttpStatus.SC_NOT_FOUND) {
             if (delete.succeeded() || status == HttpStatus.SC_NOT_FOUND) {
-                if (mFileToRemove.isDirectory()) {
-                    mDataStorageManager.removeDirectory(mFileToRemove, true, mDeleteLocalCopy);
-                } else {
-                    mDataStorageManager.removeFile(mFileToRemove, mDeleteLocalCopy);
-                }
+                mDataStorageManager.removeFile(mFileToRemove, true, mDeleteLocalCopy);
             }
             }
             delete.getResponseBodyAsString();   // exhaust the response, although not interesting
             delete.getResponseBodyAsString();   // exhaust the response, although not interesting
             result = new RemoteOperationResult((delete.succeeded() || status == HttpStatus.SC_NOT_FOUND), status, delete.getResponseHeaders());
             result = new RemoteOperationResult((delete.succeeded() || status == HttpStatus.SC_NOT_FOUND), status, delete.getResponseHeaders());

+ 6 - 6
src/com/owncloud/android/operations/RenameFileOperation.java

@@ -26,7 +26,7 @@ import org.apache.jackrabbit.webdav.client.methods.DavMethodBase;
 import android.accounts.Account;
 import android.accounts.Account;
 
 
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.Log_OC;
-import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
 import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
 import com.owncloud.android.utils.FileStorageUtils;
 import com.owncloud.android.utils.FileStorageUtils;
@@ -51,7 +51,7 @@ public class RenameFileOperation extends RemoteOperation {
     private Account mAccount;
     private Account mAccount;
     private String mNewName;
     private String mNewName;
     private String mNewRemotePath;
     private String mNewRemotePath;
-    private DataStorageManager mStorageManager;
+    private FileDataStorageManager mStorageManager;
     
     
     
     
     /**
     /**
@@ -62,7 +62,7 @@ public class RenameFileOperation extends RemoteOperation {
      * @param newName               New name to set as the name of file.
      * @param newName               New name to set as the name of file.
      * @param storageManager        Reference to the local database corresponding to the account where the file is contained. 
      * @param storageManager        Reference to the local database corresponding to the account where the file is contained. 
      */
      */
-    public RenameFileOperation(OCFile file, Account account, String newName, DataStorageManager storageManager) {
+    public RenameFileOperation(OCFile file, Account account, String newName, FileDataStorageManager storageManager) {
         mFile = file;
         mFile = file;
         mAccount = account;
         mAccount = account;
         mNewName = newName;
         mNewName = newName;
@@ -94,7 +94,7 @@ public class RenameFileOperation extends RemoteOperation {
             String parent = (new File(mFile.getRemotePath())).getParent();
             String parent = (new File(mFile.getRemotePath())).getParent();
             parent = (parent.endsWith(OCFile.PATH_SEPARATOR)) ? parent : parent + OCFile.PATH_SEPARATOR; 
             parent = (parent.endsWith(OCFile.PATH_SEPARATOR)) ? parent : parent + OCFile.PATH_SEPARATOR; 
             mNewRemotePath =  parent + mNewName;
             mNewRemotePath =  parent + mNewName;
-            if (mFile.isDirectory()) {
+            if (mFile.isFolder()) {
                 mNewRemotePath += OCFile.PATH_SEPARATOR;
                 mNewRemotePath += OCFile.PATH_SEPARATOR;
             }
             }
             
             
@@ -113,7 +113,7 @@ public class RenameFileOperation extends RemoteOperation {
             int status = client.executeMethod(move, RENAME_READ_TIMEOUT, RENAME_CONNECTION_TIMEOUT);
             int status = client.executeMethod(move, RENAME_READ_TIMEOUT, RENAME_CONNECTION_TIMEOUT);
             if (move.succeeded()) {
             if (move.succeeded()) {
 
 
-                if (mFile.isDirectory()) {
+                if (mFile.isFolder()) {
                     saveLocalDirectory();
                     saveLocalDirectory();
                     
                     
                 } else {
                 } else {
@@ -152,7 +152,7 @@ public class RenameFileOperation extends RemoteOperation {
 
 
     
     
     private void saveLocalDirectory() {
     private void saveLocalDirectory() {
-        mStorageManager.moveDirectory(mFile, mNewRemotePath);
+        mStorageManager.moveFolder(mFile, mNewRemotePath);
         String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
         String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
         File localDir = new File(localPath);
         File localDir = new File(localPath);
         if (localDir.exists()) {
         if (localDir.exists()) {

+ 10 - 9
src/com/owncloud/android/operations/SynchronizeFileOperation.java

@@ -28,7 +28,7 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.Intent;
 
 
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.Log_OC;
-import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileDownloader;
 import com.owncloud.android.files.services.FileDownloader;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.files.services.FileUploader;
@@ -46,10 +46,9 @@ public class SynchronizeFileOperation extends RemoteOperation {
     
     
     private OCFile mLocalFile;
     private OCFile mLocalFile;
     private OCFile mServerFile;
     private OCFile mServerFile;
-    private DataStorageManager mStorageManager;
+    private FileDataStorageManager mStorageManager;
     private Account mAccount;
     private Account mAccount;
     private boolean mSyncFileContents;
     private boolean mSyncFileContents;
-    private boolean mLocalChangeAlreadyKnown;
     private Context mContext;
     private Context mContext;
     
     
     private boolean mTransferWasRequested = false;
     private boolean mTransferWasRequested = false;
@@ -57,10 +56,9 @@ public class SynchronizeFileOperation extends RemoteOperation {
     public SynchronizeFileOperation(
     public SynchronizeFileOperation(
             OCFile localFile,
             OCFile localFile,
             OCFile serverFile,          // make this null to let the operation checks the server; added to reuse info from SynchronizeFolderOperation 
             OCFile serverFile,          // make this null to let the operation checks the server; added to reuse info from SynchronizeFolderOperation 
-            DataStorageManager storageManager, 
+            FileDataStorageManager storageManager, 
             Account account, 
             Account account, 
             boolean syncFileContents,
             boolean syncFileContents,
-            boolean localChangeAlreadyKnown, 
             Context context) {
             Context context) {
         
         
         mLocalFile = localFile;
         mLocalFile = localFile;
@@ -68,7 +66,6 @@ public class SynchronizeFileOperation extends RemoteOperation {
         mStorageManager = storageManager;
         mStorageManager = storageManager;
         mAccount = account;
         mAccount = account;
         mSyncFileContents = syncFileContents;
         mSyncFileContents = syncFileContents;
-        mLocalChangeAlreadyKnown = localChangeAlreadyKnown;
         mContext = context;
         mContext = context;
     }
     }
 
 
@@ -112,16 +109,18 @@ public class SynchronizeFileOperation extends RemoteOperation {
               
               
                     /// check changes in server and local file
                     /// check changes in server and local file
                     boolean serverChanged = false;
                     boolean serverChanged = false;
+                    /* time for eTag is coming, but not yet
                     if (mServerFile.getEtag() != null) {
                     if (mServerFile.getEtag() != null) {
                         serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag()));   // TODO could this be dangerous when the user upgrades the server from non-tagged to tagged?
                         serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag()));   // TODO could this be dangerous when the user upgrades the server from non-tagged to tagged?
-                    } else {
+                    } else { */
                         // server without etags
                         // server without etags
                         serverChanged = (mServerFile.getModificationTimestamp() > mLocalFile.getModificationTimestampAtLastSyncForData());
                         serverChanged = (mServerFile.getModificationTimestamp() > mLocalFile.getModificationTimestampAtLastSyncForData());
-                    }
-                    boolean localChanged = (mLocalChangeAlreadyKnown || mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData());
+                    //}
+                    boolean localChanged = (mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData());
                         // TODO this will be always true after the app is upgraded to database version 2; will result in unnecessary uploads
                         // TODO this will be always true after the app is upgraded to database version 2; will result in unnecessary uploads
               
               
                     /// decide action to perform depending upon changes
                     /// decide action to perform depending upon changes
+                    //if (!mLocalFile.getEtag().isEmpty() && localChanged && serverChanged) {
                     if (localChanged && serverChanged) {
                     if (localChanged && serverChanged) {
                         result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
                         result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
                   
                   
@@ -218,6 +217,8 @@ public class SynchronizeFileOperation extends RemoteOperation {
         file.setFileLength(we.contentLength());
         file.setFileLength(we.contentLength());
         file.setMimetype(we.contentType());
         file.setMimetype(we.contentType());
         file.setModificationTimestamp(we.modifiedTimestamp());
         file.setModificationTimestamp(we.modifiedTimestamp());
+        file.setEtag(we.etag());
+        
         return file;
         return file;
     }
     }
 
 

+ 319 - 127
src/com/owncloud/android/operations/SynchronizeFolderOperation.java

@@ -35,11 +35,13 @@ import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
 
 
 import android.accounts.Account;
 import android.accounts.Account;
 import android.content.Context;
 import android.content.Context;
+import android.content.Intent;
 
 
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.Log_OC;
-import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
 import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.syncadapter.FileSyncService;
 import com.owncloud.android.utils.FileStorageUtils;
 import com.owncloud.android.utils.FileStorageUtils;
 
 
 import eu.alefzero.webdav.WebdavClient;
 import eu.alefzero.webdav.WebdavClient;
@@ -48,55 +50,81 @@ import eu.alefzero.webdav.WebdavUtils;
 
 
 
 
 /**
 /**
- * Remote operation performing the synchronization a the contents of a remote folder with the local database
+ *  Remote operation performing the synchronization of the list of files contained 
+ *  in a folder identified with its remote path.
+ *  
+ *  Fetches the list and properties of the files contained in the given folder, including their 
+ *  properties, and updates the local database with them.
+ *  
+ *  Does NOT enter in the child folders to synchronize their contents also.
  * 
  * 
- * @author David A. Velasco
+ *  @author David A. Velasco
  */
  */
 public class SynchronizeFolderOperation extends RemoteOperation {
 public class SynchronizeFolderOperation extends RemoteOperation {
 
 
     private static final String TAG = SynchronizeFolderOperation.class.getSimpleName();
     private static final String TAG = SynchronizeFolderOperation.class.getSimpleName();
 
 
-    /** Remote folder to synchronize */
-    private String mRemotePath;
     
     
-    /** Timestamp for the synchronization in progress */
+    /** Time stamp for the synchronization process in progress */
     private long mCurrentSyncTime;
     private long mCurrentSyncTime;
     
     
-    /** Id of the folder to synchronize in the local database */
-    private long mParentId;
+    /** Remote folder to synchronize */
+    private OCFile mLocalFolder;
     
     
     /** Access to the local database */
     /** Access to the local database */
-    private DataStorageManager mStorageManager;
+    private FileDataStorageManager mStorageManager;
     
     
     /** Account where the file to synchronize belongs */
     /** Account where the file to synchronize belongs */
     private Account mAccount;
     private Account mAccount;
     
     
-    /** Android context; necessary to send requests to the download service; maybe something to refactor */
+    /** Android context; necessary to send requests to the download service */
     private Context mContext;
     private Context mContext;
     
     
-    /** Files and folders contained in the synchronized folder */
+    /** Files and folders contained in the synchronized folder after a successful operation */
     private List<OCFile> mChildren;
     private List<OCFile> mChildren;
 
 
+    /** Counter of conflicts found between local and remote files */
     private int mConflictsFound;
     private int mConflictsFound;
 
 
+    /** Counter of failed operations in synchronization of kept-in-sync files */
     private int mFailsInFavouritesFound;
     private int mFailsInFavouritesFound;
 
 
+    /** Map of remote and local paths to files that where locally stored in a location out of the ownCloud folder and couldn't be copied automatically into it */
     private Map<String, String> mForgottenLocalFiles;
     private Map<String, String> mForgottenLocalFiles;
+
+    /** 'True' means that this operation is part of a full account synchronization */ 
+    private boolean mSyncFullAccount;
+
+    /** 'True' means that the remote folder changed from last synchronization and should be fetched */
+    private boolean mRemoteFolderChanged;
     
     
     
     
-    public SynchronizeFolderOperation(  String remotePath, 
+    /**
+     * Creates a new instance of {@link SynchronizeFolderOperation}.
+     * 
+     * @param   remoteFolderPath        Remote folder to synchronize.
+     * @param   currentSyncTime         Time stamp for the synchronization process in progress.
+     * @param   localFolderId           Identifier in the local database of the folder to synchronize.
+     * @param   updateFolderProperties  'True' means that the properties of the folder should be updated also, not just its content.
+     * @param   syncFullAccount         'True' means that this operation is part of a full account synchronization.
+     * @param   dataStorageManager      Interface with the local database.
+     * @param   account                 ownCloud account where the folder is located. 
+     * @param   context                 Application context.
+     */
+    public SynchronizeFolderOperation(  OCFile folder, 
                                         long currentSyncTime, 
                                         long currentSyncTime, 
-                                        long parentId, 
-                                        DataStorageManager dataStorageManager, 
+                                        boolean syncFullAccount,
+                                        FileDataStorageManager dataStorageManager, 
                                         Account account, 
                                         Account account, 
                                         Context context ) {
                                         Context context ) {
-        mRemotePath = remotePath;
+        mLocalFolder = folder;
         mCurrentSyncTime = currentSyncTime;
         mCurrentSyncTime = currentSyncTime;
-        mParentId = parentId;
+        mSyncFullAccount = syncFullAccount;
         mStorageManager = dataStorageManager;
         mStorageManager = dataStorageManager;
         mAccount = account;
         mAccount = account;
         mContext = context;
         mContext = context;
         mForgottenLocalFiles = new HashMap<String, String>();
         mForgottenLocalFiles = new HashMap<String, String>();
+        mRemoteFolderChanged = false;
     }
     }
     
     
     
     
@@ -115,13 +143,17 @@ public class SynchronizeFolderOperation extends RemoteOperation {
     /**
     /**
      * Returns the list of files and folders contained in the synchronized folder, if called after synchronization is complete.
      * Returns the list of files and folders contained in the synchronized folder, if called after synchronization is complete.
      * 
      * 
-     * @return      List of files and folders contained in the synchronized folder.
+     * @return  List of files and folders contained in the synchronized folder.
      */
      */
     public List<OCFile> getChildren() {
     public List<OCFile> getChildren() {
         return mChildren;
         return mChildren;
     }
     }
     
     
-    
+    /**
+     * Performs the synchronization.
+     * 
+     * {@inheritDoc}
+     */
     @Override
     @Override
     protected RemoteOperationResult run(WebdavClient client) {
     protected RemoteOperationResult run(WebdavClient client) {
         RemoteOperationResult result = null;
         RemoteOperationResult result = null;
@@ -129,130 +161,115 @@ public class SynchronizeFolderOperation extends RemoteOperation {
         mConflictsFound = 0;
         mConflictsFound = 0;
         mForgottenLocalFiles.clear();
         mForgottenLocalFiles.clear();
         
         
-        // code before in FileSyncAdapter.fetchData
+        result = checkForChanges(client);
+        
+        if (result.isSuccess()) {
+            if (mRemoteFolderChanged) {
+                result = fetchAndSyncRemoteFolder(client);
+            } else {
+                mChildren = mStorageManager.getFolderContent(mLocalFolder);
+            }
+        }
+        
+        if (!mSyncFullAccount) {            
+            sendStickyBroadcast(false, mLocalFolder.getRemotePath(), result);
+        }
+
+        return result;
+        
+    }
+
+
+    private RemoteOperationResult checkForChanges(WebdavClient client) {
+        mRemoteFolderChanged = false;
+        RemoteOperationResult result = null;
+        String remotePath = null;
         PropFindMethod query = null;
         PropFindMethod query = null;
+        
         try {
         try {
-            Log_OC.d(TAG, "Synchronizing " + mAccount.name + ", fetching files in " + mRemotePath);
-            
+            remotePath = mLocalFolder.getRemotePath();
+            Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath);
+
             // remote request 
             // remote request 
-            query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath),
+            query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(remotePath),
                     DavConstants.PROPFIND_ALL_PROP,
                     DavConstants.PROPFIND_ALL_PROP,
-                    DavConstants.DEPTH_1);
+                    DavConstants.DEPTH_0);
             int status = client.executeMethod(query);
             int status = client.executeMethod(query);
-            
-            // check and process response   - /// TODO take into account all the possible status per child-resource
-            if (isMultiStatus(status)) { 
-                MultiStatus resp = query.getResponseBodyAsMultiStatus();
-            
-                // synchronize properties of the parent folder, if necessary
-                if (mParentId == DataStorageManager.ROOT_PARENT_ID) {
-                    WebdavEntry we = new WebdavEntry(resp.getResponses()[0], client.getBaseUri().getPath());
-                    OCFile parent = fillOCFile(we);
-                    mStorageManager.saveFile(parent);
-                    mParentId = parent.getFileId();
-                }
-                
-                // read contents in folder
-                List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
-                List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
-                for (int i = 1; i < resp.getResponses().length; ++i) {
-                    /// new OCFile instance with the data from the server
-                    WebdavEntry we = new WebdavEntry(resp.getResponses()[i], client.getBaseUri().getPath());
-                    OCFile file = fillOCFile(we);
-                    
-                    /// set data about local state, keeping unchanged former data if existing
-                    file.setLastSyncDateForProperties(mCurrentSyncTime);
-                    OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
-                    if (oldFile != null) {
-                        file.setKeepInSync(oldFile.keepInSync());
-                        file.setLastSyncDateForData(oldFile.getLastSyncDateForData());
-                        file.setModificationTimestampAtLastSyncForData(oldFile.getModificationTimestampAtLastSyncForData());    // must be kept unchanged when the file contents are not updated
-                        checkAndFixForeignStoragePath(oldFile);
-                        file.setStoragePath(oldFile.getStoragePath());
-                    }
 
 
-                    /// scan default location if local copy of file is not linked in OCFile instance
-                    if (file.getStoragePath() == null && !file.isDirectory()) {
-                        File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
-                        if (f.exists()) {
-                            file.setStoragePath(f.getAbsolutePath());
-                            file.setLastSyncDateForData(f.lastModified());
-                        }
-                    }
-                    
-                    /// prepare content synchronization for kept-in-sync files
-                    if (file.keepInSync()) {
-                        SynchronizeFileOperation operation = new SynchronizeFileOperation(  oldFile,        
-                                                                                            file, 
-                                                                                            mStorageManager,
-                                                                                            mAccount,       
-                                                                                            true, 
-                                                                                            false,          
-                                                                                            mContext
-                                                                                            );
-                        filesToSyncContents.add(operation);
-                    }
+            // check and process response
+            if (isMultiStatus(status)) {
+                // parse data from remote folder 
+                WebdavEntry we = new WebdavEntry(query.getResponseBodyAsMultiStatus().getResponses()[0], client.getBaseUri().getPath());
+                OCFile remoteFolder = fillOCFile(we);
                 
                 
-                    updatedFiles.add(file);
-                }
-                                
-                // save updated contents in local database; all at once, trying to get a best performance in database update (not a big deal, indeed)
-                mStorageManager.saveFiles(updatedFiles);
+                // check if remote and local folder are different
+                mRemoteFolderChanged = !(remoteFolder.getEtag().equalsIgnoreCase(mLocalFolder.getEtag()));
                 
                 
-                // request for the synchronization of files AFTER saving last properties
-                SynchronizeFileOperation op = null;
-                RemoteOperationResult contentsResult = null;
-                for (int i=0; i < filesToSyncContents.size(); i++) {
-                    op = filesToSyncContents.get(i);
-                    contentsResult = op.execute(client);   // returns without waiting for upload or download finishes
-                    if (!contentsResult.isSuccess()) {
-                        if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
-                            mConflictsFound++;
-                        } else {
-                            mFailsInFavouritesFound++;
-                            if (contentsResult.getException() != null) {
-                                Log_OC.e(TAG, "Error while synchronizing favourites : " +  contentsResult.getLogMessage(), contentsResult.getException());
-                            } else {
-                                Log_OC.e(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage());
-                            }
-                        }
-                    }   // won't let these fails break the synchronization process
-                }
-
-                    
-                // removal of obsolete files
-                mChildren = mStorageManager.getDirectoryContent(mStorageManager.getFileById(mParentId));
-                OCFile file;
-                String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
-                for (int i=0; i < mChildren.size(); ) {
-                    file = mChildren.get(i);
-                    if (file.getLastSyncDateForProperties() != mCurrentSyncTime) {
-                        Log_OC.d(TAG, "removing file: " + file);
-                        mStorageManager.removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath)));
-                        mChildren.remove(i);
-                    } else {
-                        i++;
-                    }
-                }
+                result = new RemoteOperationResult(ResultCode.OK);
                 
                 
             } else {
             } else {
+                // check failed
                 client.exhaustResponse(query.getResponseBodyAsStream());
                 client.exhaustResponse(query.getResponseBodyAsStream());
+                if (status == HttpStatus.SC_NOT_FOUND) {
+                    removeLocalFolder();
+                }
+                result = new RemoteOperationResult(false, status, query.getResponseHeaders());
+            }
+
+        } catch (Exception e) {
+            result = new RemoteOperationResult(e);
+            
+
+        } finally {
+            if (query != null)
+                query.releaseConnection();  // let the connection available for other methods
+            if (result.isSuccess()) {
+                Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " + (mRemoteFolderChanged ? "changed" : "not changed"));
+            } else {
+                if (result.isException()) {
+                    Log_OC.e(TAG, "Checked " + mAccount.name + remotePath  + " : " + result.getLogMessage(), result.getException());
+                } else {
+                    Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " + result.getLogMessage());
+                }
             }
             }
             
             
-            // prepare result object
+        }
+        return result;
+    }
+
+
+    private RemoteOperationResult fetchAndSyncRemoteFolder(WebdavClient client) {
+        RemoteOperationResult result = null;
+        String remotePath = null;
+        PropFindMethod query = null;
+        try {
+            remotePath = mLocalFolder.getRemotePath();
+            Log_OC.d(TAG, "Synchronizing " + mAccount.name + remotePath);
+
+            // remote request 
+            query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(remotePath),
+                    DavConstants.PROPFIND_ALL_PROP,
+                    DavConstants.DEPTH_1);
+            int status = client.executeMethod(query);
+
+            // check and process response
             if (isMultiStatus(status)) {
             if (isMultiStatus(status)) {
+                synchronizeData(query.getResponseBodyAsMultiStatus(), client);
                 if (mConflictsFound > 0  || mFailsInFavouritesFound > 0) { 
                 if (mConflictsFound > 0  || mFailsInFavouritesFound > 0) { 
                     result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);   // should be different result, but will do the job
                     result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);   // should be different result, but will do the job
-                            
                 } else {
                 } else {
                     result = new RemoteOperationResult(true, status, query.getResponseHeaders());
                     result = new RemoteOperationResult(true, status, query.getResponseHeaders());
                 }
                 }
+                
             } else {
             } else {
+                // synchronization failed
+                client.exhaustResponse(query.getResponseBodyAsStream());
+                if (status == HttpStatus.SC_NOT_FOUND) {
+                    removeLocalFolder();
+                }
                 result = new RemoteOperationResult(false, status, query.getResponseHeaders());
                 result = new RemoteOperationResult(false, status, query.getResponseHeaders());
             }
             }
-            
-            
-            
+
         } catch (Exception e) {
         } catch (Exception e) {
             result = new RemoteOperationResult(e);
             result = new RemoteOperationResult(e);
             
             
@@ -261,19 +278,153 @@ public class SynchronizeFolderOperation extends RemoteOperation {
             if (query != null)
             if (query != null)
                 query.releaseConnection();  // let the connection available for other methods
                 query.releaseConnection();  // let the connection available for other methods
             if (result.isSuccess()) {
             if (result.isSuccess()) {
-                Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
+                Log_OC.i(TAG, "Synchronized " + mAccount.name + remotePath + ": " + result.getLogMessage());
             } else {
             } else {
                 if (result.isException()) {
                 if (result.isException()) {
-                    Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
+                    Log_OC.e(TAG, "Synchronized " + mAccount.name + remotePath  + ": " + result.getLogMessage(), result.getException());
                 } else {
                 } else {
-                    Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
+                    Log_OC.e(TAG, "Synchronized " + mAccount.name + remotePath + ": " + result.getLogMessage());
                 }
                 }
             }
             }
+            
         }
         }
-        
         return result;
         return result;
     }
     }
-    
+
+
+    private void removeLocalFolder() {
+        if (mStorageManager.fileExists(mLocalFolder.getFileId())) {
+            String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
+            mStorageManager.removeFolder(mLocalFolder, true, (mLocalFolder.isDown() && mLocalFolder.getStoragePath().startsWith(currentSavePath)));
+        }
+    }
+
+
+    /**
+     *  Synchronizes the data retrieved from the server about the contents of the target folder 
+     *  with the current data in the local database.
+     *  
+     *  Grants that mChildren is updated with fresh data after execution.
+     * 
+     *  @param dataInServer     Full response got from the server with the data of the target 
+     *                          folder and its direct children.
+     *  @param client           Client instance to the remote server where the data were 
+     *                          retrieved.  
+     *  @return                 'True' when any change was made in the local data, 'false' otherwise.
+     */
+    private void synchronizeData(MultiStatus dataInServer, WebdavClient client) {
+        // get 'fresh data' from the database
+        mLocalFolder = mStorageManager.getFileByPath(mLocalFolder.getRemotePath());
+        
+        // parse data from remote folder 
+        WebdavEntry we = new WebdavEntry(dataInServer.getResponses()[0], client.getBaseUri().getPath());
+        OCFile remoteFolder = fillOCFile(we);
+        remoteFolder.setParentId(mLocalFolder.getParentId());
+        remoteFolder.setFileId(mLocalFolder.getFileId());
+        
+        Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath() + " changed - starting update of local data ");
+        
+        List<OCFile> updatedFiles = new Vector<OCFile>(dataInServer.getResponses().length - 1);
+        List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
+
+        // get current data about local contents of the folder to synchronize
+        List<OCFile> localFiles = mStorageManager.getFolderContent(mLocalFolder);
+        Map<String, OCFile> localFilesMap = new HashMap<String, OCFile>(localFiles.size());
+        for (OCFile file : localFiles) {
+            localFilesMap.put(file.getRemotePath(), file);
+        }
+        
+        // loop to update every child
+        OCFile remoteFile = null, localFile = null;
+        for (int i = 1; i < dataInServer.getResponses().length; ++i) {
+            /// new OCFile instance with the data from the server
+            we = new WebdavEntry(dataInServer.getResponses()[i], client.getBaseUri().getPath());                        
+            remoteFile = fillOCFile(we);
+            remoteFile.setParentId(mLocalFolder.getFileId());
+
+            /// retrieve local data for the read file 
+            //localFile = mStorageManager.getFileByPath(remoteFile.getRemotePath());
+            localFile = localFilesMap.remove(remoteFile.getRemotePath());
+            
+            /// add to the remoteFile (the new one) data about LOCAL STATE (not existing in the server side)
+            remoteFile.setLastSyncDateForProperties(mCurrentSyncTime);
+            if (localFile != null) {
+                // some properties of local state are kept unmodified
+                remoteFile.setFileId(localFile.getFileId());
+                remoteFile.setKeepInSync(localFile.keepInSync());
+                remoteFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
+                remoteFile.setModificationTimestampAtLastSyncForData(localFile.getModificationTimestampAtLastSyncForData());
+                remoteFile.setStoragePath(localFile.getStoragePath());
+                remoteFile.setEtag(localFile.getEtag());    // eTag will not be updated unless contents are synchronized (Synchronize[File|Folder]Operation with remoteFile as parameter)
+                if (remoteFile.isFolder()) {
+                    remoteFile.setFileLength(localFile.getFileLength()); // TODO move operations about size of folders to FileContentProvider
+                }
+            } else {
+                remoteFile.setEtag(""); // remote eTag will not be updated unless contents are synchronized (Synchronize[File|Folder]Operation with remoteFile as parameter)
+            }
+
+            /// check and fix, if needed, local storage path
+            checkAndFixForeignStoragePath(remoteFile);      // fixing old policy - now local files must be copied into the ownCloud local folder 
+            searchForLocalFileInDefaultPath(remoteFile);    // legacy   
+
+            /// prepare content synchronization for kept-in-sync files
+            if (remoteFile.keepInSync()) {
+                SynchronizeFileOperation operation = new SynchronizeFileOperation(  localFile,        
+                                                                                    remoteFile, 
+                                                                                    mStorageManager,
+                                                                                    mAccount,       
+                                                                                    true, 
+                                                                                    mContext
+                                                                                    );
+                filesToSyncContents.add(operation);
+            }
+            
+            updatedFiles.add(remoteFile);
+        }
+
+        // save updated contents in local database; all at once, trying to get a best performance in database update (not a big deal, indeed)
+        mStorageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values());
+
+        // request for the synchronization of file contents AFTER saving current remote properties
+        startContentSynchronizations(filesToSyncContents, client);
+
+        // removal of obsolete files
+        //removeObsoleteFiles();
+       
+        // must be done AFTER saving all the children information, so that eTag is not updated in the database in case of unexpected exceptions
+        //mStorageManager.saveFile(remoteFolder);
+        mChildren = updatedFiles;
+        
+    }
+
+    /**
+     * Performs a list of synchronization operations, determining if a download or upload is needed or
+     * if exists conflict due to changes both in local and remote contents of the each file.
+     * 
+     * If download or upload is needed, request the operation to the corresponding service and goes on.
+     * 
+     * @param filesToSyncContents       Synchronization operations to execute.
+     * @param client                    Interface to the remote ownCloud server.
+     */
+    private void startContentSynchronizations(List<SynchronizeFileOperation> filesToSyncContents, WebdavClient client) {
+        RemoteOperationResult contentsResult = null;
+        for (SynchronizeFileOperation op: filesToSyncContents) {
+            contentsResult = op.execute(client);   // returns without waiting for upload or download finishes
+            if (!contentsResult.isSuccess()) {
+                if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
+                    mConflictsFound++;
+                } else {
+                    mFailsInFavouritesFound++;
+                    if (contentsResult.getException() != null) {
+                        Log_OC.e(TAG, "Error while synchronizing favourites : " +  contentsResult.getLogMessage(), contentsResult.getException());
+                    } else {
+                        Log_OC.e(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage());
+                    }
+                }
+            }   // won't let these fails break the synchronization process
+        }
+    }
+
 
 
     public boolean isMultiStatus(int status) {
     public boolean isMultiStatus(int status) {
         return (status == HttpStatus.SC_MULTI_STATUS); 
         return (status == HttpStatus.SC_MULTI_STATUS); 
@@ -292,7 +443,7 @@ public class SynchronizeFolderOperation extends RemoteOperation {
         file.setFileLength(we.contentLength());
         file.setFileLength(we.contentLength());
         file.setMimetype(we.contentType());
         file.setMimetype(we.contentType());
         file.setModificationTimestamp(we.modifiedTimestamp());
         file.setModificationTimestamp(we.modifiedTimestamp());
-        file.setParentId(mParentId);
+        file.setEtag(we.etag());
         return file;
         return file;
     }
     }
     
     
@@ -303,7 +454,7 @@ public class SynchronizeFolderOperation extends RemoteOperation {
      * 
      * 
      * If the copy fails, the link to the local file is nullified. The account of forgotten files is kept in 
      * If the copy fails, the link to the local file is nullified. The account of forgotten files is kept in 
      * {@link #mForgottenLocalFiles}
      * {@link #mForgottenLocalFiles}
-     * 
+     *) 
      * @param file      File to check and fix.
      * @param file      File to check and fix.
      */
      */
     private void checkAndFixForeignStoragePath(OCFile file) {
     private void checkAndFixForeignStoragePath(OCFile file) {
@@ -360,5 +511,46 @@ public class SynchronizeFolderOperation extends RemoteOperation {
         }
         }
     }
     }
 
 
+    /**
+     * Scans the default location for saving local copies of files searching for
+     * a 'lost' file with the same full name as the {@link OCFile} received as 
+     * parameter.
+     *  
+     * @param file      File to associate a possible 'lost' local file.
+     */
+    private void searchForLocalFileInDefaultPath(OCFile file) {
+        if (file.getStoragePath() == null && !file.isFolder()) {
+            File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+            if (f.exists()) {
+                file.setStoragePath(f.getAbsolutePath());
+                file.setLastSyncDateForData(f.lastModified());
+            }
+        }
+    }
+
+    
+    /**
+     * Sends a message to any application component interested in the progress of the synchronization.
+     * 
+     * @param inProgress        'True' when the synchronization progress is not finished.
+     * @param dirRemotePath     Remote path of a folder that was just synchronized (with or without success)
+     */
+    private void sendStickyBroadcast(boolean inProgress, String dirRemotePath, RemoteOperationResult result) {
+        Intent i = new Intent(FileSyncService.getSyncMessage());
+        i.putExtra(FileSyncService.IN_PROGRESS, inProgress);
+        i.putExtra(FileSyncService.ACCOUNT_NAME, mAccount.name);
+        if (dirRemotePath != null) {
+            i.putExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH, dirRemotePath);
+        }
+        if (result != null) {
+            i.putExtra(FileSyncService.SYNC_RESULT, result);
+        }
+        mContext.sendStickyBroadcast(i);
+    }
+
+
+    public boolean getRemoteFolderChanged() {
+        return mRemoteFolderChanged;
+    }
 
 
 }
 }

+ 257 - 34
src/com/owncloud/android/providers/FileContentProvider.java

@@ -18,18 +18,23 @@
 
 
 package com.owncloud.android.providers;
 package com.owncloud.android.providers;
 
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashMap;
 
 
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.db.ProviderMeta;
 import com.owncloud.android.db.ProviderMeta;
 import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
 import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
 
 
 
 
 import android.content.ContentProvider;
 import android.content.ContentProvider;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
 import android.content.ContentUris;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Context;
+import android.content.OperationApplicationException;
 import android.content.UriMatcher;
 import android.content.UriMatcher;
 import android.database.Cursor;
 import android.database.Cursor;
 import android.database.SQLException;
 import android.database.SQLException;
@@ -43,6 +48,7 @@ import android.text.TextUtils;
  * The ContentProvider for the ownCloud App.
  * The ContentProvider for the ownCloud App.
  * 
  * 
  * @author Bartek Przybylski
  * @author Bartek Przybylski
+ * @author David A. Velasco
  * 
  * 
  */
  */
 public class FileContentProvider extends ContentProvider {
 public class FileContentProvider extends ContentProvider {
@@ -79,44 +85,110 @@ public class FileContentProvider extends ContentProvider {
                 ProviderTableMeta.FILE_KEEP_IN_SYNC);
                 ProviderTableMeta.FILE_KEEP_IN_SYNC);
         mProjectionMap.put(ProviderTableMeta.FILE_ACCOUNT_OWNER,
         mProjectionMap.put(ProviderTableMeta.FILE_ACCOUNT_OWNER,
                 ProviderTableMeta.FILE_ACCOUNT_OWNER);
                 ProviderTableMeta.FILE_ACCOUNT_OWNER);
+        mProjectionMap.put(ProviderTableMeta.FILE_ETAG, 
+                ProviderTableMeta.FILE_ETAG);
     }
     }
 
 
     private static final int SINGLE_FILE = 1;
     private static final int SINGLE_FILE = 1;
     private static final int DIRECTORY = 2;
     private static final int DIRECTORY = 2;
     private static final int ROOT_DIRECTORY = 3;
     private static final int ROOT_DIRECTORY = 3;
-    
-    private UriMatcher mUriMatcher;
-//    static {
-//        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-//        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, null, ROOT_DIRECTORY);
-//        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "file/", SINGLE_FILE);
-//        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "file/#", SINGLE_FILE);
-//        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "dir/#", DIRECTORY);
-//    }
 
 
+    private UriMatcher mUriMatcher;
     
     
     @Override
     @Override
     public int delete(Uri uri, String where, String[] whereArgs) {
     public int delete(Uri uri, String where, String[] whereArgs) {
+        //Log_OC.d(TAG, "Deleting " + uri + " at provider " + this);
+        int count = 0;
         SQLiteDatabase db = mDbHelper.getWritableDatabase();
         SQLiteDatabase db = mDbHelper.getWritableDatabase();
+        db.beginTransaction();
+        try {
+            count = delete(db, uri, where, whereArgs);
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+        getContext().getContentResolver().notifyChange(uri, null);
+        return count;
+    }
+    
+    
+    private int delete(SQLiteDatabase db, Uri uri, String where, String[] whereArgs) {
         int count = 0;
         int count = 0;
         switch (mUriMatcher.match(uri)) {
         switch (mUriMatcher.match(uri)) {
         case SINGLE_FILE:
         case SINGLE_FILE:
+            /*Cursor c = query(db, uri, null, where, whereArgs, null);
+            String remotePath = "(unexisting)";
+            if (c != null && c.moveToFirst()) {
+                remotePath = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH));
+            }
+            Log_OC.d(TAG, "Removing FILE " + remotePath);
+            */
             count = db.delete(ProviderTableMeta.DB_NAME,
             count = db.delete(ProviderTableMeta.DB_NAME,
                     ProviderTableMeta._ID
                     ProviderTableMeta._ID
                             + "="
                             + "="
                             + uri.getPathSegments().get(1)
                             + uri.getPathSegments().get(1)
                             + (!TextUtils.isEmpty(where) ? " AND (" + where
                             + (!TextUtils.isEmpty(where) ? " AND (" + where
                                     + ")" : ""), whereArgs);
                                     + ")" : ""), whereArgs);
+            /* just for log
+            if (c!=null) {
+                c.close();
+            }
+            */
+            break;
+        case DIRECTORY:
+            // deletion of folder is recursive
+            /*
+            Uri folderUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, Long.parseLong(uri.getPathSegments().get(1)));
+            Cursor folder = query(db, folderUri, null, null, null, null);
+            String folderName = "(unknown)";
+            if (folder != null && folder.moveToFirst()) {
+                folderName = folder.getString(folder.getColumnIndex(ProviderTableMeta.FILE_PATH));
+            }
+            */
+            Cursor children = query(uri, null, null, null, null);
+            if (children != null && children.moveToFirst())  {
+                long childId;
+                boolean isDir; 
+                //String remotePath; 
+                while (!children.isAfterLast()) {
+                    childId = children.getLong(children.getColumnIndex(ProviderTableMeta._ID));
+                    isDir = "DIR".equals(children.getString(children.getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)));
+                    //remotePath = children.getString(children.getColumnIndex(ProviderTableMeta.FILE_PATH));
+                    if (isDir) {
+                        count += delete(db, ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_DIR, childId), null, null);
+                    } else {
+                        count += delete(db, ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, childId), null, null);
+                    }
+                    children.moveToNext();
+                }
+                children.close();
+            } /*else {
+                Log_OC.d(TAG, "No child to remove in DIRECTORY " + folderName);
+            }
+            Log_OC.d(TAG, "Removing DIRECTORY " + folderName + " (or maybe not) ");
+            */
+            count += db.delete(ProviderTableMeta.DB_NAME,
+                    ProviderTableMeta._ID
+                    + "="
+                    + uri.getPathSegments().get(1)
+                    + (!TextUtils.isEmpty(where) ? " AND (" + where
+                            + ")" : ""), whereArgs);
+            /* Just for log
+             if (folder != null) {
+                folder.close();
+            }*/
             break;
             break;
         case ROOT_DIRECTORY:
         case ROOT_DIRECTORY:
+            //Log_OC.d(TAG, "Removing ROOT!");
             count = db.delete(ProviderTableMeta.DB_NAME, where, whereArgs);
             count = db.delete(ProviderTableMeta.DB_NAME, where, whereArgs);
             break;
             break;
         default:
         default:
+            //Log_OC.e(TAG, "Unknown uri " + uri);
             throw new IllegalArgumentException("Unknown uri: " + uri.toString());
             throw new IllegalArgumentException("Unknown uri: " + uri.toString());
         }
         }
-        getContext().getContentResolver().notifyChange(uri, null);
         return count;
         return count;
     }
     }
+    
 
 
     @Override
     @Override
     public String getType(Uri uri) {
     public String getType(Uri uri) {
@@ -133,24 +205,52 @@ public class FileContentProvider extends ContentProvider {
 
 
     @Override
     @Override
     public Uri insert(Uri uri, ContentValues values) {
     public Uri insert(Uri uri, ContentValues values) {
+        //Log_OC.d(TAG, "Inserting " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this);
+        Uri newUri = null;
+        SQLiteDatabase db = mDbHelper.getWritableDatabase();
+        db.beginTransaction();
+        try {
+            newUri = insert(db, uri, values);
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+        getContext().getContentResolver().notifyChange(newUri, null);
+        return newUri;
+    }
+    
+    private Uri insert(SQLiteDatabase db, Uri uri, ContentValues values) {
         if (mUriMatcher.match(uri) != SINGLE_FILE &&
         if (mUriMatcher.match(uri) != SINGLE_FILE &&
-            mUriMatcher.match(uri) != ROOT_DIRECTORY) {
-            
+                mUriMatcher.match(uri) != ROOT_DIRECTORY) {
+            //Log_OC.e(TAG, "Inserting invalid URI: " + uri);
             throw new IllegalArgumentException("Unknown uri id: " + uri);
             throw new IllegalArgumentException("Unknown uri id: " + uri);
         }
         }
 
 
-        SQLiteDatabase db = mDbHelper.getWritableDatabase();
-        long rowId = db.insert(ProviderTableMeta.DB_NAME, null, values);
-        if (rowId > 0) {
-            Uri insertedFileUri = ContentUris.withAppendedId(
-                    ProviderTableMeta.CONTENT_URI_FILE, rowId);
-            getContext().getContentResolver().notifyChange(insertedFileUri,
-                    null);
+        String remotePath = values.getAsString(ProviderTableMeta.FILE_PATH);
+        String accountName = values.getAsString(ProviderTableMeta.FILE_ACCOUNT_OWNER);
+        String[] projection = new String[] {ProviderTableMeta._ID, ProviderTableMeta.FILE_PATH, ProviderTableMeta.FILE_ACCOUNT_OWNER };
+        String where = ProviderTableMeta.FILE_PATH + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
+        String[] whereArgs = new String[] {remotePath, accountName};
+        Cursor doubleCheck = query(db, uri, projection, where, whereArgs, null);
+        if (doubleCheck == null || !doubleCheck.moveToFirst()) {    // ugly patch; serious refactorization is needed to reduce work in FileDataStorageManager and bring it to FileContentProvider 
+            long rowId = db.insert(ProviderTableMeta.DB_NAME, null, values);
+            if (rowId > 0) {
+                Uri insertedFileUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, rowId);
+                //Log_OC.d(TAG, "Inserted " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this);
+                return insertedFileUri;
+            } else {
+                //Log_OC.d(TAG, "Error while inserting " + values.getAsString(ProviderTableMeta.FILE_PATH)  + " at provider " + this);
+                throw new SQLException("ERROR " + uri);
+            }
+        } else {
+            // file is already inserted; race condition, let's avoid a duplicated entry
+            Uri insertedFileUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, doubleCheck.getLong(doubleCheck.getColumnIndex(ProviderTableMeta._ID)));
+            doubleCheck.close();
             return insertedFileUri;
             return insertedFileUri;
         }
         }
-        throw new SQLException("ERROR " + uri);
     }
     }
 
 
+    
     @Override
     @Override
     public boolean onCreate() {
     public boolean onCreate() {
         mDbHelper = new DataBaseHelper(getContext());
         mDbHelper = new DataBaseHelper(getContext());
@@ -160,14 +260,28 @@ public class FileContentProvider extends ContentProvider {
         mUriMatcher.addURI(authority, null, ROOT_DIRECTORY);
         mUriMatcher.addURI(authority, null, ROOT_DIRECTORY);
         mUriMatcher.addURI(authority, "file/", SINGLE_FILE);
         mUriMatcher.addURI(authority, "file/", SINGLE_FILE);
         mUriMatcher.addURI(authority, "file/#", SINGLE_FILE);
         mUriMatcher.addURI(authority, "file/#", SINGLE_FILE);
+        mUriMatcher.addURI(authority, "dir/", DIRECTORY);
         mUriMatcher.addURI(authority, "dir/#", DIRECTORY);
         mUriMatcher.addURI(authority, "dir/#", DIRECTORY);
         
         
         return true;
         return true;
     }
     }
 
 
+    
     @Override
     @Override
-    public Cursor query(Uri uri, String[] projection, String selection,
-            String[] selectionArgs, String sortOrder) {
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+        Cursor result = null;
+        SQLiteDatabase db = mDbHelper.getReadableDatabase();
+        db.beginTransaction();
+        try {
+            result = query(db, uri, projection, selection, selectionArgs, sortOrder);
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+        return result;
+    }
+    
+    private Cursor query(SQLiteDatabase db, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
         SQLiteQueryBuilder sqlQuery = new SQLiteQueryBuilder();
         SQLiteQueryBuilder sqlQuery = new SQLiteQueryBuilder();
 
 
         sqlQuery.setTables(ProviderTableMeta.DB_NAME);
         sqlQuery.setTables(ProviderTableMeta.DB_NAME);
@@ -177,8 +291,9 @@ public class FileContentProvider extends ContentProvider {
         case ROOT_DIRECTORY:
         case ROOT_DIRECTORY:
             break;
             break;
         case DIRECTORY:
         case DIRECTORY:
+            String folderId = uri.getPathSegments().get(1);
             sqlQuery.appendWhere(ProviderTableMeta.FILE_PARENT + "="
             sqlQuery.appendWhere(ProviderTableMeta.FILE_PARENT + "="
-                    + uri.getPathSegments().get(1));
+                    + folderId);
             break;
             break;
         case SINGLE_FILE:
         case SINGLE_FILE:
             if (uri.getPathSegments().size() > 1) {
             if (uri.getPathSegments().size() > 1) {
@@ -197,24 +312,115 @@ public class FileContentProvider extends ContentProvider {
             order = sortOrder;
             order = sortOrder;
         }
         }
 
 
-        SQLiteDatabase db = mDbHelper.getReadableDatabase();
         // DB case_sensitive
         // DB case_sensitive
         db.execSQL("PRAGMA case_sensitive_like = true");
         db.execSQL("PRAGMA case_sensitive_like = true");
-        Cursor c = sqlQuery.query(db, projection, selection, selectionArgs,
-                null, null, order);
-
+        Cursor c = sqlQuery.query(db, projection, selection, selectionArgs, null, null, order);
         c.setNotificationUri(getContext().getContentResolver(), uri);
         c.setNotificationUri(getContext().getContentResolver(), uri);
-
         return c;
         return c;
     }
     }
 
 
     @Override
     @Override
-    public int update(Uri uri, ContentValues values, String selection,
-            String[] selectionArgs) {
-        return mDbHelper.getWritableDatabase().update(
-                ProviderTableMeta.DB_NAME, values, selection, selectionArgs);
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        
+        //Log_OC.d(TAG, "Updating " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this);
+        int count = 0;
+        SQLiteDatabase db = mDbHelper.getWritableDatabase();
+        db.beginTransaction();
+        try {
+            count = update(db, uri, values, selection, selectionArgs);
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+        getContext().getContentResolver().notifyChange(uri, null);
+        return count;
+    }
+    
+    
+    private int update(SQLiteDatabase db, Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        switch (mUriMatcher.match(uri)) {
+            case DIRECTORY:
+                return updateFolderSize(db, selectionArgs[0]);
+            default:
+                return db.update(ProviderTableMeta.DB_NAME, values, selection, selectionArgs);
+        }
+    }    
+
+    
+    private int updateFolderSize(SQLiteDatabase db, String folderId) {
+        int count = 0;
+        String [] whereArgs = new String[] { folderId };
+        
+        // read current size saved for the folder 
+        long folderSize = 0;
+        long folderParentId = -1;
+        Uri selectFolderUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, folderId);
+        String[] folderProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH,  ProviderTableMeta.FILE_PARENT};
+        String folderWhere = ProviderTableMeta._ID + "=?";
+        Cursor folderCursor = query(db, selectFolderUri, folderProjection, folderWhere, whereArgs, null);
+        if (folderCursor != null && folderCursor.moveToFirst()) {
+            folderSize = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH));;
+            folderParentId = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_PARENT));;
+        }
+        folderCursor.close();
+        
+        // read and sum sizes of children
+        long childrenSize = 0;
+        Uri selectChildrenUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, folderId);
+        String[] childrenProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH,  ProviderTableMeta.FILE_PARENT};
+        String childrenWhere = ProviderTableMeta.FILE_PARENT + "=?";
+        Cursor childrenCursor = query(db, selectChildrenUri, childrenProjection, childrenWhere, whereArgs, null);
+        if (childrenCursor != null && childrenCursor.moveToFirst()) {
+            while (!childrenCursor.isAfterLast()) {
+                childrenSize += childrenCursor.getLong(childrenCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH));
+                childrenCursor.moveToNext();
+            }
+        }
+        childrenCursor.close();
+        
+        // update if needed
+        if (folderSize != childrenSize) {
+            Log_OC.d("FileContentProvider", "Updating " + folderSize + " to " + childrenSize);
+            ContentValues cv = new ContentValues();
+            cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, childrenSize);
+            count = db.update(ProviderTableMeta.DB_NAME, cv, folderWhere, whereArgs);
+            
+            // propagate update until root
+            if (folderParentId > FileDataStorageManager.ROOT_PARENT_ID) {
+                Log_OC.d("FileContentProvider", "Propagating update to " + folderParentId);
+                updateFolderSize(db, String.valueOf(folderParentId));
+            } else {
+                Log_OC.d("FileContentProvider", "NOT propagating to " + folderParentId);
+            }
+        } else {
+            Log_OC.d("FileContentProvider", "NOT updating, sizes are " + folderSize + " and " + childrenSize);
+        }
+        return count;
+    }
+
+    
+    @Override
+    public ContentProviderResult[] applyBatch (ArrayList<ContentProviderOperation> operations) throws OperationApplicationException {
+        Log_OC.d("FileContentProvider", "applying batch in provider " + this + " (temporary: " + isTemporary() + ")" );
+        ContentProviderResult[] results = new ContentProviderResult[operations.size()];
+        int i=0;
+        
+        SQLiteDatabase db = mDbHelper.getWritableDatabase();
+        db.beginTransaction();  // it's supposed that transactions can be nested
+        try {
+            for (ContentProviderOperation operation : operations) {
+                results[i] = operation.apply(this, results, i);
+                i++;
+            }
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+        Log_OC.d("FileContentProvider", "applied batch in provider " + this);
+        return results;
     }
     }
 
 
+
     class DataBaseHelper extends SQLiteOpenHelper {
     class DataBaseHelper extends SQLiteOpenHelper {
 
 
         public DataBaseHelper(Context context) {
         public DataBaseHelper(Context context) {
@@ -240,7 +446,8 @@ public class FileContentProvider extends ContentProvider {
                     + ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, "
                     + ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, "
                     + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, "
                     + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, "
                     + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER, "
                     + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER, "
-                    + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER );"
+                    + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER, "
+                    + ProviderTableMeta.FILE_ETAG + " TEXT );"
                     );
                     );
         }
         }
 
 
@@ -294,8 +501,24 @@ public class FileContentProvider extends ContentProvider {
             }
             }
             if (!upgraded)
             if (!upgraded)
                 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion);
                 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion);
+        
+            if (oldVersion < 5 && newVersion >= 5) {
+                Log_OC.i("SQL", "Entering in the #4 ADD in onUpgrade");
+                db.beginTransaction();
+                try {
+                    db .execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
+                            " ADD COLUMN " + ProviderTableMeta.FILE_ETAG + " TEXT " +
+                            " DEFAULT NULL");
+                    
+                    upgraded = true;
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                }
+            }
+            if (!upgraded)
+                Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion);
         }
         }
-
     }
     }
 
 
 }
 }

+ 18 - 57
src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java

@@ -19,17 +19,14 @@
 package com.owncloud.android.syncadapter;
 package com.owncloud.android.syncadapter;
 
 
 import java.io.IOException;
 import java.io.IOException;
-import java.util.Date;
 
 
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.ClientProtocolException;
-import org.apache.http.conn.ConnectionKeepAliveStrategy;
-import org.apache.http.protocol.HttpContext;
 
 
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;
 import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;
-import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.network.OwnCloudClientUtils;
 import com.owncloud.android.network.OwnCloudClientUtils;
 
 
 import android.accounts.Account;
 import android.accounts.Account;
@@ -42,11 +39,13 @@ import android.content.Context;
 import eu.alefzero.webdav.WebdavClient;
 import eu.alefzero.webdav.WebdavClient;
 
 
 /**
 /**
- * Base SyncAdapter for OwnCloud Designed to be subclassed for the concrete
- * SyncAdapter, like ConcatsSync, CalendarSync, FileSync etc..
+ * Base synchronization adapter for ownCloud designed to be subclassed for different
+ * resource types, like FileSync, ConcatsSync, CalendarSync, etc..
  * 
  * 
- * @author sassman
+ * Implements the standard {@link AbstractThreadedSyncAdapter}.
  * 
  * 
+ * @author sassman
+ * @author David A. Velasco
  */
  */
 public abstract class AbstractOwnCloudSyncAdapter extends
 public abstract class AbstractOwnCloudSyncAdapter extends
         AbstractThreadedSyncAdapter {
         AbstractThreadedSyncAdapter {
@@ -54,8 +53,7 @@ public abstract class AbstractOwnCloudSyncAdapter extends
     private AccountManager accountManager;
     private AccountManager accountManager;
     private Account account;
     private Account account;
     private ContentProviderClient contentProvider;
     private ContentProviderClient contentProvider;
-    private Date lastUpdated;
-    private DataStorageManager mStoreManager;
+    private FileDataStorageManager mStoreManager;
 
 
     private WebdavClient mClient = null;
     private WebdavClient mClient = null;
 
 
@@ -88,60 +86,14 @@ public abstract class AbstractOwnCloudSyncAdapter extends
         this.contentProvider = contentProvider;
         this.contentProvider = contentProvider;
     }
     }
 
 
-    public Date getLastUpdated() {
-        return lastUpdated;
-    }
-
-    public void setLastUpdated(Date lastUpdated) {
-        this.lastUpdated = lastUpdated;
-    }
-
-    public void setStorageManager(DataStorageManager storage_manager) {
+    public void setStorageManager(FileDataStorageManager storage_manager) {
         mStoreManager = storage_manager;
         mStoreManager = storage_manager;
     }
     }
 
 
-    public DataStorageManager getStorageManager() {
+    public FileDataStorageManager getStorageManager() {
         return mStoreManager;
         return mStoreManager;
     }
     }
 
 
-    protected ConnectionKeepAliveStrategy getKeepAliveStrategy() {
-        return new ConnectionKeepAliveStrategy() {
-            public long getKeepAliveDuration(HttpResponse response,
-                    HttpContext context) {
-                // Change keep alive straategy basing on response: ie
-                // forbidden/not found/etc
-                // should have keep alive 0
-                // default return: 5s
-                int statusCode = response.getStatusLine().getStatusCode();
-
-                // HTTP 400, 500 Errors as well as HTTP 118 - Connection timed
-                // out
-                if ((statusCode >= 400 && statusCode <= 418)
-                        || (statusCode >= 421 && statusCode <= 426)
-                        || (statusCode >= 500 && statusCode <= 510)
-                        || statusCode == 118) {
-                    return 0;
-                }
-
-                return 5 * 1000;
-            }
-        };
-    }
-
-    protected HttpResponse fireRawRequest(HttpRequest query)
-            throws ClientProtocolException, OperationCanceledException,
-            AuthenticatorException, IOException {
-        /*
-         * BasicHttpContext httpContext = new BasicHttpContext(); BasicScheme
-         * basicAuth = new BasicScheme();
-         * httpContext.setAttribute("preemptive-auth", basicAuth);
-         * 
-         * HttpResponse response = getClient().execute(mHost, query,
-         * httpContext);
-         */
-        return null;
-    }
-
     protected void initClientForCurrentAccount() throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
     protected void initClientForCurrentAccount() throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
         AccountUtils.constructFullURLForAccount(getContext(), account);
         AccountUtils.constructFullURLForAccount(getContext(), account);
         mClient = OwnCloudClientUtils.createOwnCloudClient(account, getContext());
         mClient = OwnCloudClientUtils.createOwnCloudClient(account, getContext());
@@ -150,4 +102,13 @@ public abstract class AbstractOwnCloudSyncAdapter extends
     protected WebdavClient getClient() {
     protected WebdavClient getClient() {
         return mClient;
         return mClient;
     }
     }
+    
+    
+    /* method called by ContactSyncAdapter, that is never used */
+    protected HttpResponse fireRawRequest(HttpRequest query)
+            throws ClientProtocolException, OperationCanceledException,
+            AuthenticatorException, IOException {
+        return null;
+    }
+
 }
 }

+ 95 - 46
src/com/owncloud/android/syncadapter/FileSyncAdapter.java

@@ -30,7 +30,6 @@ import com.owncloud.android.Log_OC;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
 import com.owncloud.android.authentication.AuthenticatorActivity;
 import com.owncloud.android.authentication.AuthenticatorActivity;
-import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.operations.RemoteOperationResult;
 import com.owncloud.android.operations.RemoteOperationResult;
@@ -44,6 +43,7 @@ import android.accounts.AccountsException;
 import android.app.Notification;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.PendingIntent;
+import android.content.AbstractThreadedSyncAdapter;
 import android.content.ContentProviderClient;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Context;
@@ -52,36 +52,60 @@ import android.content.SyncResult;
 import android.os.Bundle;
 import android.os.Bundle;
 
 
 /**
 /**
- * SyncAdapter implementation for syncing sample SyncAdapter contacts to the
- * platform ContactOperations provider.
+ * Implementation of {@link AbstractThreadedSyncAdapter} responsible for synchronizing 
+ * ownCloud files.
+ * 
+ * Performs a full synchronization of the account recieved in {@link #onPerformSync(Account, Bundle, String, ContentProviderClient, SyncResult)}.
  * 
  * 
  * @author Bartek Przybylski
  * @author Bartek Przybylski
  * @author David A. Velasco
  * @author David A. Velasco
  */
  */
 public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
 public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
 
 
-    private final static String TAG = "FileSyncAdapter";
+    private final static String TAG = FileSyncAdapter.class.getSimpleName();
 
 
-    /** 
-     * Maximum number of failed folder synchronizations that are supported before finishing the synchronization operation
-     */
+    /** Maximum number of failed folder synchronizations that are supported before finishing the synchronization operation */
     private static final int MAX_FAILED_RESULTS = 3; 
     private static final int MAX_FAILED_RESULTS = 3; 
     
     
+    
+    /** Time stamp for the current synchronization process, used to distinguish fresh data */
     private long mCurrentSyncTime;
     private long mCurrentSyncTime;
+    
+    /** Flag made 'true' when a request to cancel the synchronization is received */
     private boolean mCancellation;
     private boolean mCancellation;
+    
+    /** When 'true' the process was requested by the user through the user interface; when 'false', it was requested automatically by the system */
     private boolean mIsManualSync;
     private boolean mIsManualSync;
-    private int mFailedResultsCounter;    
+    
+    /** Counter for failed operations in the synchronization process */
+    private int mFailedResultsCounter;
+    
+    /** Result of the last failed operation */
     private RemoteOperationResult mLastFailedResult;
     private RemoteOperationResult mLastFailedResult;
-    private SyncResult mSyncResult;
+    
+    /** Counter of conflicts found between local and remote files */
     private int mConflictsFound;
     private int mConflictsFound;
+    
+    /** Counter of failed operations in synchronization of kept-in-sync files */
     private int mFailsInFavouritesFound;
     private int mFailsInFavouritesFound;
+    
+    /** Map of remote and local paths to files that where locally stored in a location out of the ownCloud folder and couldn't be copied automatically into it */
     private Map<String, String> mForgottenLocalFiles;
     private Map<String, String> mForgottenLocalFiles;
 
 
+    /** {@link SyncResult} instance to return to the system when the synchronization finish */
+    private SyncResult mSyncResult;
+    
     
     
+    /**
+     * Creates an {@link FileSyncAdapter}
+     *
+     * {@inheritDoc}
+     */
     public FileSyncAdapter(Context context, boolean autoInitialize) {
     public FileSyncAdapter(Context context, boolean autoInitialize) {
         super(context, autoInitialize);
         super(context, autoInitialize);
     }
     }
 
 
+    
     /**
     /**
      * {@inheritDoc}
      * {@inheritDoc}
      */
      */
@@ -99,20 +123,20 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
         mForgottenLocalFiles = new HashMap<String, String>();
         mForgottenLocalFiles = new HashMap<String, String>();
         mSyncResult = syncResult;
         mSyncResult = syncResult;
         mSyncResult.fullSyncRequested = false;
         mSyncResult.fullSyncRequested = false;
-        mSyncResult.delayUntil = 60*60*24; // sync after 24h
+        mSyncResult.delayUntil = 60*60*24; // avoid too many automatic synchronizations
 
 
         this.setAccount(account);
         this.setAccount(account);
         this.setContentProvider(provider);
         this.setContentProvider(provider);
-        this.setStorageManager(new FileDataStorageManager(account, getContentProvider()));
+        this.setStorageManager(new FileDataStorageManager(account, provider));
         try {
         try {
             this.initClientForCurrentAccount();
             this.initClientForCurrentAccount();
         } catch (IOException e) {
         } catch (IOException e) {
-            /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again
+            /// the account is unknown for the Synchronization Manager, unreachable this context, or can not be authenticated; don't try this again
             mSyncResult.tooManyRetries = true;
             mSyncResult.tooManyRetries = true;
             notifyFailedSynchronization();
             notifyFailedSynchronization();
             return;
             return;
         } catch (AccountsException e) {
         } catch (AccountsException e) {
-            /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again
+            /// the account is unknown for the Synchronization Manager, unreachable this context, or can not be authenticated; don't try this again
             mSyncResult.tooManyRetries = true;
             mSyncResult.tooManyRetries = true;
             notifyFailedSynchronization();
             notifyFailedSynchronization();
             return;
             return;
@@ -125,10 +149,10 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
             updateOCVersion();
             updateOCVersion();
             mCurrentSyncTime = System.currentTimeMillis();
             mCurrentSyncTime = System.currentTimeMillis();
             if (!mCancellation) {
             if (!mCancellation) {
-                fetchData(OCFile.PATH_SEPARATOR, DataStorageManager.ROOT_PARENT_ID);
+                synchronizeFolder(getStorageManager().getFileByPath(OCFile.ROOT_PATH));
                 
                 
             } else {
             } else {
-                Log_OC.d(TAG, "Leaving synchronization before any remote request due to cancellation was requested");
+                Log_OC.d(TAG, "Leaving synchronization before synchronizing the root folder because cancelation request");
             }
             }
             
             
             
             
@@ -142,14 +166,12 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
                 
                 
                 /// notify the user about the failure of MANUAL synchronization
                 /// notify the user about the failure of MANUAL synchronization
                 notifyFailedSynchronization();
                 notifyFailedSynchronization();
-                
             }
             }
             if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
             if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
                 notifyFailsInFavourites();
                 notifyFailsInFavourites();
             }
             }
             if (mForgottenLocalFiles.size() > 0) {
             if (mForgottenLocalFiles.size() > 0) {
                 notifyForgottenLocalFiles();
                 notifyForgottenLocalFiles();
-                
             }
             }
             sendStickyBroadcast(false, null, mLastFailedResult);        // message to signal the end to the UI
             sendStickyBroadcast(false, null, mLastFailedResult);        // message to signal the end to the UI
         }
         }
@@ -159,8 +181,12 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
     /**
     /**
      * Called by system SyncManager when a synchronization is required to be cancelled.
      * Called by system SyncManager when a synchronization is required to be cancelled.
      * 
      * 
-     * Sets the mCancellation flag to 'true'. THe synchronization will be stopped when before a new folder is fetched. Data of the last folder
-     * fetched will be still saved in the database. See onPerformSync implementation.
+     * Sets the mCancellation flag to 'true'. THe synchronization will be stopped later, 
+     * before a new folder is fetched. Data of the last folder synchronized will be still 
+     * locally saved. 
+     * 
+     * See {@link #onPerformSync(Account, Bundle, String, ContentProviderClient, SyncResult)}
+     * and {@link #synchronizeFolder(String, long)}.
      */
      */
     @Override
     @Override
     public void onSyncCanceled() {
     public void onSyncCanceled() {
@@ -183,20 +209,36 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
     
     
     
     
     /**
     /**
-     * Synchronize the properties of files and folders contained in a remote folder given by remotePath.
+     *  Synchronizes the list of files contained in a folder identified with its remote path.
+     *  
+     *  Fetches the list and properties of the files contained in the given folder, including their 
+     *  properties, and updates the local database with them.
+     *  
+     *  Enters in the child folders to synchronize their contents also, following a recursive
+     *  depth first strategy. 
      * 
      * 
-     * @param remotePath        Remote path to the folder to synchronize.
-     * @param parentId          Database Id of the folder to synchronize.
+     *  @param folder                   Folder to synchronize.
      */
      */
-    private void fetchData(String remotePath, long parentId) {
+    private void synchronizeFolder(OCFile folder) {
         
         
         if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult))
         if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult))
             return;
             return;
         
         
-        // perform folder synchronization
-        SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation(  remotePath, 
+        /*
+        OCFile folder, 
+        long currentSyncTime, 
+        boolean updateFolderProperties,
+        boolean syncFullAccount,
+        DataStorageManager dataStorageManager, 
+        Account account, 
+        Context context ) {
+            
+        }
+        */
+        // folder synchronization
+        SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation(  folder, 
                                                                                     mCurrentSyncTime, 
                                                                                     mCurrentSyncTime, 
-                                                                                    parentId, 
+                                                                                    true,
                                                                                     getStorageManager(), 
                                                                                     getStorageManager(), 
                                                                                     getAccount(), 
                                                                                     getAccount(), 
                                                                                     getContext()
                                                                                     getContext()
@@ -205,8 +247,9 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
         
         
         
         
         // synchronized folder -> notice to UI - ALWAYS, although !result.isSuccess
         // synchronized folder -> notice to UI - ALWAYS, although !result.isSuccess
-        sendStickyBroadcast(true, remotePath, null);
+        sendStickyBroadcast(true, folder.getRemotePath(), null);
         
         
+        // check the result of synchronizing the folder
         if (result.isSuccess() || result.getCode() == ResultCode.SYNC_CONFLICT) {
         if (result.isSuccess() || result.getCode() == ResultCode.SYNC_CONFLICT) {
             
             
             if (result.getCode() == ResultCode.SYNC_CONFLICT) {
             if (result.getCode() == ResultCode.SYNC_CONFLICT) {
@@ -216,15 +259,15 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
             if (synchFolderOp.getForgottenLocalFiles().size() > 0) {
             if (synchFolderOp.getForgottenLocalFiles().size() > 0) {
                 mForgottenLocalFiles.putAll(synchFolderOp.getForgottenLocalFiles());
                 mForgottenLocalFiles.putAll(synchFolderOp.getForgottenLocalFiles());
             }
             }
-            // synchronize children folders 
-            List<OCFile> children = synchFolderOp.getChildren();
-            fetchChildren(children);    // beware of the 'hidden' recursion here!
-            
-            sendStickyBroadcast(true, remotePath, null);
+            if (result.isSuccess()) {
+                // synchronize children folders 
+                List<OCFile> children = synchFolderOp.getChildren();
+                fetchChildren(folder, children, synchFolderOp.getRemoteFolderChanged());    // beware of the 'hidden' recursion here!
+            }
             
             
         } else {
         } else {
+            // in failures, the statistics for the global result are updated
             if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED ||
             if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED ||
-                   // (result.isTemporalRedirection() && result.isIdPRedirection() &&
                     ( result.isIdPRedirection() && 
                     ( result.isIdPRedirection() && 
                             MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))) {
                             MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))) {
                 mSyncResult.stats.numAuthExceptions++;
                 mSyncResult.stats.numAuthExceptions++;
@@ -260,23 +303,31 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
     }
     }
 
 
     /**
     /**
-     * Synchronize data of folders in the list of received files
+     * Triggers the synchronization of any folder contained in the list of received files.
      * 
      * 
-     * @param files         Files to recursively fetch 
+     * @param files         Files to recursively synchronize.
      */
      */
-    private void fetchChildren(List<OCFile> files) {
+    private void fetchChildren(OCFile parent, List<OCFile> files, boolean parentEtagChanged) {
         int i;
         int i;
+        OCFile newFile = null;
+        String etag = null;
+        boolean syncDown = false;
         for (i=0; i < files.size() && !mCancellation; i++) {
         for (i=0; i < files.size() && !mCancellation; i++) {
-            OCFile newFile = files.get(i);
-            if (newFile.isDirectory()) {
-                fetchData(newFile.getRemotePath(), newFile.getFileId());
-                
-                // Update folder size on DB
-                getStorageManager().calculateFolderSize(newFile.getFileId());                   
+            newFile = files.get(i);
+            if (newFile.isFolder()) {
+                /*
+                etag = newFile.getEtag();
+                syncDown = (parentEtagChanged || etag == null || etag.length() == 0);
+                if(syncDown) { */
+                    synchronizeFolder(newFile);
+                    // update the size of the parent folder again after recursive synchronization 
+                    //getStorageManager().updateFolderSize(parent.getFileId());  
+                    sendStickyBroadcast(true, parent.getRemotePath(), null);        // notify again to refresh size in UI
+                //}
             }
             }
         }
         }
        
        
-        if (mCancellation && i <files.size()) Log_OC.d(TAG, "Leaving synchronization before synchronizing " + files.get(i).getRemotePath() + " because cancelation request");
+        if (mCancellation && i <files.size()) Log_OC.d(TAG, "Leaving synchronization before synchronizing " + files.get(i).getRemotePath() + " due to cancelation request");
     }
     }
 
 
     
     
@@ -287,9 +338,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
      * @param dirRemotePath     Remote path of a folder that was just synchronized (with or without success)
      * @param dirRemotePath     Remote path of a folder that was just synchronized (with or without success)
      */
      */
     private void sendStickyBroadcast(boolean inProgress, String dirRemotePath, RemoteOperationResult result) {
     private void sendStickyBroadcast(boolean inProgress, String dirRemotePath, RemoteOperationResult result) {
-        FileSyncService fileSyncService = new FileSyncService();
-        
-        Intent i = new Intent(fileSyncService.getSyncMessage());
+        Intent i = new Intent(FileSyncService.getSyncMessage());
         i.putExtra(FileSyncService.IN_PROGRESS, inProgress);
         i.putExtra(FileSyncService.IN_PROGRESS, inProgress);
         i.putExtra(FileSyncService.ACCOUNT_NAME, getAccount().name);
         i.putExtra(FileSyncService.ACCOUNT_NAME, getAccount().name);
         if (dirRemotePath != null) {
         if (dirRemotePath != null) {

+ 9 - 5
src/com/owncloud/android/syncadapter/FileSyncService.java

@@ -22,20 +22,23 @@ import android.content.Intent;
 import android.os.IBinder;
 import android.os.IBinder;
 
 
 /**
 /**
- * Background service for syncing files to our local Database
+ * Background service for synchronizing remote files with their local state.
  * 
  * 
- * @author Bartek Przybylski
+ * Serves as a connector to an instance of {@link FileSyncAdapter}, as required by standard Android APIs. 
  * 
  * 
+ * @author Bartek Przybylski
+ * @author David A. Velasco
  */
  */
 public class FileSyncService extends Service {
 public class FileSyncService extends Service {
-    public static final String SYNC_MESSAGE = "ACCOUNT_SYNC";
+    
+    private static final String SYNC_MESSAGE = "ACCOUNT_SYNC";
     public static final String SYNC_FOLDER_REMOTE_PATH = "SYNC_FOLDER_REMOTE_PATH";
     public static final String SYNC_FOLDER_REMOTE_PATH = "SYNC_FOLDER_REMOTE_PATH";
     public static final String IN_PROGRESS = "SYNC_IN_PROGRESS";
     public static final String IN_PROGRESS = "SYNC_IN_PROGRESS";
     public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
     public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
     public static final String SYNC_RESULT = "SYNC_RESULT";
     public static final String SYNC_RESULT = "SYNC_RESULT";
 
 
-    public String getSyncMessage(){
-        return getClass().getName().toString() + SYNC_MESSAGE;
+    public static String getSyncMessage(){
+        return FileSyncService.class.getName().toString() + SYNC_MESSAGE;
     }
     }
     /*
     /*
      * {@inheritDoc}
      * {@inheritDoc}
@@ -51,4 +54,5 @@ public class FileSyncService extends Service {
     public IBinder onBind(Intent intent) {
     public IBinder onBind(Intent intent) {
        return new FileSyncAdapter(getApplicationContext(), true).getSyncAdapterBinder();
        return new FileSyncAdapter(getApplicationContext(), true).getSyncAdapterBinder();
     }
     }
+    
 }
 }

+ 1 - 2
src/com/owncloud/android/ui/activity/ConflictsResolveActivity.java

@@ -19,7 +19,6 @@
 package com.owncloud.android.ui.activity;
 package com.owncloud.android.ui.activity;
 
 
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.Log_OC;
-import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.files.services.FileUploader;
@@ -81,7 +80,7 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict
                 finish();
                 finish();
             } else {
             } else {
                 /// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
                 /// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
-                DataStorageManager storageManager = new FileDataStorageManager(getAccount(), getContentResolver());
+                FileDataStorageManager storageManager = new FileDataStorageManager(getAccount(), getContentResolver());
                 file = storageManager.getFileByPath(file.getRemotePath());   // file = null if not in the current Account
                 file = storageManager.getFileByPath(file.getRemotePath());   // file = null if not in the current Account
                 if (file != null) {
                 if (file != null) {
                     setFile(file);
                     setFile(file);

+ 180 - 80
src/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -22,8 +22,8 @@ import java.io.File;
 
 
 import android.accounts.Account;
 import android.accounts.Account;
 import android.app.AlertDialog;
 import android.app.AlertDialog;
-import android.app.ProgressDialog;
 import android.app.Dialog;
 import android.app.Dialog;
+import android.app.ProgressDialog;
 import android.content.BroadcastReceiver;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentResolver;
@@ -60,7 +60,6 @@ import com.actionbarsherlock.view.Window;
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
-import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileDownloader;
 import com.owncloud.android.files.services.FileDownloader;
@@ -72,21 +71,21 @@ import com.owncloud.android.operations.CreateFolderOperation;
 import com.owncloud.android.operations.OnRemoteOperationListener;
 import com.owncloud.android.operations.OnRemoteOperationListener;
 import com.owncloud.android.operations.RemoteOperation;
 import com.owncloud.android.operations.RemoteOperation;
 import com.owncloud.android.operations.RemoteOperationResult;
 import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
 import com.owncloud.android.operations.RemoveFileOperation;
 import com.owncloud.android.operations.RemoveFileOperation;
 import com.owncloud.android.operations.RenameFileOperation;
 import com.owncloud.android.operations.RenameFileOperation;
 import com.owncloud.android.operations.SynchronizeFileOperation;
 import com.owncloud.android.operations.SynchronizeFileOperation;
-import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.operations.SynchronizeFolderOperation;
 import com.owncloud.android.syncadapter.FileSyncService;
 import com.owncloud.android.syncadapter.FileSyncService;
 import com.owncloud.android.ui.dialog.EditNameDialog;
 import com.owncloud.android.ui.dialog.EditNameDialog;
+import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
 import com.owncloud.android.ui.dialog.LoadingDialog;
 import com.owncloud.android.ui.dialog.LoadingDialog;
 import com.owncloud.android.ui.dialog.SslValidatorDialog;
 import com.owncloud.android.ui.dialog.SslValidatorDialog;
-import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
 import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
 import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
 import com.owncloud.android.ui.fragment.FileDetailFragment;
 import com.owncloud.android.ui.fragment.FileDetailFragment;
 import com.owncloud.android.ui.fragment.FileFragment;
 import com.owncloud.android.ui.fragment.FileFragment;
 import com.owncloud.android.ui.fragment.OCFileListFragment;
 import com.owncloud.android.ui.fragment.OCFileListFragment;
 import com.owncloud.android.ui.preview.PreviewImageActivity;
 import com.owncloud.android.ui.preview.PreviewImageActivity;
-import com.owncloud.android.ui.preview.PreviewImageFragment;
 import com.owncloud.android.ui.preview.PreviewMediaFragment;
 import com.owncloud.android.ui.preview.PreviewMediaFragment;
 import com.owncloud.android.ui.preview.PreviewVideoActivity;
 import com.owncloud.android.ui.preview.PreviewVideoActivity;
 
 
@@ -103,7 +102,7 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
     private ArrayAdapter<String> mDirectories;
     private ArrayAdapter<String> mDirectories;
 
 
     /** Access point to the cached database for the current ownCloud {@link Account} */
     /** Access point to the cached database for the current ownCloud {@link Account} */
-    private DataStorageManager mStorageManager = null;
+    private FileDataStorageManager mStorageManager = null;
 
 
     private SyncBroadcastReceiver mSyncBroadcastReceiver;
     private SyncBroadcastReceiver mSyncBroadcastReceiver;
     private UploadFinishReceiver mUploadFinishReceiver;
     private UploadFinishReceiver mUploadFinishReceiver;
@@ -118,6 +117,7 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
     private View mRightFragmentContainer;
     private View mRightFragmentContainer;
 
 
     private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
     private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
+    private static final String KEY_SYNC_IN_PROGRESS = "SYNC_IN_PROGRESS";
 
 
     public static final int DIALOG_SHORT_WAIT = 0;
     public static final int DIALOG_SHORT_WAIT = 0;
     private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 1;
     private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 1;
@@ -139,8 +139,7 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
     private OCFile mWaitingToPreview;
     private OCFile mWaitingToPreview;
     private Handler mHandler;
     private Handler mHandler;
     
     
-    private String mDownloadAddedMessage;
-    private String mDownloadFinishMessage;
+    private boolean mSyncInProgress = false;
 
 
     @Override
     @Override
     protected void onCreate(Bundle savedInstanceState) {
     protected void onCreate(Bundle savedInstanceState) {
@@ -151,10 +150,6 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
 
 
         mHandler = new Handler();
         mHandler = new Handler();
         
         
-        FileDownloader downloader = new FileDownloader();
-        mDownloadAddedMessage = downloader.getDownloadAddedMessage();
-        mDownloadFinishMessage= downloader.getDownloadFinishMessage();
-
         /// bindings to transference services
         /// bindings to transference services
         mUploadConnection = new ListServiceConnection(); 
         mUploadConnection = new ListServiceConnection(); 
         mDownloadConnection = new ListServiceConnection();
         mDownloadConnection = new ListServiceConnection();
@@ -174,10 +169,12 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
         /// Load of saved instance state
         /// Load of saved instance state
         if(savedInstanceState != null) {
         if(savedInstanceState != null) {
             mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW);
             mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW);
-
+            mSyncInProgress = savedInstanceState.getBoolean(KEY_SYNC_IN_PROGRESS);
+           
         } else {
         } else {
             mWaitingToPreview = null;
             mWaitingToPreview = null;
-        }
+            mSyncInProgress = false;
+        }        
 
 
         /// USER INTERFACE
         /// USER INTERFACE
 
 
@@ -193,7 +190,7 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
         // Action bar setup
         // Action bar setup
         mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
         mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
         getSupportActionBar().setHomeButtonEnabled(true);       // mandatory since Android ICS, according to the official documentation
         getSupportActionBar().setHomeButtonEnabled(true);       // mandatory since Android ICS, according to the official documentation
-        setSupportProgressBarIndeterminateVisibility(false);    // always AFTER setContentView(...) ; to work around bug in its implementation
+        setSupportProgressBarIndeterminateVisibility(mSyncInProgress);    // always AFTER setContentView(...) ; to work around bug in its implementation        
         
         
         
         
         
         
@@ -236,27 +233,20 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
             }
             }
             if (file == null) {
             if (file == null) {
                 // fall back to root folder
                 // fall back to root folder
-                file = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);  // never returns null
+                file = mStorageManager.getFileByPath(OCFile.ROOT_PATH);  // never returns null
             }
             }
             setFile(file);
             setFile(file);
-            mDirectories.clear();
-            OCFile fileIt = file;
-            while(fileIt != null && fileIt.getFileName() != OCFile.PATH_SEPARATOR) {
-                if (fileIt.isDirectory()) {
-                    mDirectories.add(fileIt.getFileName());
-                }
-                // get parent from path
-                parentPath = fileIt.getRemotePath().substring(0, fileIt.getRemotePath().lastIndexOf(fileIt.getFileName()));
-                fileIt = mStorageManager.getFileByPath(parentPath);
-            }
-            mDirectories.add(OCFile.PATH_SEPARATOR);
+            setNavigationListWithFolder(file);
             if (!stateWasRecovered) {
             if (!stateWasRecovered) {
                 Log_OC.e(TAG, "Initializing Fragments in onAccountChanged..");
                 Log_OC.e(TAG, "Initializing Fragments in onAccountChanged..");
                 initFragmentsWithFile();
                 initFragmentsWithFile();
+                if (file.isFolder()) {
+                    startSyncFolderOperation(file);
+                }
                 
                 
             } else {
             } else {
-                updateFragmentsVisibility(!file.isDirectory());
-                updateNavigationElementsInActionBar(file.isDirectory() ? null : file);
+                updateFragmentsVisibility(!file.isFolder());
+                updateNavigationElementsInActionBar(file.isFolder() ? null : file);
             }
             }
             
             
             
             
@@ -266,6 +256,23 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
     }
     }
 
 
 
 
+    private void setNavigationListWithFolder(OCFile file) {
+        mDirectories.clear();
+        OCFile fileIt = file;
+        String parentPath;
+        while(fileIt != null && fileIt.getFileName() != OCFile.ROOT_PATH) {
+            if (fileIt.isFolder()) {
+                mDirectories.add(fileIt.getFileName());
+            }
+            //fileIt = mStorageManager.getFileById(fileIt.getParentId());
+            // get parent from path
+            parentPath = fileIt.getRemotePath().substring(0, fileIt.getRemotePath().lastIndexOf(fileIt.getFileName()));
+            fileIt = mStorageManager.getFileByPath(parentPath);
+        }
+        mDirectories.add(OCFile.PATH_SEPARATOR);
+    }
+
+
     private void createMinFragments() {
     private void createMinFragments() {
         OCFileListFragment listOfFiles = new OCFileListFragment();
         OCFileListFragment listOfFiles = new OCFileListFragment();
         FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
         FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
@@ -308,7 +315,7 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
 
 
     private Fragment chooseInitialSecondFragment(OCFile file) {
     private Fragment chooseInitialSecondFragment(OCFile file) {
         Fragment secondFragment = null;
         Fragment secondFragment = null;
-        if (file != null && !file.isDirectory()) {
+        if (file != null && !file.isFolder()) {
             if (file.isDown() && PreviewMediaFragment.canBePreviewed(file) 
             if (file.isDown() && PreviewMediaFragment.canBePreviewed(file) 
                     && file.getLastSyncDateForProperties() > 0  // temporal fix
                     && file.getLastSyncDateForProperties() > 0  // temporal fix
                     ) {
                     ) {
@@ -412,12 +419,12 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
                 // the user browsed to other file ; forget the automatic preview 
                 // the user browsed to other file ; forget the automatic preview 
                 mWaitingToPreview = null;
                 mWaitingToPreview = null;
 
 
-            } else if (downloadEvent.equals(mDownloadAddedMessage)) {
+            } else if (downloadEvent.equals(FileDownloader.getDownloadAddedMessage())) {
                 // grant that the right panel updates the progress bar
                 // grant that the right panel updates the progress bar
                 detailsFragment.listenForTransferProgress();
                 detailsFragment.listenForTransferProgress();
                 detailsFragment.updateFileDetails(true, false);
                 detailsFragment.updateFileDetails(true, false);
 
 
-            } else if (downloadEvent.equals(mDownloadFinishMessage)) {
+            } else if (downloadEvent.equals(FileDownloader.getDownloadFinishMessage())) {
                 //  update the right panel
                 //  update the right panel
                 boolean detailsFragmentChanged = false;
                 boolean detailsFragmentChanged = false;
                 if (waitedPreview) {
                 if (waitedPreview) {
@@ -473,8 +480,9 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
             FileFragment second = getSecondFragment();
             FileFragment second = getSecondFragment();
             OCFile currentDir = getCurrentDir();
             OCFile currentDir = getCurrentDir();
             if((currentDir != null && currentDir.getParentId() != 0) || 
             if((currentDir != null && currentDir.getParentId() != 0) || 
-                    (second != null && second.getFile() != null)) {
+                    (second != null && second.getFile() != null)) {                
                 onBackPressed(); 
                 onBackPressed(); 
+                
             }
             }
             break;
             break;
         }
         }
@@ -496,15 +504,22 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
 
 
     @Override
     @Override
     public boolean onNavigationItemSelected(int itemPosition, long itemId) {
     public boolean onNavigationItemSelected(int itemPosition, long itemId) {
-        int i = itemPosition;
-        while (i-- != 0) {
-            onBackPressed();
-        }
-        // the next operation triggers a new call to this method, but it's necessary to 
-        // ensure that the name exposed in the action bar is the current directory when the 
-        // user selected it in the navigation list
-        if (itemPosition != 0)
+        if (itemPosition != 0) {
+            String targetPath = "";
+            for (int i=itemPosition; i < mDirectories.getCount() - 1; i++) {
+                targetPath = mDirectories.getItem(i) + OCFile.PATH_SEPARATOR + targetPath; 
+            }
+            targetPath = OCFile.PATH_SEPARATOR + targetPath;
+            OCFile targetFolder = mStorageManager.getFileByPath(targetPath);
+            if (targetFolder != null) {
+                browseTo(targetFolder);
+            }
+            
+            // the next operation triggers a new call to this method, but it's necessary to 
+            // ensure that the name exposed in the action bar is the current directory when the 
+            // user selected it in the navigation list
             getSupportActionBar().setSelectedNavigationItem(0);
             getSupportActionBar().setSelectedNavigationItem(0);
+        }
         return true;
         return true;
     }
     }
 
 
@@ -609,14 +624,17 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
                     finish();
                     finish();
                     return;
                     return;
                 }
                 }
-                popDirname();
-                listOfFiles.onBrowseUp();
+                int levelsUp = listOfFiles.onBrowseUp();
+                for (int i=0; i < levelsUp && mDirectories.getCount() > 1 ; i++) {
+                    popDirname();
+                }
             }
             }
         }
         }
         if (listOfFiles != null) {  // should never be null, indeed
         if (listOfFiles != null) {  // should never be null, indeed
             setFile(listOfFiles.getCurrentFile());
             setFile(listOfFiles.getCurrentFile());
         }
         }
         cleanSecondFragment();
         cleanSecondFragment();
+
     }
     }
 
 
     @Override
     @Override
@@ -625,6 +643,8 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
         Log_OC.e(TAG, "onSaveInstanceState() start");
         Log_OC.e(TAG, "onSaveInstanceState() start");
         super.onSaveInstanceState(outState);
         super.onSaveInstanceState(outState);
         outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
         outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
+        outState.putBoolean(FileDisplayActivity.KEY_SYNC_IN_PROGRESS, mSyncInProgress);
+
         Log_OC.d(TAG, "onSaveInstanceState() end");
         Log_OC.d(TAG, "onSaveInstanceState() end");
     }
     }
     
     
@@ -635,22 +655,19 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
         super.onResume();
         super.onResume();
         Log_OC.e(TAG, "onResume() start");
         Log_OC.e(TAG, "onResume() start");
 
 
-        FileUploader fileUploader = new FileUploader();
-        FileSyncService fileSyncService = new FileSyncService();
-        
         // Listen for sync messages
         // Listen for sync messages
-        IntentFilter syncIntentFilter = new IntentFilter(fileSyncService.getSyncMessage());
+        IntentFilter syncIntentFilter = new IntentFilter(FileSyncService.getSyncMessage());
         mSyncBroadcastReceiver = new SyncBroadcastReceiver();
         mSyncBroadcastReceiver = new SyncBroadcastReceiver();
         registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
         registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
 
 
         // Listen for upload messages
         // Listen for upload messages
-        IntentFilter uploadIntentFilter = new IntentFilter(fileUploader.getUploadFinishMessage());
+        IntentFilter uploadIntentFilter = new IntentFilter(FileUploader.getUploadFinishMessage());
         mUploadFinishReceiver = new UploadFinishReceiver();
         mUploadFinishReceiver = new UploadFinishReceiver();
         registerReceiver(mUploadFinishReceiver, uploadIntentFilter);
         registerReceiver(mUploadFinishReceiver, uploadIntentFilter);
 
 
         // Listen for download messages
         // Listen for download messages
-        IntentFilter downloadIntentFilter = new IntentFilter(mDownloadAddedMessage);
-        downloadIntentFilter.addAction(mDownloadFinishMessage);
+        IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.getDownloadAddedMessage());
+        downloadIntentFilter.addAction(FileDownloader.getDownloadFinishMessage());
         mDownloadFinishReceiver = new DownloadFinishReceiver();
         mDownloadFinishReceiver = new DownloadFinishReceiver();
         registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
         registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
     
     
@@ -815,10 +832,10 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
     /**
     /**
      * Pushes a directory to the drop down list
      * Pushes a directory to the drop down list
      * @param directory to push
      * @param directory to push
-     * @throws IllegalArgumentException If the {@link OCFile#isDirectory()} returns false.
+     * @throws IllegalArgumentException If the {@link OCFile#isFolder()} returns false.
      */
      */
     public void pushDirname(OCFile directory) {
     public void pushDirname(OCFile directory) {
-        if(!directory.isDirectory()){
+        if(!directory.isFolder()){
             throw new IllegalArgumentException("Only directories may be pushed!");
             throw new IllegalArgumentException("Only directories may be pushed!");
         }
         }
         mDirectories.insert(directory.getFileName(), 0);
         mDirectories.insert(directory.getFileName(), 0);
@@ -870,8 +887,7 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
         public void onReceive(Context context, Intent intent) {
         public void onReceive(Context context, Intent intent) {
             boolean inProgress = intent.getBooleanExtra(FileSyncService.IN_PROGRESS, false);
             boolean inProgress = intent.getBooleanExtra(FileSyncService.IN_PROGRESS, false);
             String accountName = intent.getStringExtra(FileSyncService.ACCOUNT_NAME);
             String accountName = intent.getStringExtra(FileSyncService.ACCOUNT_NAME);
-
-            Log_OC.d(TAG, "sync of account " + accountName + " is in_progress: " + inProgress);
+            RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);
 
 
             if (getAccount() != null && accountName.equals(getAccount().name)
             if (getAccount() != null && accountName.equals(getAccount().name)
                     && mStorageManager != null
                     && mStorageManager != null
@@ -879,31 +895,39 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
 
 
                 String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH); 
                 String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH); 
 
 
-                boolean fillBlankRoot = false;
-                OCFile currentDir = getCurrentDir();
-                if (currentDir == null) {
-                    currentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
-                    fillBlankRoot = (currentDir != null);                   
-                }
+                OCFile currentFile = (getFile() == null) ? null : mStorageManager.getFileByPath(getFile().getRemotePath());
+                OCFile currentDir = (getCurrentDir() == null) ? null : mStorageManager.getFileByPath(getCurrentDir().getRemotePath());
 
 
-                if ((synchFolderRemotePath != null && currentDir != null && (currentDir.getRemotePath().equals(synchFolderRemotePath)))
-                        || fillBlankRoot ) {
-                    if (!fillBlankRoot) 
-                        currentDir = mStorageManager.getFileByPath(synchFolderRemotePath);
-                    OCFileListFragment fileListFragment = getListOfFilesFragment();
-                    if (fileListFragment != null) {
-                        fileListFragment.listDirectory(currentDir);
+                if (currentDir == null) {
+                    // current folder was removed from the server 
+                    Toast.makeText( FileDisplayActivity.this, 
+                                    String.format(getString(R.string.sync_current_folder_was_removed), mDirectories.getItem(0)), 
+                                    Toast.LENGTH_LONG)
+                        .show();
+                    browseToRoot();
+                    
+                } else {
+                    if (currentFile == null && !getFile().isFolder()) {
+                        // currently selected file was removed in the server, and now we know it
+                        cleanSecondFragment();
+                        currentFile = currentDir;
                     }
                     }
-                    if (getSecondFragment() == null)
-                        setFile(currentDir);
+                
+                    if (synchFolderRemotePath != null && currentDir.getRemotePath().equals(synchFolderRemotePath)) {
+                        OCFileListFragment fileListFragment = getListOfFilesFragment();
+                        if (fileListFragment != null) {
+                            fileListFragment.listDirectory(currentDir);
+                        }
+                    }
+                    setFile(currentFile);
                 }
                 }
                 
                 
                 setSupportProgressBarIndeterminateVisibility(inProgress);
                 setSupportProgressBarIndeterminateVisibility(inProgress);
                 removeStickyBroadcast(intent);
                 removeStickyBroadcast(intent);
+                mSyncInProgress = inProgress;
 
 
             }
             }
-
-            RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);
+            
             if (synchResult != null) {
             if (synchResult != null) {
                 if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {
                 if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {
                     mLastSslUntrustedServerResult = synchResult;
                     mLastSslUntrustedServerResult = synchResult;
@@ -912,7 +936,7 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
             }
             }
         }
         }
     }
     }
-
+    
 
 
     private class UploadFinishReceiver extends BroadcastReceiver {
     private class UploadFinishReceiver extends BroadcastReceiver {
         /**
         /**
@@ -972,11 +996,43 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
      * {@inheritDoc}
      * {@inheritDoc}
      */
      */
     @Override
     @Override
-    public DataStorageManager getStorageManager() {
+    public FileDataStorageManager getStorageManager() {
         return mStorageManager;
         return mStorageManager;
     }
     }
 
 
 
 
+    public void browseToRoot() {
+        OCFileListFragment listOfFiles = getListOfFilesFragment(); 
+        if (listOfFiles != null) {  // should never be null, indeed
+            while (mDirectories.getCount() > 1) {
+                popDirname();
+            }
+            OCFile root = mStorageManager.getFileByPath(OCFile.ROOT_PATH);
+            listOfFiles.listDirectory(root);
+            setFile(listOfFiles.getCurrentFile());
+            startSyncFolderOperation(root);
+        }
+        cleanSecondFragment();
+    }
+    
+    
+    public void browseTo(OCFile folder) {
+        if (folder == null || !folder.isFolder()) {
+            throw new IllegalArgumentException("Trying to browse to invalid folder " + folder);
+        }
+        OCFileListFragment listOfFiles = getListOfFilesFragment(); 
+        if (listOfFiles != null) {
+            setNavigationListWithFolder(folder);
+            listOfFiles.listDirectory(folder);
+            setFile(listOfFiles.getCurrentFile());
+            startSyncFolderOperation(folder);
+        } else {
+            Log_OC.e(TAG, "Unexpected null when accessing list fragment");
+        }
+        cleanSecondFragment();
+    }
+
+
     /**
     /**
      * {@inheritDoc}
      * {@inheritDoc}
      * 
      * 
@@ -986,6 +1042,10 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
     public void onBrowsedDownTo(OCFile directory) {
     public void onBrowsedDownTo(OCFile directory) {
         pushDirname(directory);
         pushDirname(directory);
         cleanSecondFragment();
         cleanSecondFragment();
+        
+        // Sync Folder
+        startSyncFolderOperation(directory);
+        
     }
     }
 
 
     /**
     /**
@@ -1074,6 +1134,19 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
     }
     }
 
 
 
 
+//    private void updateDisplayHomeAtSync(){
+//        ActionBar actionBar = getSupportActionBar();
+//        OCFile currentDir = getCurrentDir();
+//        if (currentDir.getParentId() != DataStorageManager.ROOT_PARENT_ID) {
+//            actionBar.setHomeButtonEnabled(!mSyncInProgress);
+//            actionBar.setDisplayHomeAsUpEnabled(!mSyncInProgress);
+//        }
+//        else {
+//            actionBar.setHomeButtonEnabled(true);
+//            actionBar.setDisplayHomeAsUpEnabled(false);
+//        }
+//    }
+//    
     /**
     /**
      * {@inheritDoc}
      * {@inheritDoc}
      */
      */
@@ -1194,7 +1267,8 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
 
 
         } else if (operation instanceof CreateFolderOperation) {
         } else if (operation instanceof CreateFolderOperation) {
             onCreateFolderOperationFinish((CreateFolderOperation)operation, result);
             onCreateFolderOperationFinish((CreateFolderOperation)operation, result);
-        }
+            
+        } 
     }
     }
 
 
 
 
@@ -1242,7 +1316,6 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
             refeshListOfFilesFragment();
             refeshListOfFilesFragment();
 
 
         } else {
         } else {
-            //dismissDialog(DIALOG_SHORT_WAIT);
             dismissLoadingDialog();
             dismissLoadingDialog();
             try {
             try {
                 Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG); 
                 Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG); 
@@ -1303,10 +1376,7 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
                 i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, getAccount());
                 i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, getAccount());
                 startActivity(i);
                 startActivity(i);
 
 
-            } else {
-                Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG); 
-                msg.show();
-            }
+            } 
 
 
         } else {
         } else {
             if (operation.transferWasRequested()) {
             if (operation.transferWasRequested()) {
@@ -1375,7 +1445,7 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
     private OCFile getCurrentDir() {
     private OCFile getCurrentDir() {
         OCFile file = getFile();
         OCFile file = getFile();
         if (file != null) {
         if (file != null) {
-            if (file.isDirectory()) {
+            if (file.isFolder()) {
                 return file;
                 return file;
             } else if (mStorageManager != null) {
             } else if (mStorageManager != null) {
                 String parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
                 String parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
@@ -1384,5 +1454,35 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
         }
         }
         return null;
         return null;
     }
     }
+    
+    public void startSyncFolderOperation(OCFile folder) {
+        long currentSyncTime = System.currentTimeMillis(); 
+        
+        mSyncInProgress = true;
+                
+        // perform folder synchronization
+        RemoteOperation synchFolderOp = new SynchronizeFolderOperation( folder,  
+                                                                        currentSyncTime, 
+                                                                        false,
+                                                                        getStorageManager(), 
+                                                                        getAccount(), 
+                                                                        getApplicationContext()
+                                                                      );
+        synchFolderOp.execute(getAccount(), this, null, null, this);
+        
+        setSupportProgressBarIndeterminateVisibility(true);
+    }
 
 
+    
+//    public void enableDisableViewGroup(ViewGroup viewGroup, boolean enabled) {
+//        int childCount = viewGroup.getChildCount();
+//        for (int i = 0; i < childCount; i++) {
+//          View view = viewGroup.getChildAt(i);
+//          view.setEnabled(enabled);
+//          view.setClickable(!enabled);
+//          if (view instanceof ViewGroup) {
+//            enableDisableViewGroup((ViewGroup) view, enabled);
+//          }
+//        }
+//      }
 }
 }

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

@@ -31,7 +31,7 @@ import android.widget.TextView;
 import com.owncloud.android.DisplayUtils;
 import com.owncloud.android.DisplayUtils;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
@@ -51,7 +51,7 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
     private Context mContext;
     private Context mContext;
     private OCFile mFile = null;
     private OCFile mFile = null;
     private Vector<OCFile> mFiles = null;
     private Vector<OCFile> mFiles = null;
-    private DataStorageManager mStorageManager;
+    private FileDataStorageManager mStorageManager;
     private Account mAccount;
     private Account mAccount;
     private TransferServiceGetter mTransferServiceGetter;
     private TransferServiceGetter mTransferServiceGetter;
     //total size of a directory (recursive)
     //total size of a directory (recursive)
@@ -135,7 +135,7 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
             TextView lastModV = (TextView) view.findViewById(R.id.last_mod);
             TextView lastModV = (TextView) view.findViewById(R.id.last_mod);
             ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);
             ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);
             
             
-            if (!file.isDirectory()) {
+            if (!file.isFolder()) {
                 fileSizeV.setVisibility(View.VISIBLE);
                 fileSizeV.setVisibility(View.VISIBLE);
                 fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
                 fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
                 lastModV.setVisibility(View.VISIBLE);
                 lastModV.setVisibility(View.VISIBLE);
@@ -195,14 +195,14 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
      * @param directory                 New file to adapt. Can be NULL, meaning "no content to adapt".
      * @param directory                 New file to adapt. Can be NULL, meaning "no content to adapt".
      * @param updatedStorageManager     Optional updated storage manager; used to replace mStorageManager if is different (and not NULL)
      * @param updatedStorageManager     Optional updated storage manager; used to replace mStorageManager if is different (and not NULL)
      */
      */
-    public void swapDirectory(OCFile directory, DataStorageManager updatedStorageManager) {
+    public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager) {
         mFile = directory;
         mFile = directory;
         if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {
         if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {
             mStorageManager = updatedStorageManager;
             mStorageManager = updatedStorageManager;
             mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
             mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
         }
         }
         if (mStorageManager != null) {
         if (mStorageManager != null) {
-            mFiles = mStorageManager.getDirectoryContent(mFile);
+            mFiles = mStorageManager.getFolderContent(mFile);
         } else {
         } else {
             mFiles = null;
             mFiles = null;
         }
         }

+ 4 - 8
src/com/owncloud/android/ui/fragment/FileDetailFragment.java

@@ -207,8 +207,7 @@ public class FileDetailFragment extends FileFragment implements
     public void onResume() {
     public void onResume() {
         super.onResume();
         super.onResume();
         mUploadFinishReceiver = new UploadFinishReceiver();
         mUploadFinishReceiver = new UploadFinishReceiver();
-        FileUploader fileUploader = new FileUploader();
-        IntentFilter filter = new IntentFilter(fileUploader.getUploadFinishMessage());
+        IntentFilter filter = new IntentFilter(FileUploader.getUploadFinishMessage());
         getActivity().registerReceiver(mUploadFinishReceiver, filter);
         getActivity().registerReceiver(mUploadFinishReceiver, filter);
 
 
     }
     }
@@ -417,7 +416,7 @@ public class FileDetailFragment extends FileFragment implements
     private void renameFile() {
     private void renameFile() {
         OCFile file = getFile();
         OCFile file = getFile();
         String fileName = file.getFileName();
         String fileName = file.getFileName();
-        int extensionStart = file.isDirectory() ? -1 : fileName.lastIndexOf(".");
+        int extensionStart = file.isFolder() ? -1 : fileName.lastIndexOf(".");
         int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
         int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
         EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
         EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
         dialog.show(getFragmentManager(), "nameeditdialog");
         dialog.show(getFragmentManager(), "nameeditdialog");
@@ -448,7 +447,7 @@ public class FileDetailFragment extends FileFragment implements
             }
             }
             
             
         } else {
         } else {
-            mLastRemoteOperation = new SynchronizeFileOperation(file, null, mStorageManager, mAccount, true, false, getActivity());
+            mLastRemoteOperation = new SynchronizeFileOperation(file, null, mStorageManager, mAccount, true, getActivity());
             mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
             mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
             
             
             // update ui 
             // update ui 
@@ -837,10 +836,7 @@ public class FileDetailFragment extends FileFragment implements
                 i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mAccount);
                 i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mAccount);
                 startActivity(i);
                 startActivity(i);
                 
                 
-            } else {
-                Toast msg = Toast.makeText(getActivity(), R.string.sync_file_fail_msg, Toast.LENGTH_LONG); 
-                msg.show();
-            }
+            } 
             
             
             if (file.isDown()) {
             if (file.isDown()) {
                 setButtonsForDown();
                 setButtonsForDown();

+ 46 - 26
src/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -24,7 +24,7 @@ import java.util.List;
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.FileHandler;
 import com.owncloud.android.files.FileHandler;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
@@ -121,24 +121,52 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName
 
 
 
 
     /**
     /**
-     * Call this, when the user presses the up button
+     * Call this, when the user presses the up button.
+     * 
+     * Tries to move up the current folder one level. If the parent folder was removed from the database, 
+     * it continues browsing up until finding an existing folders.
+     * 
+     * return       Count of folder levels browsed up.
      */
      */
-    public void onBrowseUp() {
+    public int onBrowseUp() {
         OCFile parentDir = null;
         OCFile parentDir = null;
+        int moveCount = 0;
         
         
         if(mFile != null){
         if(mFile != null){
-            DataStorageManager storageManager = mContainerActivity.getStorageManager();
-            parentDir = storageManager.getFileById(mFile.getParentId());
-            mFile = parentDir;
+            FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
+            
+            String parentPath = null;
+            if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {
+                parentPath = new File(mFile.getRemotePath()).getParent();
+                parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
+                parentDir = storageManager.getFileByPath(parentPath);
+                moveCount++;
+            } else {
+                parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);    // never returns null; keep the path in root folder
+            }
+            while (parentDir == null) {
+                parentPath = new File(parentPath).getParent();
+                parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
+                parentDir = storageManager.getFileByPath(parentPath);
+                moveCount++;
+            }   // exit is granted because storageManager.getFileByPath("/") never returns null
+            mFile = parentDir;           
         }
         }
-        listDirectory(parentDir);
+        
+        if (mFile != null) {
+            listDirectory(mFile);
+
+            mContainerActivity.startSyncFolderOperation(mFile);
+        }   // else - should never happen now
+   
+        return moveCount;
     }
     }
     
     
     @Override
     @Override
     public void onItemClick(AdapterView<?> l, View v, int position, long id) {
     public void onItemClick(AdapterView<?> l, View v, int position, long id) {
         OCFile file = (OCFile) mAdapter.getItem(position);
         OCFile file = (OCFile) mAdapter.getItem(position);
         if (file != null) {
         if (file != null) {
-            if (file.isDirectory()) { 
+            if (file.isFolder()) { 
                 // update state and view of this fragment
                 // update state and view of this fragment
                 listDirectory(file);
                 listDirectory(file);
                 // then, notify parent activity to let it update its state and view, and other fragments
                 // then, notify parent activity to let it update its state and view, and other fragments
@@ -185,7 +213,7 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName
         List<Integer> toDisable = new ArrayList<Integer>();  
         List<Integer> toDisable = new ArrayList<Integer>();  
         
         
         MenuItem item = null;
         MenuItem item = null;
-        if (targetFile.isDirectory()) {
+        if (targetFile.isFolder()) {
             // contextual menu for folders
             // contextual menu for folders
             toHide.add(R.id.action_open_file_with);
             toHide.add(R.id.action_open_file_with);
             toHide.add(R.id.action_download_file);
             toHide.add(R.id.action_download_file);
@@ -257,7 +285,7 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName
         switch (item.getItemId()) {
         switch (item.getItemId()) {
             case R.id.action_rename_file: {
             case R.id.action_rename_file: {
                 String fileName = mTargetFile.getFileName();
                 String fileName = mTargetFile.getFileName();
-                int extensionStart = mTargetFile.isDirectory() ? -1 : fileName.lastIndexOf(".");
+                int extensionStart = mTargetFile.isFolder() ? -1 : fileName.lastIndexOf(".");
                 int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
                 int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
                 EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
                 EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
                 dialog.show(getFragmentManager(), EditNameDialog.TAG);
                 dialog.show(getFragmentManager(), EditNameDialog.TAG);
@@ -267,7 +295,7 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName
                 int messageStringId = R.string.confirmation_remove_alert;
                 int messageStringId = R.string.confirmation_remove_alert;
                 int posBtnStringId = R.string.confirmation_remove_remote;
                 int posBtnStringId = R.string.confirmation_remove_remote;
                 int neuBtnStringId = -1;
                 int neuBtnStringId = -1;
-                if (mTargetFile.isDirectory()) {
+                if (mTargetFile.isFolder()) {
                     messageStringId = R.string.confirmation_remove_folder_alert;
                     messageStringId = R.string.confirmation_remove_folder_alert;
                     posBtnStringId = R.string.confirmation_remove_remote_and_local;
                     posBtnStringId = R.string.confirmation_remove_remote_and_local;
                     neuBtnStringId = R.string.confirmation_remove_folder_local;
                     neuBtnStringId = R.string.confirmation_remove_folder_local;
@@ -287,7 +315,7 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName
             }
             }
             case R.id.action_sync_file: {
             case R.id.action_sync_file: {
                 Account account = AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity());
                 Account account = AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity());
-                RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, false, getSherlockActivity());
+                RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, getSherlockActivity());
                 operation.execute(account, getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
                 operation.execute(account, getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
                 ((FileDisplayActivity) getSherlockActivity()).showLoadingDialog();
                 ((FileDisplayActivity) getSherlockActivity()).showLoadingDialog();
                 return true;
                 return true;
@@ -346,7 +374,7 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName
      * @param directory File to be listed
      * @param directory File to be listed
      */
      */
     public void listDirectory(OCFile directory) {
     public void listDirectory(OCFile directory) {
-        DataStorageManager storageManager = mContainerActivity.getStorageManager();
+        FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
         if (storageManager != null) {
         if (storageManager != null) {
 
 
             // Check input parameters for null
             // Check input parameters for null
@@ -361,7 +389,7 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName
         
         
         
         
             // If that's not a directory -> List its parent
             // If that's not a directory -> List its parent
-            if(!directory.isDirectory()){
+            if(!directory.isFolder()){
                 Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
                 Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
                 directory = storageManager.getFileById(directory.getParentId());
                 directory = storageManager.getFileById(directory.getParentId());
             }
             }
@@ -371,7 +399,6 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName
                 mList.setSelectionFromTop(0, 0);
                 mList.setSelectionFromTop(0, 0);
             }
             }
             mFile = directory;
             mFile = directory;
-
         }
         }
     }
     }
     
     
@@ -396,11 +423,13 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName
         public void startMediaPreview(OCFile file, int i, boolean b);
         public void startMediaPreview(OCFile file, int i, boolean b);
 
 
         public void startImagePreview(OCFile file);
         public void startImagePreview(OCFile file);
+        
+        public void startSyncFolderOperation(OCFile folder);
 
 
         /**
         /**
          * Getter for the current DataStorageManager in the container activity
          * Getter for the current DataStorageManager in the container activity
          */
          */
-        public DataStorageManager getStorageManager();
+        public FileDataStorageManager getStorageManager();
         
         
         
         
         /**
         /**
@@ -454,16 +483,7 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName
     
     
     @Override
     @Override
     public void onNeutral(String callerTag) {
     public void onNeutral(String callerTag) {
-        File f = null;
-        if (mTargetFile.isDirectory()) {
-            // TODO run in a secondary thread?
-            mContainerActivity.getStorageManager().removeDirectory(mTargetFile, false, true);
-            
-        } else if (mTargetFile.isDown() && (f = new File(mTargetFile.getStoragePath())).exists()) {
-            f.delete();
-            mTargetFile.setStoragePath(null);
-            mContainerActivity.getStorageManager().saveFile(mTargetFile);
-        }
+        mContainerActivity.getStorageManager().removeFile(mTargetFile, false, true);    // TODO perform in background task / new thread
         listDirectory();
         listDirectory();
         mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
         mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
     }
     }

+ 6 - 13
src/com/owncloud/android/ui/preview/PreviewImageActivity.java

@@ -36,7 +36,6 @@ import com.actionbarsherlock.app.ActionBar;
 import com.actionbarsherlock.view.MenuItem;
 import com.actionbarsherlock.view.MenuItem;
 import com.actionbarsherlock.view.Window;
 import com.actionbarsherlock.view.Window;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileDownloader;
 import com.owncloud.android.files.services.FileDownloader;
@@ -67,7 +66,7 @@ public class PreviewImageActivity extends FileActivity implements FileFragment.C
     
     
     private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
     private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
     
     
-    private DataStorageManager mStorageManager;
+    private FileDataStorageManager mStorageManager;
     
     
     private ViewPager mViewPager; 
     private ViewPager mViewPager; 
     private PreviewImagePagerAdapter mPreviewImagePagerAdapter;    
     private PreviewImagePagerAdapter mPreviewImagePagerAdapter;    
@@ -82,9 +81,6 @@ public class PreviewImageActivity extends FileActivity implements FileFragment.C
 
 
     private boolean mFullScreen;
     private boolean mFullScreen;
     
     
-    private String mDownloadAddedMessage;
-    private String mDownloadFinishMessage;
-    
     
     
     @Override
     @Override
     protected void onCreate(Bundle savedInstanceState) {
     protected void onCreate(Bundle savedInstanceState) {
@@ -104,9 +100,6 @@ public class PreviewImageActivity extends FileActivity implements FileFragment.C
             mRequestWaitingForBinder = false;
             mRequestWaitingForBinder = false;
         }
         }
         
         
-        FileDownloader downloader = new FileDownloader();
-        mDownloadAddedMessage = downloader.getDownloadAddedMessage();
-        mDownloadFinishMessage= downloader.getDownloadFinishMessage();
     }
     }
 
 
     private void initViewPager() {
     private void initViewPager() {
@@ -116,7 +109,7 @@ public class PreviewImageActivity extends FileActivity implements FileFragment.C
         //OCFile parentFolder = mStorageManager.getFileById(getFile().getParentId());
         //OCFile parentFolder = mStorageManager.getFileById(getFile().getParentId());
         if (parentFolder == null) {
         if (parentFolder == null) {
             // should not be necessary
             // should not be necessary
-            parentFolder = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
+            parentFolder = mStorageManager.getFileByPath(OCFile.ROOT_PATH);
         }
         }
         mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), mStorageManager);
         mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), mStorageManager);
         mViewPager = (ViewPager) findViewById(R.id.fragmentPager);
         mViewPager = (ViewPager) findViewById(R.id.fragmentPager);
@@ -227,8 +220,8 @@ public class PreviewImageActivity extends FileActivity implements FileFragment.C
         //Log.e(TAG, "ACTIVITY, ONRESUME");
         //Log.e(TAG, "ACTIVITY, ONRESUME");
         mDownloadFinishReceiver = new DownloadFinishReceiver();
         mDownloadFinishReceiver = new DownloadFinishReceiver();
         
         
-        IntentFilter filter = new IntentFilter(mDownloadFinishMessage);
-        filter.addAction(mDownloadAddedMessage);
+        IntentFilter filter = new IntentFilter(FileDownloader.getDownloadFinishMessage());
+        filter.addAction(FileDownloader.getDownloadAddedMessage());
         registerReceiver(mDownloadFinishReceiver, filter);
         registerReceiver(mDownloadFinishReceiver, filter);
     }
     }
 
 
@@ -387,7 +380,7 @@ public class PreviewImageActivity extends FileActivity implements FileFragment.C
                 boolean downloadWasFine = intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false);
                 boolean downloadWasFine = intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false);
                 //boolean isOffscreen =  Math.abs((mViewPager.getCurrentItem() - position)) <= mViewPager.getOffscreenPageLimit();
                 //boolean isOffscreen =  Math.abs((mViewPager.getCurrentItem() - position)) <= mViewPager.getOffscreenPageLimit();
                 
                 
-                if (position >= 0 && intent.getAction().equals(mDownloadFinishMessage)) {
+                if (position >= 0 && intent.getAction().equals(FileDownloader.getDownloadFinishMessage())) {
                     if (downloadWasFine) {
                     if (downloadWasFine) {
                         mPreviewImagePagerAdapter.updateFile(position, file);   
                         mPreviewImagePagerAdapter.updateFile(position, file);   
                         
                         
@@ -442,7 +435,7 @@ public class PreviewImageActivity extends FileActivity implements FileFragment.C
             mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());            
             mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());            
             
             
             // Update file according to DB file, if it is possible
             // Update file according to DB file, if it is possible
-            if (file.getFileId() > DataStorageManager.ROOT_PARENT_ID)            
+            if (file.getFileId() > FileDataStorageManager.ROOT_PARENT_ID)            
                 file = mStorageManager.getFileById(file.getFileId());
                 file = mStorageManager.getFileById(file.getFileId());
             
             
             if (file != null) {
             if (file != null) {

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

@@ -28,7 +28,7 @@ import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentStatePagerAdapter;
 import android.support.v4.app.FragmentStatePagerAdapter;
 import android.view.ViewGroup;
 import android.view.ViewGroup;
 
 
-import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.ui.fragment.FileFragment;
 import com.owncloud.android.ui.fragment.FileFragment;
 
 
@@ -45,7 +45,7 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
     private Set<Object> mObsoleteFragments;
     private Set<Object> mObsoleteFragments;
     private Set<Integer> mObsoletePositions;
     private Set<Integer> mObsoletePositions;
     private Set<Integer> mDownloadErrors;
     private Set<Integer> mDownloadErrors;
-    private DataStorageManager mStorageManager;
+    private FileDataStorageManager mStorageManager;
     
     
     private Map<Integer, FileFragment> mCachedFragments;
     private Map<Integer, FileFragment> mCachedFragments;
 
 
@@ -56,7 +56,7 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
      * @param parentFolder      Folder where images will be searched for.
      * @param parentFolder      Folder where images will be searched for.
      * @param storageManager    Bridge to database.
      * @param storageManager    Bridge to database.
      */
      */
-    public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFolder, Account account, DataStorageManager storageManager) {
+    public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFolder, Account account, FileDataStorageManager storageManager) {
         super(fragmentManager);
         super(fragmentManager);
         
         
         if (fragmentManager == null) {
         if (fragmentManager == null) {
@@ -71,7 +71,7 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
 
 
         mAccount = account;
         mAccount = account;
         mStorageManager = storageManager;
         mStorageManager = storageManager;
-        mImageFiles = mStorageManager.getDirectoryImages(parentFolder); 
+        mImageFiles = mStorageManager.getFolderImages(parentFolder); 
         mObsoleteFragments = new HashSet<Object>();
         mObsoleteFragments = new HashSet<Object>();
         mObsoletePositions = new HashSet<Integer>();
         mObsoletePositions = new HashSet<Integer>();
         mDownloadErrors = new HashSet<Integer>();
         mDownloadErrors = new HashSet<Integer>();

+ 1 - 2
src/com/owncloud/android/ui/preview/PreviewVideoActivity.java

@@ -32,7 +32,6 @@ import android.widget.VideoView;
 
 
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.Log_OC;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
-import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;
 import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;
@@ -60,7 +59,7 @@ public class PreviewVideoActivity extends FileActivity implements OnCompletionLi
     
     
     private static final String TAG = PreviewVideoActivity.class.getSimpleName();
     private static final String TAG = PreviewVideoActivity.class.getSimpleName();
 
 
-    private DataStorageManager mStorageManager;
+    private FileDataStorageManager mStorageManager;
     
     
     private int mSavedPlaybackPosition;         // in the unit time handled by MediaPlayer.getCurrentPosition()
     private int mSavedPlaybackPosition;         // in the unit time handled by MediaPlayer.getCurrentPosition()
     private boolean mAutoplay;                  // when 'true', the playback starts immediately with the activity
     private boolean mAutoplay;                  // when 'true', the playback starts immediately with the activity

+ 1 - 1
src/eu/alefzero/webdav/WebdavClient.java

@@ -168,7 +168,7 @@ public class WebdavClient extends HttpClient {
         try {
         try {
             method.setFollowRedirects(mFollowRedirects);
             method.setFollowRedirects(mFollowRedirects);
         } catch (Exception e) {
         } catch (Exception e) {
-            if (mFollowRedirects) Log_OC.d(TAG, "setFollowRedirects failed for " + method.getName() + " method, custom redirection will be used");
+            //if (mFollowRedirects) Log_OC.d(TAG, "setFollowRedirects failed for " + method.getName() + " method, custom redirection will be used if needed");
             customRedirectionNeeded = mFollowRedirects;
             customRedirectionNeeded = mFollowRedirects;
         }
         }
         if (mSsoSessionCookie != null && mSsoSessionCookie.length() > 0) {
         if (mSsoSessionCookie != null && mSsoSessionCookie.length() > 0) {

+ 14 - 2
src/eu/alefzero/webdav/WebdavEntry.java

@@ -28,7 +28,7 @@ import com.owncloud.android.Log_OC;
 import android.net.Uri;
 import android.net.Uri;
 
 
 public class WebdavEntry {
 public class WebdavEntry {
-    private String mName, mPath, mUri, mContentType;
+    private String mName, mPath, mUri, mContentType, mEtag;
     private long mContentLength, mCreateTimestamp, mModifiedTimestamp;
     private long mContentLength, mCreateTimestamp, mModifiedTimestamp;
 
 
     public WebdavEntry(MultiStatusResponse ms, String splitElement) {
     public WebdavEntry(MultiStatusResponse ms, String splitElement) {
@@ -42,8 +42,10 @@ public class WebdavEntry {
             DavPropertySet propSet = ms.getProperties(status);
             DavPropertySet propSet = ms.getProperties(status);
             @SuppressWarnings("rawtypes")
             @SuppressWarnings("rawtypes")
             DavProperty prop = propSet.get(DavPropertyName.DISPLAYNAME);
             DavProperty prop = propSet.get(DavPropertyName.DISPLAYNAME);
-            if (prop != null)
+            if (prop != null) {
                 mName = (String) prop.getName().toString();
                 mName = (String) prop.getName().toString();
+                mName = mName.substring(1, mName.length()-1);
+            }
             else {
             else {
                 String[] tmp = mPath.split("/");
                 String[] tmp = mPath.split("/");
                 if (tmp.length > 0)
                 if (tmp.length > 0)
@@ -87,6 +89,12 @@ public class WebdavEntry {
                         .parseResponseDate((String) prop.getValue());
                         .parseResponseDate((String) prop.getValue());
                 mCreateTimestamp = (d != null) ? d.getTime() : 0;
                 mCreateTimestamp = (d != null) ? d.getTime() : 0;
             }
             }
+            
+            prop = propSet.get(DavPropertyName.GETETAG);
+            if (prop != null) {
+                mEtag = (String) prop.getValue();
+                mEtag = mEtag.substring(1, mEtag.length()-1);
+            }
 
 
         } else {
         } else {
             Log_OC.e("WebdavEntry",
             Log_OC.e("WebdavEntry",
@@ -129,6 +137,10 @@ public class WebdavEntry {
     public long modifiedTimestamp() {
     public long modifiedTimestamp() {
         return mModifiedTimestamp;
         return mModifiedTimestamp;
     }
     }
+    
+    public String etag() {
+        return mEtag;
+    }
 
 
     private void resetData() {
     private void resetData() {
         mName = mUri = mContentType = null;
         mName = mUri = mContentType = null;