Merge branch 'develop' into setup_buttons
authorDavid A. Velasco <dvelasco@solidgear.es>
Wed, 6 Nov 2013 09:36:34 +0000 (10:36 +0100)
committerDavid A. Velasco <dvelasco@solidgear.es>
Wed, 6 Nov 2013 09:36:34 +0000 (10:36 +0100)
25 files changed:
1  2 
AndroidManifest.xml
src/com/owncloud/android/Uploader.java
src/com/owncloud/android/datamodel/FileDataStorageManager.java
src/com/owncloud/android/datamodel/OCFile.java
src/com/owncloud/android/files/OwnCloudFileObserver.java
src/com/owncloud/android/files/services/FileDownloader.java
src/com/owncloud/android/files/services/FileUploader.java
src/com/owncloud/android/operations/CreateFolderOperation.java
src/com/owncloud/android/operations/RemoteOperation.java
src/com/owncloud/android/operations/RemoveFileOperation.java
src/com/owncloud/android/operations/RenameFileOperation.java
src/com/owncloud/android/operations/SynchronizeFileOperation.java
src/com/owncloud/android/providers/FileContentProvider.java
src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java
src/com/owncloud/android/syncadapter/FileSyncAdapter.java
src/com/owncloud/android/ui/activity/FileDisplayActivity.java
src/com/owncloud/android/ui/activity/Preferences.java
src/com/owncloud/android/ui/adapter/FileListListAdapter.java
src/com/owncloud/android/ui/fragment/FileDetailFragment.java
src/com/owncloud/android/ui/fragment/OCFileListFragment.java
src/com/owncloud/android/ui/preview/PreviewImageActivity.java
src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java
src/com/owncloud/android/ui/preview/PreviewVideoActivity.java
src/eu/alefzero/webdav/WebdavClient.java
src/eu/alefzero/webdav/WebdavEntry.java

diff --combined AndroidManifest.xml
@@@ -92,9 -92,9 +92,9 @@@
          <activity android:name=".ui.activity.PreferencesNewSessionewSession" >
          </activity>
          
 -        <activity     android:name="com.owncloud.android.ui.preview.PreviewImageActivity" />
 +        <activity     android:name=".ui.preview.PreviewImageActivity" />
                        
 -        <activity     android:name="com.owncloud.android.ui.preview.PreviewVideoActivity"
 +        <activity     android:name=".ui.preview.PreviewVideoActivity"
                                        android:label="@string/app_name"
                                        android:theme="@style/Theme.ownCloud.Fullscreen" >
                </activity>        
          <service android:name=".media.MediaService" />
          
          <activity android:name=".ui.activity.PinCodeActivity" />
-         <activity android:name="com.owncloud.android.extensions.ExtensionsAvailableActivity"></activity>
-         <activity android:name="com.owncloud.android.extensions.ExtensionsListActivity"></activity>
          <activity android:name=".ui.activity.AccountSelectActivity" android:uiOptions="none" android:label="@string/prefs_accounts"></activity>
          <activity android:name=".ui.activity.ConflictsResolveActivity"/>
          <activity android:name=".ui.activity.GenericExplanationActivity"/>
@@@ -26,13 -26,6 +26,12 @@@ import java.util.List
  import java.util.Stack;
  import java.util.Vector;
  
- import com.owncloud.android.datamodel.DataStorageManager;
 +import com.owncloud.android.R;
 +import com.owncloud.android.authentication.AccountAuthenticator;
 +import com.owncloud.android.datamodel.FileDataStorageManager;
 +import com.owncloud.android.datamodel.OCFile;
 +import com.owncloud.android.files.services.FileUploader;
 +
  import android.accounts.Account;
  import android.accounts.AccountManager;
  import android.app.AlertDialog;
@@@ -61,8 -54,13 +60,7 @@@ import android.widget.EditText
  import android.widget.SimpleAdapter;
  import android.widget.Toast;
  
 -import com.owncloud.android.authentication.AccountAuthenticator;
 -import com.owncloud.android.datamodel.FileDataStorageManager;
 -import com.owncloud.android.datamodel.OCFile;
 -import com.owncloud.android.files.services.FileUploader;
 -
 -import com.owncloud.android.R;
  
  /**
   * This can be used to upload things to an ownCloud instance.
   * 
@@@ -78,7 -76,7 +76,7 @@@ public class Uploader extends ListActiv
      private ArrayList<Parcelable> mStreamsToUpload;
      private boolean mCreateDir;
      private String mUploadPath;
-     private DataStorageManager mStorageManager;
+     private FileDataStorageManager mStorageManager;
      private OCFile mFile;
  
      private final static int DIALOG_NO_ACCOUNT = 0;
      public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
          // click on folder in the list
          Log_OC.d(TAG, "on item click");
-         Vector<OCFile> tmpfiles = mStorageManager.getDirectoryContent(mFile);
+         Vector<OCFile> tmpfiles = mStorageManager.getFolderContent(mFile);
          if (tmpfiles.size() <= 0) return;
          // filter on dirtype
          Vector<OCFile> files = new Vector<OCFile>();
          for (OCFile f : tmpfiles)
-             if (f.isDirectory())
+             if (f.isFolder())
                  files.add(f);
          if (files.size() < position) {
              throw new IndexOutOfBoundsException("Incorrect item selected");
          
          mFile = mStorageManager.getFileByPath(full_path);
          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>>();
              for (OCFile f : files) {
                  HashMap<String, Object> h = new HashMap<String, Object>();
-                 if (f.isDirectory()) {
+                 if (f.isFolder()) {
                      h.put("dirname", f.getFileName());
                      data.add(h);
                  }
@@@ -20,9 -20,9 +20,9 @@@ package com.owncloud.android.datamodel
  
  import java.io.File;
  import java.util.ArrayList;
+ import java.util.Collection;
  import java.util.Collections;
  import java.util.Iterator;
- import java.util.List;
  import java.util.Vector;
  
  import com.owncloud.android.Log_OC;
@@@ -30,39 -30,67 +30,68 @@@ import com.owncloud.android.MainApp
  import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
  import com.owncloud.android.utils.FileStorageUtils;
  
 +
  import android.accounts.Account;
  import android.content.ContentProviderClient;
  import android.content.ContentProviderOperation;
  import android.content.ContentProviderResult;
  import android.content.ContentResolver;
+ import android.content.ContentUris;
  import android.content.ContentValues;
  import android.content.OperationApplicationException;
  import android.database.Cursor;
  import android.net.Uri;
  import android.os.RemoteException;
  
- public class FileDataStorageManager implements DataStorageManager {
+ public class FileDataStorageManager {
+     public static final int ROOT_PARENT_ID = 0;
  
      private ContentResolver mContentResolver;
-     private ContentProviderClient mContentProvider;
+     private ContentProviderClient mContentProviderClient;
      private Account mAccount;
  
-     private static String TAG = "FileDataStorageManager";
+     private static String TAG = FileDataStorageManager.class.getSimpleName();
  
+     
      public FileDataStorageManager(Account account, ContentResolver cr) {
-         mContentProvider = null;
+         mContentProviderClient = null;
          mContentResolver = cr;
          mAccount = account;
      }
  
      public FileDataStorageManager(Account account, ContentProviderClient cp) {
-         mContentProvider = cp;
+         mContentProviderClient = cp;
          mContentResolver = null;
          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) {
          Cursor c = getCursorForValue(ProviderTableMeta.FILE_PATH, path);
          OCFile file = null;
              file = createFileInstance(c);
          }
          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 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) {
          Cursor c = getCursorForValue(ProviderTableMeta._ID, String.valueOf(id));
          OCFile file = null;
          return file;
      }
  
-     @Override
      public boolean fileExists(long id) {
          return fileExists(ProviderTableMeta._ID, String.valueOf(id));
      }
  
-     @Override
      public boolean fileExists(String 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) {
          boolean overriden = false;
          ContentValues cv = new ContentValues();
          cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
          cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
          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_PATH, file.getRemotePath());
-         if (!file.isDirectory())
+         if (!file.isFolder())
              cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
          cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
          cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
          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_ETAG, file.getEtag());
  
          boolean sameRemotePath = fileExists(file.getRemotePath());
-         boolean changesSizeOfAncestors = false;
          if (sameRemotePath ||
                  fileExists(file.getFileId())        ) {           // for renamed files; no more delete and create
  
              } else {
                  oldFile = getFileById(file.getFileId());
              }
-             changesSizeOfAncestors = (oldFile.getFileLength() != file.getFileLength());
  
              overriden = true;
              if (getContentResolver() != null) {
                          new String[] { String.valueOf(file.getFileId()) });
              } else {
                  try {
-                     getContentProvider().update(ProviderTableMeta.CONTENT_URI,
+                     getContentProviderClient().update(ProviderTableMeta.CONTENT_URI,
                              cv, ProviderTableMeta._ID + "=?",
                              new String[] { String.valueOf(file.getFileId()) });
                  } catch (RemoteException e) {
                  }
              }
          } else {
-             changesSizeOfAncestors = true;
              Uri result_uri = null;
              if (getContentResolver() != null) {
                  result_uri = getContentResolver().insert(
                          ProviderTableMeta.CONTENT_URI_FILE, cv);
              } else {
                  try {
-                     result_uri = getContentProvider().insert(
+                     result_uri = getContentProviderClient().insert(
                              ProviderTableMeta.CONTENT_URI_FILE, cv);
                  } catch (RemoteException e) {
                      Log_OC.e(TAG,
              }            
          }
  
-         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;
      }
  
  
-     @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();
              cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
              cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData());
              cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
              cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
              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());
-             if (!file.isDirectory())
+             if (!file.isFolder()) {
                  cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
+             }
              cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
              cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
              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_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).
                          withValues(cv).
                          withSelection(  ProviderTableMeta._ID + "=?", 
                                  .build());
  
              } else {
+                 // adding a new file
                  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
          ContentProviderResult[] results = null;
+         Log_OC.d(TAG, "Sending " + operations.size() + " operations to FileContentProvider");
          try {
              if (getContentResolver() != null) {
                  results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
  
              } else {
-                 results = getContentProvider().applyBatch(operations);
+                 results = getContentProviderClient().applyBatch(operations);
              }
  
          } 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) {
-             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
          if (results != null) {
              long newId;
+             Iterator<OCFile> filesIt = updatedFiles.iterator();
+             OCFile file = null;
              for (int i=0; i<results.length; i++) {
+                 if (filesIt.hasNext()) {
+                     file = filesIt.next();
+                 } else {
+                     file = null;
+                 }
                  if (results[i].uri != null) {
                      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>();
  
                  String.valueOf(parentId));
          Cursor c = null;
  
-         if (getContentProvider() != null) {
+         if (getContentProviderClient() != null) {
              try {
-                 c = getContentProvider().query(req_uri, null, 
+                 c = getContentProviderClient().query(req_uri, null, 
                          ProviderTableMeta.FILE_PARENT + "=?" ,
                          new String[] { String.valueOf(parentId)}, null);
              } catch (RemoteException e) {
      }
      
      
+     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) {
          Cursor c;
                                      new String[] { value, mAccount.name }, null);
          } else {
              try {
-                 c = getContentProvider().query(
+                 c = getContentProviderClient().query(
                          ProviderTableMeta.CONTENT_URI,
                          null,
                          cmp_key + "=? AND "
                                      new String[] { value, mAccount.name }, null);
          } else {
              try {
-                 c = getContentProvider().query(
+                 c = getContentProviderClient().query(
                          ProviderTableMeta.CONTENT_URI,
                          null,
                          key + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER
                      .getColumnIndex(ProviderTableMeta.FILE_PARENT)));
              file.setMimetype(c.getString(c
                      .getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)));
-             if (!file.isDirectory()) {
+             if (!file.isFolder()) {
                  file.setStoragePath(c.getString(c
                          .getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)));
                  if (file.getStoragePath() == null) {
                      getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA)));
              file.setKeepInSync(c.getInt(
                      c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false);
+             file.setEtag(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG)));
+                     
          }
          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();
-             
-         }              
-         
-     }
-     
  }
@@@ -22,7 -22,6 +22,7 @@@ import java.io.File
  
  import com.owncloud.android.Log_OC;
  
 +
  import android.os.Parcel;
  import android.os.Parcelable;
  import android.webkit.MimeTypeMap;
@@@ -42,6 -41,7 +42,7 @@@ public class OCFile implements Parcelab
      };
  
      public static final String PATH_SEPARATOR = "/";
+     public static final String ROOT_PATH = PATH_SEPARATOR;
  
      private static final String TAG = OCFile.class.getSimpleName();
      
@@@ -61,6 -61,7 +62,7 @@@
  
      private String mEtag;
  
      /**
       * Create new {@link OCFile} with given path.
       * 
@@@ -96,6 -97,7 +98,7 @@@
          mKeepInSync = source.readInt() == 1;
          mLastSyncDateForProperties = source.readLong();
          mLastSyncDateForData = source.readLong();
+         mEtag = source.readString();
      }
  
      @Override
          dest.writeInt(mKeepInSync ? 1 : 0);
          dest.writeLong(mLastSyncDateForProperties);
          dest.writeLong(mLastSyncDateForData);
+         dest.writeString(mEtag);
      }
      
      /**
      }
  
      /**
-      * 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");
      }
  
       */
      public String getFileName() {
          File f = new File(getRemotePath());
-         return f.getName().length() == 0 ? PATH_SEPARATOR : f.getName();
+         return f.getName().length() == 0 ? ROOT_PATH : f.getName();
      }
      
      /**
       */
      public void setFileName(String name) {
          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();
              parent = (parent.endsWith(PATH_SEPARATOR)) ? parent : parent + PATH_SEPARATOR;
              mRemotePath =  parent + name;
-             if (isDirectory()) {
+             if (isFolder()) {
                  mRemotePath += PATH_SEPARATOR;
              }
              Log_OC.d(TAG, "OCFile name changed to " + mRemotePath);
       *             not a directory
       */
      public void addFile(OCFile file) throws IllegalStateException {
-         if (isDirectory()) {
+         if (isFolder()) {
              file.mParentId = mId;
              mNeedsUpdating = true;
              return;
          mLastSyncDateForData = 0;
          mKeepInSync = false;
          mNeedsUpdating = false;
+         mEtag = null;
      }
  
      /**
  
      @Override
      public int compareTo(OCFile another) {
-         if (isDirectory() && another.isDirectory()) {
+         if (isFolder() && another.isFolder()) {
              return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
-         } else if (isDirectory()) {
+         } else if (isFolder()) {
              return -1;
-         } else if (another.isDirectory()) {
+         } else if (another.isFolder()) {
              return 1;
          }
          return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
  
      @Override
      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 mEtag;
      }
  
+     public void setEtag(String etag) {
+         this.mEtag = etag;
+     }
+     
      public long getLocalModificationTimestamp() {
          if (mLocalPath != null && mLocalPath.length() > 0) {
              File f = new File(mLocalPath);
@@@ -29,7 -29,6 +29,7 @@@ import com.owncloud.android.operations.
  import com.owncloud.android.ui.activity.ConflictsResolveActivity;
  
  
 +
  import android.accounts.Account;
  import android.content.Context;
  import android.content.Intent;
@@@ -84,7 -83,6 +84,6 @@@ public class OwnCloudFileObserver exten
                                                                      storageManager, 
                                                                      mOCAccount, 
                                                                      true, 
-                                                                     true, 
                                                                      mContext);
          RemoteOperationResult result = sfo.execute(mOCAccount, mContext);
          if (result.getCode() == ResultCode.SYNC_CONFLICT) {
@@@ -28,12 -28,11 +28,12 @@@ import java.util.Vector
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.ConcurrentMap;
  
 +import com.owncloud.android.Log_OC;
 +import com.owncloud.android.MainApp;
 +import com.owncloud.android.R;
  import com.owncloud.android.authentication.AuthenticatorActivity;
  import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
 -import eu.alefzero.webdav.OnDatatransferProgressListener;
 -
  import com.owncloud.android.network.OwnCloudClientUtils;
  import com.owncloud.android.operations.DownloadFileOperation;
  import com.owncloud.android.operations.RemoteOperationResult;
@@@ -43,9 -42,6 +43,9 @@@ import com.owncloud.android.ui.activity
  import com.owncloud.android.ui.preview.PreviewImageActivity;
  import com.owncloud.android.ui.preview.PreviewImageFragment;
  
 +import eu.alefzero.webdav.OnDatatransferProgressListener;
 +
 +
  import android.accounts.Account;
  import android.accounts.AccountsException;
  import android.app.Notification;
@@@ -62,6 -58,9 +62,6 @@@ import android.os.Message
  import android.os.Process;
  import android.widget.RemoteViews;
  
 -import com.owncloud.android.Log_OC;
 -import com.owncloud.android.MainApp;
 -import com.owncloud.android.R;
  import eu.alefzero.webdav.WebdavClient;
  
  public class FileDownloader extends Service implements OnDatatransferProgressListener {
              if (account == null || file == null) return false;
              String targetKey = buildRemoteName(account, file);
              synchronized (mPendingDownloads) {
-                 if (file.isDirectory()) {
+                 if (file.isFolder()) {
                      // this can be slow if there are many downloads :(
                      Iterator<String> it = mPendingDownloads.keySet().iterator();
                      boolean found = false;
@@@ -33,15 -33,10 +33,15 @@@ import org.apache.jackrabbit.webdav.Dav
  import org.apache.jackrabbit.webdav.MultiStatus;
  import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
  
 +import com.owncloud.android.Log_OC;
 +import com.owncloud.android.MainApp;
 +import com.owncloud.android.R;
  import com.owncloud.android.authentication.AccountAuthenticator;
  import com.owncloud.android.authentication.AuthenticatorActivity;
  import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
 +import com.owncloud.android.db.DbHandler;
 +import com.owncloud.android.network.OwnCloudClientUtils;
  import com.owncloud.android.operations.ChunkedUploadFileOperation;
  import com.owncloud.android.operations.CreateFolderOperation;
  import com.owncloud.android.operations.ExistenceCheckOperation;
@@@ -49,19 -44,13 +49,19 @@@ import com.owncloud.android.operations.
  import com.owncloud.android.operations.RemoteOperationResult;
  import com.owncloud.android.operations.UploadFileOperation;
  import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
 +import com.owncloud.android.ui.activity.FailedUploadActivity;
 +import com.owncloud.android.ui.activity.FileActivity;
 +import com.owncloud.android.ui.activity.FileDisplayActivity;
 +import com.owncloud.android.ui.activity.InstantUploadActivity;
 +import com.owncloud.android.ui.preview.PreviewImageActivity;
 +import com.owncloud.android.ui.preview.PreviewImageFragment;
  import com.owncloud.android.utils.OwnCloudVersion;
  
 +
  import eu.alefzero.webdav.OnDatatransferProgressListener;
  import eu.alefzero.webdav.WebdavEntry;
  import eu.alefzero.webdav.WebdavUtils;
  
 -import com.owncloud.android.network.OwnCloudClientUtils;
  
  import android.accounts.Account;
  import android.accounts.AccountManager;
@@@ -81,6 -70,16 +81,6 @@@ import android.os.Process
  import android.webkit.MimeTypeMap;
  import android.widget.RemoteViews;
  
 -import com.owncloud.android.Log_OC;
 -import com.owncloud.android.MainApp;
 -import com.owncloud.android.R;
 -import com.owncloud.android.db.DbHandler;
 -import com.owncloud.android.ui.activity.FailedUploadActivity;
 -import com.owncloud.android.ui.activity.FileActivity;
 -import com.owncloud.android.ui.activity.FileDisplayActivity;
 -import com.owncloud.android.ui.activity.InstantUploadActivity;
 -import com.owncloud.android.ui.preview.PreviewImageActivity;
 -import com.owncloud.android.ui.preview.PreviewImageFragment;
  
  import eu.alefzero.webdav.WebdavClient;
  
@@@ -384,7 -383,7 +384,7 @@@ public class FileUploader extends Servi
                  return false;
              String targetKey = buildRemoteName(account, file);
              synchronized (mPendingUploads) {
-                 if (file.isDirectory()) {
+                 if (file.isFolder()) {
                      // this can be slow if there are many uploads :(
                      Iterator<String> it = mPendingUploads.keySet().iterator();
                      boolean found = false;
@@@ -23,10 -23,9 +23,10 @@@ import org.apache.commons.httpclient.Ht
  import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
  
  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 eu.alefzero.webdav.WebdavClient;
  import eu.alefzero.webdav.WebdavUtils;
  
@@@ -44,7 -43,7 +44,7 @@@ public class CreateFolderOperation exte
      
      protected String mRemotePath;
      protected boolean mCreateFullPath;
-     protected DataStorageManager mStorageManager;
+     protected FileDataStorageManager mStorageManager;
      
      /**
       * Constructor
@@@ -53,7 -52,7 +53,7 @@@
       * @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. 
       */
-     public CreateFolderOperation(String remotePath, boolean createFullPath, DataStorageManager storageManager) {
+     public CreateFolderOperation(String remotePath, boolean createFullPath, FileDataStorageManager storageManager) {
          mRemotePath = remotePath;
          mCreateFullPath = createFullPath;
          mStorageManager = storageManager;
@@@ -26,7 -26,6 +26,7 @@@ import com.owncloud.android.network.Bea
  import com.owncloud.android.network.OwnCloudClientUtils;
  import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
  
 +
  import android.accounts.Account;
  import android.accounts.AccountManager;
  import android.accounts.AccountsException;
@@@ -137,14 -136,8 +137,8 @@@ public abstract class RemoteOperation i
          mCallerActivity = callerActivity;
          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;
          
-         if (listenerHandler == null) {
-             throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
-         }
          mListenerHandler = listenerHandler;
          
          Thread runnerThread = new Thread(this);
@@@ -21,10 -21,9 +21,10 @@@ import org.apache.commons.httpclient.Ht
  import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
  
  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 eu.alefzero.webdav.WebdavClient;
  import eu.alefzero.webdav.WebdavUtils;
  
@@@ -42,7 -41,7 +42,7 @@@ public class RemoveFileOperation extend
      
      OCFile mFileToRemove;
      boolean mDeleteLocalCopy;
-     DataStorageManager mDataStorageManager;
+     FileDataStorageManager mDataStorageManager;
      
      
      /**
@@@ -52,7 -51,7 +52,7 @@@
       * @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. 
       */
-     public RemoveFileOperation(OCFile fileToRemove, boolean deleteLocalCopy, DataStorageManager storageManager) {
+     public RemoveFileOperation(OCFile fileToRemove, boolean deleteLocalCopy, FileDataStorageManager storageManager) {
          mFileToRemove = fileToRemove;
          mDeleteLocalCopy = deleteLocalCopy;
          mDataStorageManager = storageManager;
              delete = new DeleteMethod(client.getBaseUri() + WebdavUtils.encodePath(mFileToRemove.getRemotePath()));
              int status = client.executeMethod(delete, REMOVE_READ_TIMEOUT, REMOVE_CONNECTION_TIMEOUT);
              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
              result = new RemoteOperationResult((delete.succeeded() || status == HttpStatus.SC_NOT_FOUND), status, delete.getResponseHeaders());
@@@ -21,16 -21,15 +21,16 @@@ import java.io.File
  import java.io.IOException;
  
  import org.apache.jackrabbit.webdav.client.methods.DavMethodBase;
 -//import org.apache.jackrabbit.webdav.client.methods.MoveMethod;
 -
 -import android.accounts.Account;
  
  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.operations.RemoteOperationResult.ResultCode;
  import com.owncloud.android.utils.FileStorageUtils;
 +//import org.apache.jackrabbit.webdav.client.methods.MoveMethod;
 +
 +import android.accounts.Account;
 +
  
  import eu.alefzero.webdav.WebdavClient;
  import eu.alefzero.webdav.WebdavUtils;
@@@ -52,7 -51,7 +52,7 @@@ public class RenameFileOperation extend
      private Account mAccount;
      private String mNewName;
      private String mNewRemotePath;
-     private DataStorageManager mStorageManager;
+     private FileDataStorageManager mStorageManager;
      
      
      /**
@@@ -63,7 -62,7 +63,7 @@@
       * @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. 
       */
-     public RenameFileOperation(OCFile file, Account account, String newName, DataStorageManager storageManager) {
+     public RenameFileOperation(OCFile file, Account account, String newName, FileDataStorageManager storageManager) {
          mFile = file;
          mAccount = account;
          mNewName = newName;
@@@ -95,7 -94,7 +95,7 @@@
              String parent = (new File(mFile.getRemotePath())).getParent();
              parent = (parent.endsWith(OCFile.PATH_SEPARATOR)) ? parent : parent + OCFile.PATH_SEPARATOR; 
              mNewRemotePath =  parent + mNewName;
-             if (mFile.isDirectory()) {
+             if (mFile.isFolder()) {
                  mNewRemotePath += OCFile.PATH_SEPARATOR;
              }
              
              int status = client.executeMethod(move, RENAME_READ_TIMEOUT, RENAME_CONNECTION_TIMEOUT);
              if (move.succeeded()) {
  
-                 if (mFile.isDirectory()) {
+                 if (mFile.isFolder()) {
                      saveLocalDirectory();
                      
                  } else {
  
      
      private void saveLocalDirectory() {
-         mStorageManager.moveDirectory(mFile, mNewRemotePath);
+         mStorageManager.moveFolder(mFile, mNewRemotePath);
          String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
          File localDir = new File(localPath);
          if (localDir.exists()) {
@@@ -23,18 -23,17 +23,18 @@@ import org.apache.jackrabbit.webdav.Dav
  import org.apache.jackrabbit.webdav.MultiStatus;
  import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
  
 -import android.accounts.Account;
 -import android.content.Context;
 -import android.content.Intent;
 -
  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.files.services.FileDownloader;
  import com.owncloud.android.files.services.FileUploader;
  import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
  
 +import android.accounts.Account;
 +import android.content.Context;
 +import android.content.Intent;
 +
 +
  import eu.alefzero.webdav.WebdavClient;
  import eu.alefzero.webdav.WebdavEntry;
  import eu.alefzero.webdav.WebdavUtils;
@@@ -47,10 -46,9 +47,9 @@@ public class SynchronizeFileOperation e
      
      private OCFile mLocalFile;
      private OCFile mServerFile;
-     private DataStorageManager mStorageManager;
+     private FileDataStorageManager mStorageManager;
      private Account mAccount;
      private boolean mSyncFileContents;
-     private boolean mLocalChangeAlreadyKnown;
      private Context mContext;
      
      private boolean mTransferWasRequested = false;
      public SynchronizeFileOperation(
              OCFile localFile,
              OCFile serverFile,          // make this null to let the operation checks the server; added to reuse info from SynchronizeFolderOperation 
-             DataStorageManager storageManager, 
+             FileDataStorageManager storageManager, 
              Account account, 
              boolean syncFileContents,
-             boolean localChangeAlreadyKnown, 
              Context context) {
          
          mLocalFile = localFile;
@@@ -69,7 -66,6 +67,6 @@@
          mStorageManager = storageManager;
          mAccount = account;
          mSyncFileContents = syncFileContents;
-         mLocalChangeAlreadyKnown = localChangeAlreadyKnown;
          mContext = context;
      }
  
                
                      /// check changes in server and local file
                      boolean serverChanged = false;
+                     /* time for eTag is coming, but not yet
                      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?
-                     } else {
+                     } else { */
                          // server without etags
                          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
                
                      /// decide action to perform depending upon changes
+                     //if (!mLocalFile.getEtag().isEmpty() && localChanged && serverChanged) {
                      if (localChanged && serverChanged) {
                          result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
                    
          file.setFileLength(we.contentLength());
          file.setMimetype(we.contentType());
          file.setModificationTimestamp(we.modifiedTimestamp());
+         file.setEtag(we.etag());
+         
          return file;
      }
  
  
  package com.owncloud.android.providers;
  
+ import java.util.ArrayList;
  import java.util.HashMap;
  
  import com.owncloud.android.Log_OC;
  import com.owncloud.android.R;
+ import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.db.ProviderMeta;
  import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
  
  
 +
  import android.content.ContentProvider;
+ import android.content.ContentProviderOperation;
+ import android.content.ContentProviderResult;
  import android.content.ContentUris;
  import android.content.ContentValues;
  import android.content.Context;
+ import android.content.OperationApplicationException;
  import android.content.UriMatcher;
  import android.database.Cursor;
  import android.database.SQLException;
@@@ -44,6 -48,7 +49,7 @@@ import android.text.TextUtils
   * The ContentProvider for the ownCloud App.
   * 
   * @author Bartek Przybylski
+  * @author David A. Velasco
   * 
   */
  public class FileContentProvider extends ContentProvider {
                  ProviderTableMeta.FILE_KEEP_IN_SYNC);
          mProjectionMap.put(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 DIRECTORY = 2;
      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
      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();
+         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;
          switch (mUriMatcher.match(uri)) {
          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,
                      ProviderTableMeta._ID
                              + "="
                              + uri.getPathSegments().get(1)
                              + (!TextUtils.isEmpty(where) ? " AND (" + where
                                      + ")" : ""), 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;
          case ROOT_DIRECTORY:
+             //Log_OC.d(TAG, "Removing ROOT!");
              count = db.delete(ProviderTableMeta.DB_NAME, where, whereArgs);
              break;
          default:
+             //Log_OC.e(TAG, "Unknown uri " + uri);
              throw new IllegalArgumentException("Unknown uri: " + uri.toString());
          }
-         getContext().getContentResolver().notifyChange(uri, null);
          return count;
      }
+     
  
      @Override
      public String getType(Uri uri) {
  
      @Override
      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 &&
-             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);
          }
  
-         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;
          }
-         throw new SQLException("ERROR " + uri);
      }
  
+     
      @Override
      public boolean onCreate() {
          mDbHelper = new DataBaseHelper(getContext());
          mUriMatcher.addURI(authority, null, ROOT_DIRECTORY);
          mUriMatcher.addURI(authority, "file/", SINGLE_FILE);
          mUriMatcher.addURI(authority, "file/#", SINGLE_FILE);
+         mUriMatcher.addURI(authority, "dir/", DIRECTORY);
          mUriMatcher.addURI(authority, "dir/#", DIRECTORY);
          
          return true;
      }
  
+     
      @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();
  
          sqlQuery.setTables(ProviderTableMeta.DB_NAME);
          case ROOT_DIRECTORY:
              break;
          case DIRECTORY:
+             String folderId = uri.getPathSegments().get(1);
              sqlQuery.appendWhere(ProviderTableMeta.FILE_PARENT + "="
-                     + uri.getPathSegments().get(1));
+                     + folderId);
              break;
          case SINGLE_FILE:
              if (uri.getPathSegments().size() > 1) {
              order = sortOrder;
          }
  
-         SQLiteDatabase db = mDbHelper.getReadableDatabase();
          // DB case_sensitive
          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);
          return c;
      }
  
      @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 {
  
          public DataBaseHelper(Context context) {
                      + ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, "
                      + ProviderTableMeta.FILE_KEEP_IN_SYNC + " 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 );"
                      );
          }
  
              }
              if (!upgraded)
                  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);
          }
      }
  
  }
  package com.owncloud.android.syncadapter;\r
  \r
  import java.io.IOException;\r
- import java.util.Date;\r
  \r
  import org.apache.http.HttpRequest;\r
  import org.apache.http.HttpResponse;\r
  import org.apache.http.client.ClientProtocolException;\r
- import org.apache.http.conn.ConnectionKeepAliveStrategy;\r
- import org.apache.http.protocol.HttpContext;\r
  \r
  import com.owncloud.android.authentication.AccountUtils;\r
  import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;\r
- import com.owncloud.android.datamodel.DataStorageManager;\r
+ import com.owncloud.android.datamodel.FileDataStorageManager;\r
  import com.owncloud.android.network.OwnCloudClientUtils;\r
  \r
 +\r
  import android.accounts.Account;\r
  import android.accounts.AccountManager;\r
  import android.accounts.AuthenticatorException;\r
@@@ -43,11 -39,13 +40,13 @@@ import android.content.Context
  import eu.alefzero.webdav.WebdavClient;\r
  \r
  /**\r
-  * Base SyncAdapter for OwnCloud Designed to be subclassed for the concrete\r
-  * SyncAdapter, like ConcatsSync, CalendarSync, FileSync etc..\r
+  * Base synchronization adapter for ownCloud designed to be subclassed for different\r
+  * resource types, like FileSync, ConcatsSync, CalendarSync, etc..\r
   * \r
-  * @author sassman\r
+  * Implements the standard {@link AbstractThreadedSyncAdapter}.\r
   * \r
+  * @author sassman\r
+  * @author David A. Velasco\r
   */\r
  public abstract class AbstractOwnCloudSyncAdapter extends\r
          AbstractThreadedSyncAdapter {\r
@@@ -55,8 -53,7 +54,7 @@@
      private AccountManager accountManager;\r
      private Account account;\r
      private ContentProviderClient contentProvider;\r
-     private Date lastUpdated;\r
-     private DataStorageManager mStoreManager;\r
+     private FileDataStorageManager mStoreManager;\r
  \r
      private WebdavClient mClient = null;\r
  \r
          this.contentProvider = contentProvider;\r
      }\r
  \r
-     public Date getLastUpdated() {\r
-         return lastUpdated;\r
-     }\r
\r
-     public void setLastUpdated(Date lastUpdated) {\r
-         this.lastUpdated = lastUpdated;\r
-     }\r
\r
-     public void setStorageManager(DataStorageManager storage_manager) {\r
+     public void setStorageManager(FileDataStorageManager storage_manager) {\r
          mStoreManager = storage_manager;\r
      }\r
  \r
-     public DataStorageManager getStorageManager() {\r
+     public FileDataStorageManager getStorageManager() {\r
          return mStoreManager;\r
      }\r
  \r
-     protected ConnectionKeepAliveStrategy getKeepAliveStrategy() {\r
-         return new ConnectionKeepAliveStrategy() {\r
-             public long getKeepAliveDuration(HttpResponse response,\r
-                     HttpContext context) {\r
-                 // Change keep alive straategy basing on response: ie\r
-                 // forbidden/not found/etc\r
-                 // should have keep alive 0\r
-                 // default return: 5s\r
-                 int statusCode = response.getStatusLine().getStatusCode();\r
\r
-                 // HTTP 400, 500 Errors as well as HTTP 118 - Connection timed\r
-                 // out\r
-                 if ((statusCode >= 400 && statusCode <= 418)\r
-                         || (statusCode >= 421 && statusCode <= 426)\r
-                         || (statusCode >= 500 && statusCode <= 510)\r
-                         || statusCode == 118) {\r
-                     return 0;\r
-                 }\r
\r
-                 return 5 * 1000;\r
-             }\r
-         };\r
-     }\r
\r
-     protected HttpResponse fireRawRequest(HttpRequest query)\r
-             throws ClientProtocolException, OperationCanceledException,\r
-             AuthenticatorException, IOException {\r
-         /*\r
-          * BasicHttpContext httpContext = new BasicHttpContext(); BasicScheme\r
-          * basicAuth = new BasicScheme();\r
-          * httpContext.setAttribute("preemptive-auth", basicAuth);\r
-          * \r
-          * HttpResponse response = getClient().execute(mHost, query,\r
-          * httpContext);\r
-          */\r
-         return null;\r
-     }\r
\r
      protected void initClientForCurrentAccount() throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {\r
          AccountUtils.constructFullURLForAccount(getContext(), account);\r
          mClient = OwnCloudClientUtils.createOwnCloudClient(account, getContext());\r
      protected WebdavClient getClient() {\r
          return mClient;\r
      }\r
+     \r
+     \r
+     /* method called by ContactSyncAdapter, that is never used */\r
+     protected HttpResponse fireRawRequest(HttpRequest query)\r
+             throws ClientProtocolException, OperationCanceledException,\r
+             AuthenticatorException, IOException {\r
+         return null;\r
+     }\r
\r
  }
@@@ -30,7 -30,6 +30,6 @@@ import com.owncloud.android.Log_OC
  import com.owncloud.android.MainApp;
  import com.owncloud.android.R;
  import com.owncloud.android.authentication.AuthenticatorActivity;
- import com.owncloud.android.datamodel.DataStorageManager;
  import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
  import com.owncloud.android.operations.RemoteOperationResult;
@@@ -39,12 -38,12 +38,13 @@@ import com.owncloud.android.operations.
  import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
  import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity;
  
 +
  import android.accounts.Account;
  import android.accounts.AccountsException;
  import android.app.Notification;
  import android.app.NotificationManager;
  import android.app.PendingIntent;
+ import android.content.AbstractThreadedSyncAdapter;
  import android.content.ContentProviderClient;
  import android.content.ContentResolver;
  import android.content.Context;
@@@ -53,36 -52,60 +53,60 @@@ import android.content.SyncResult
  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 David A. Velasco
   */
  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; 
      
+     
+     /** Time stamp for the current synchronization process, used to distinguish fresh data */
      private long mCurrentSyncTime;
+     
+     /** Flag made 'true' when a request to cancel the synchronization is received */
      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 int mFailedResultsCounter;    
+     
+     /** Counter for failed operations in the synchronization process */
+     private int mFailedResultsCounter;
+     
+     /** Result of the last failed operation */
      private RemoteOperationResult mLastFailedResult;
-     private SyncResult mSyncResult;
+     
+     /** Counter of conflicts found between local and remote files */
      private int mConflictsFound;
+     
+     /** Counter of failed operations in synchronization of kept-in-sync files */
      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;
  
+     /** {@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) {
          super(context, autoInitialize);
      }
  
+     
      /**
       * {@inheritDoc}
       */
          mForgottenLocalFiles = new HashMap<String, String>();
          mSyncResult = syncResult;
          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.setContentProvider(provider);
-         this.setStorageManager(new FileDataStorageManager(account, getContentProvider()));
+         this.setStorageManager(new FileDataStorageManager(account, provider));
          try {
              this.initClientForCurrentAccount();
          } 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;
              notifyFailedSynchronization();
              return;
          } 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;
              notifyFailedSynchronization();
              return;
              updateOCVersion();
              mCurrentSyncTime = System.currentTimeMillis();
              if (!mCancellation) {
-                 fetchData(OCFile.PATH_SEPARATOR, DataStorageManager.ROOT_PARENT_ID);
+                 synchronizeFolder(getStorageManager().getFileByPath(OCFile.ROOT_PATH));
                  
              } 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");
              }
              
              
                  
                  /// notify the user about the failure of MANUAL synchronization
                  notifyFailedSynchronization();
-                 
              }
              if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
                  notifyFailsInFavourites();
              }
              if (mForgottenLocalFiles.size() > 0) {
                  notifyForgottenLocalFiles();
-                 
              }
              sendStickyBroadcast(false, null, mLastFailedResult);        // message to signal the end to the UI
          }
      /**
       * 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
      public void onSyncCanceled() {
      
      
      /**
-      * 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))
              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, 
-                                                                                     parentId, 
+                                                                                     true,
                                                                                      getStorageManager(), 
                                                                                      getAccount(), 
                                                                                      getContext()
          
          
          // 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.getCode() == ResultCode.SYNC_CONFLICT) {
              if (synchFolderOp.getForgottenLocalFiles().size() > 0) {
                  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 {
+             // in failures, the statistics for the global result are updated
              if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED ||
-                    // (result.isTemporalRedirection() && result.isIdPRedirection() &&
                      ( result.isIdPRedirection() && 
                              MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))) {
                  mSyncResult.stats.numAuthExceptions++;
      }
  
      /**
-      * 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;
+         OCFile newFile = null;
+         String etag = null;
+         boolean syncDown = false;
          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");
      }
  
      
@@@ -22,8 -22,8 +22,8 @@@ import java.io.File
  
  import android.accounts.Account;
  import android.app.AlertDialog;
- import android.app.ProgressDialog;
  import android.app.Dialog;
+ import android.app.ProgressDialog;
  import android.content.BroadcastReceiver;
  import android.content.ComponentName;
  import android.content.ContentResolver;
@@@ -60,27 -60,27 +60,27 @@@ import com.actionbarsherlock.view.Windo
  import com.owncloud.android.Log_OC;
  import com.owncloud.android.MainApp;
  import com.owncloud.android.R;
- import com.owncloud.android.datamodel.DataStorageManager;
  import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
  import com.owncloud.android.files.services.FileDownloader;
 -import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
  import com.owncloud.android.files.services.FileObserverService;
  import com.owncloud.android.files.services.FileUploader;
 +import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
  import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
  import com.owncloud.android.operations.CreateFolderOperation;
  import com.owncloud.android.operations.OnRemoteOperationListener;
  import com.owncloud.android.operations.RemoteOperation;
  import com.owncloud.android.operations.RemoteOperationResult;
+ import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
  import com.owncloud.android.operations.RemoveFileOperation;
  import com.owncloud.android.operations.RenameFileOperation;
  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.ui.dialog.EditNameDialog;
+ import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
  import com.owncloud.android.ui.dialog.LoadingDialog;
  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.fragment.FileDetailFragment;
  import com.owncloud.android.ui.fragment.FileFragment;
@@@ -89,7 -89,6 +89,7 @@@ import com.owncloud.android.ui.preview.
  import com.owncloud.android.ui.preview.PreviewMediaFragment;
  import com.owncloud.android.ui.preview.PreviewVideoActivity;
  
 +
  /**
   * Displays, what files the user has available in his ownCloud.
   * 
@@@ -103,7 -102,7 +103,7 @@@ OCFileListFragment.ContainerActivity, F
      private ArrayAdapter<String> mDirectories;
  
      /** Access point to the cached database for the current ownCloud {@link Account} */
-     private DataStorageManager mStorageManager = null;
+     private FileDataStorageManager mStorageManager = null;
  
      private SyncBroadcastReceiver mSyncBroadcastReceiver;
      private UploadFinishReceiver mUploadFinishReceiver;
      private View mRightFragmentContainer;
  
      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;
      private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 1;
      private OCFile mWaitingToPreview;
      private Handler mHandler;
      
+     private boolean mSyncInProgress = false;
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          Log_OC.d(TAG, "onCreate() start");
          /// Load of saved instance state
          if(savedInstanceState != null) {
              mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW);
+             mSyncInProgress = savedInstanceState.getBoolean(KEY_SYNC_IN_PROGRESS);
+            
          } else {
              mWaitingToPreview = null;
-         }
+             mSyncInProgress = false;
+         }        
  
          /// USER INTERFACE
  
          // Action bar setup
          mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
          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        
          
          
          
              }
              if (file == null) {
                  // 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);
-             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) {
                  Log_OC.e(TAG, "Initializing Fragments in onAccountChanged..");
                  initFragmentsWithFile();
+                 if (file.isFolder()) {
+                     startSyncFolderOperation(file);
+                 }
                  
              } else {
-                 updateFragmentsVisibility(!file.isDirectory());
-                 updateNavigationElementsInActionBar(file.isDirectory() ? null : file);
+                 updateFragmentsVisibility(!file.isFolder());
+                 updateNavigationElementsInActionBar(file.isFolder() ? null : file);
              }
              
              
      }
  
  
+     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() {
          OCFileListFragment listOfFiles = new OCFileListFragment();
          FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  
      private Fragment chooseInitialSecondFragment(OCFile file) {
          Fragment secondFragment = null;
-         if (file != null && !file.isDirectory()) {
+         if (file != null && !file.isFolder()) {
              if (file.isDown() && PreviewMediaFragment.canBePreviewed(file) 
                      && file.getLastSyncDateForProperties() > 0  // temporal fix
                      ) {
              FileFragment second = getSecondFragment();
              OCFile currentDir = getCurrentDir();
              if((currentDir != null && currentDir.getParentId() != 0) || 
-                     (second != null && second.getFile() != null)) {
+                     (second != null && second.getFile() != null)) {                
                  onBackPressed(); 
+                 
              }
              break;
          }
  
      @Override
      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);
+         }
          return true;
      }
  
                      finish();
                      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
              setFile(listOfFiles.getCurrentFile());
          }
          cleanSecondFragment();
      }
  
      @Override
          Log_OC.e(TAG, "onSaveInstanceState() start");
          super.onSaveInstanceState(outState);
          outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
+         outState.putBoolean(FileDisplayActivity.KEY_SYNC_IN_PROGRESS, mSyncInProgress);
          Log_OC.d(TAG, "onSaveInstanceState() end");
      }
      
      /**
       * Pushes a directory to the drop down list
       * @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) {
-         if(!directory.isDirectory()){
+         if(!directory.isFolder()){
              throw new IllegalArgumentException("Only directories may be pushed!");
          }
          mDirectories.insert(directory.getFileName(), 0);
          public void onReceive(Context context, Intent intent) {
              boolean inProgress = intent.getBooleanExtra(FileSyncService.IN_PROGRESS, false);
              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)
                      && mStorageManager != null
  
                  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);
                  removeStickyBroadcast(intent);
+                 mSyncInProgress = inProgress;
  
              }
-             RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);
+             
              if (synchResult != null) {
                  if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {
                      mLastSslUntrustedServerResult = synchResult;
              }
          }
      }
+     
  
      private class UploadFinishReceiver extends BroadcastReceiver {
          /**
       * {@inheritDoc}
       */
      @Override
-     public DataStorageManager getStorageManager() {
+     public FileDataStorageManager getStorageManager() {
          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}
       * 
      public void onBrowsedDownTo(OCFile directory) {
          pushDirname(directory);
          cleanSecondFragment();
+         
+         // Sync Folder
+         startSyncFolderOperation(directory);
+         
      }
  
      /**
      }
  
  
+ //    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}
       */
  
          } else if (operation instanceof CreateFolderOperation) {
              onCreateFolderOperationFinish((CreateFolderOperation)operation, result);
-         }
+             
+         } 
      }
  
  
              refeshListOfFilesFragment();
  
          } else {
-             //dismissDialog(DIALOG_SHORT_WAIT);
              dismissLoadingDialog();
              try {
                  Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG); 
                  i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, getAccount());
                  startActivity(i);
  
-             } else {
-                 Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG); 
-                 msg.show();
-             }
+             } 
  
          } else {
              if (operation.transferWasRequested()) {
      private OCFile getCurrentDir() {
          OCFile file = getFile();
          if (file != null) {
-             if (file.isDirectory()) {
+             if (file.isFolder()) {
                  return file;
              } else if (mStorageManager != null) {
                  String parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
          }
          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);
+ //          }
+ //        }
+ //      }
  }
@@@ -26,7 -26,6 +26,6 @@@ import android.content.pm.PackageManage
  import android.net.Uri;
  import android.os.Bundle;
  import android.preference.CheckBoxPreference;
- import android.preference.ListPreference;
  import android.preference.Preference;
  import android.preference.Preference.OnPreferenceChangeListener;
  import android.preference.Preference.OnPreferenceClickListener;
@@@ -42,22 -41,19 +41,20 @@@ import com.owncloud.android.OwnCloudSes
  import com.owncloud.android.R;
  import com.owncloud.android.db.DbHandler;
  
 +
  /**
   * An Activity that allows the user to change the application's settings.
   * 
   * @author Bartek Przybylski
-  * 
+  * @author David A. Velasco
   */
- public class Preferences extends SherlockPreferenceActivity implements OnPreferenceChangeListener {
+ public class Preferences extends SherlockPreferenceActivity {
      
      private static final String TAG = "OwnCloudPreferences";
      private final int mNewSession = 47;
      private final int mEditSession = 48;
      private DbHandler mDbHandler;
      private Vector<OwnCloudSession> mSessions;
-     private ListPreference mTrackingUpdateInterval;
-     private CheckBoxPreference mDeviceTracking;
      private CheckBoxPreference pCode;
      //private CheckBoxPreference pLogging;
      //private Preference pLoggingHistory;
          Intent intent;
  
          switch (item.getItemId()) {
-         //case R.id.addSessionItem:
-         case 1:
-             intent = new Intent(this, PreferencesNewSession.class);
-             startActivityForResult(intent, mNewSession);
-             break;
-         case R.id.SessionContextEdit:
-             intent = new Intent(this, PreferencesNewSession.class);
-             intent.putExtra("sessionId", mSessions.get(mSelectedMenuItem)
-                     .getEntryId());
-             intent.putExtra("sessionName", mSessions.get(mSelectedMenuItem)
-                     .getName());
-             intent.putExtra("sessionURL", mSessions.get(mSelectedMenuItem)
-                     .getUrl());
-             startActivityForResult(intent, mEditSession);
-             break;
          case android.R.id.home:
              intent = new Intent(getBaseContext(), FileDisplayActivity.class);
              intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
          super.onDestroy();
      }
      
-     @Override
-     /**
-      * Updates various summaries after updates. Also starts and stops 
-      * the
-      */
-     public boolean onPreferenceChange(Preference preference, Object newValue) {
-         // Update current account summary
-         /*if (preference.equals(mAccountList)) {
-             mAccountList.setSummary(newValue.toString());
-         }
-         // Update tracking interval summary
-         else*/ if (preference.equals(mTrackingUpdateInterval)) {
-             String trackingSummary = getResources().getString(
-                     R.string.prefs_trackmydevice_interval_summary);
-             trackingSummary = String.format(trackingSummary,
-                     newValue.toString());
-             mTrackingUpdateInterval.setSummary(trackingSummary);
-         }
-         // Start or stop tracking service
-         else if (preference.equals(mDeviceTracking)) {
-             Intent locationServiceIntent = new Intent();
-             locationServiceIntent
-                     .setAction("com.owncloud.android.location.LocationLauncher");
-             locationServiceIntent.putExtra("TRACKING_SETTING",
-                     (Boolean) newValue);
-             sendBroadcast(locationServiceIntent);
-         } 
-         return true;
-     }
  }
@@@ -28,18 -28,17 +28,18 @@@ import android.widget.ListAdapter
  import android.widget.ListView;\r
  import android.widget.TextView;\r
  \r
 +\r
 +import java.util.Vector;\r
 +\r
  import com.owncloud.android.DisplayUtils;\r
  import com.owncloud.android.R;\r
  import com.owncloud.android.authentication.AccountUtils;\r
- import com.owncloud.android.datamodel.DataStorageManager;\r
+ import com.owncloud.android.datamodel.FileDataStorageManager;\r
  import com.owncloud.android.datamodel.OCFile;\r
  import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
  import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
  import com.owncloud.android.ui.activity.TransferServiceGetter;\r
  \r
 -import java.util.Vector;\r
 -\r
  \r
  /**\r
   * This Adapter populates a ListView with all files and folders in an ownCloud\r
@@@ -52,9 -51,12 +52,9 @@@ public class FileListListAdapter extend
      private Context mContext;\r
      private OCFile mFile = null;\r
      private Vector<OCFile> mFiles = null;\r
-     private DataStorageManager mStorageManager;\r
+     private FileDataStorageManager mStorageManager;\r
      private Account mAccount;\r
      private TransferServiceGetter mTransferServiceGetter;\r
 -    //total size of a directory (recursive)\r
 -    private Long totalSizeOfDirectoriesRecursive = null;\r
 -    private Long lastModifiedOfAllSubdirectories = null;\r
      \r
      public FileListListAdapter(Context context, TransferServiceGetter transferServiceGetter) {\r
          mContext = context;\r
              TextView lastModV = (TextView) view.findViewById(R.id.last_mod);\r
              ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);\r
              \r
-             if (!file.isDirectory()) {\r
+             if (!file.isFolder()) {\r
                  fileSizeV.setVisibility(View.VISIBLE);\r
                  fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
                  lastModV.setVisibility(View.VISIBLE);\r
       * @param directory                 New file to adapt. Can be NULL, meaning "no content to adapt".\r
       * @param updatedStorageManager     Optional updated storage manager; used to replace mStorageManager if is different (and not NULL)\r
       */\r
-     public void swapDirectory(OCFile directory, DataStorageManager updatedStorageManager) {\r
+     public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager) {\r
          mFile = directory;\r
          if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {\r
              mStorageManager = updatedStorageManager;\r
              mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
          }\r
          if (mStorageManager != null) {\r
-             mFiles = mStorageManager.getDirectoryContent(mFile);\r
+             mFiles = mStorageManager.getFolderContent(mFile);\r
          } else {\r
              mFiles = null;\r
          }\r
@@@ -48,17 -48,17 +48,17 @@@ import com.owncloud.android.Log_OC
  import com.owncloud.android.R;
  import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
 -import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
  import com.owncloud.android.files.services.FileObserverService;
  import com.owncloud.android.files.services.FileUploader;
 +import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
  import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
  import com.owncloud.android.operations.OnRemoteOperationListener;
  import com.owncloud.android.operations.RemoteOperation;
  import com.owncloud.android.operations.RemoteOperationResult;
 -import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
  import com.owncloud.android.operations.RemoveFileOperation;
  import com.owncloud.android.operations.RenameFileOperation;
  import com.owncloud.android.operations.SynchronizeFileOperation;
 +import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
  import com.owncloud.android.ui.activity.ConflictsResolveActivity;
  import com.owncloud.android.ui.activity.FileActivity;
  import com.owncloud.android.ui.activity.FileDisplayActivity;
@@@ -66,7 -66,6 +66,7 @@@ import com.owncloud.android.ui.dialog.E
  import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
  import com.owncloud.android.ui.preview.PreviewImageFragment;
  
 +
  import eu.alefzero.webdav.OnDatatransferProgressListener;
  
  /**
@@@ -417,7 -416,7 +417,7 @@@ public class FileDetailFragment extend
      private void renameFile() {
          OCFile file = getFile();
          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();
          EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
          dialog.show(getFragmentManager(), "nameeditdialog");
              }
              
          } 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());
              
              // update ui 
                  i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mAccount);
                  startActivity(i);
                  
-             } else {
-                 Toast msg = Toast.makeText(getActivity(), R.string.sync_file_fail_msg, Toast.LENGTH_LONG); 
-                 msg.show();
-             }
+             } 
              
              if (file.isDown()) {
                  setButtonsForDown();
@@@ -24,7 -24,7 +24,7 @@@ import java.util.List
  import com.owncloud.android.Log_OC;
  import com.owncloud.android.R;
  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.files.FileHandler;
  import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
@@@ -43,7 -43,6 +43,7 @@@ import com.owncloud.android.ui.fragment
  import com.owncloud.android.ui.preview.PreviewImageFragment;
  import com.owncloud.android.ui.preview.PreviewMediaFragment;
  
 +
  import android.accounts.Account;
  import android.app.Activity;
  import android.os.Bundle;
@@@ -122,24 -121,52 +122,52 @@@ public class OCFileListFragment extend
  
  
      /**
-      * 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;
+         int moveCount = 0;
          
          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
      public void onItemClick(AdapterView<?> l, View v, int position, long id) {
          OCFile file = (OCFile) mAdapter.getItem(position);
          if (file != null) {
-             if (file.isDirectory()) { 
+             if (file.isFolder()) { 
                  // update state and view of this fragment
                  listDirectory(file);
                  // then, notify parent activity to let it update its state and view, and other fragments
          List<Integer> toDisable = new ArrayList<Integer>();  
          
          MenuItem item = null;
-         if (targetFile.isDirectory()) {
+         if (targetFile.isFolder()) {
              // contextual menu for folders
              toHide.add(R.id.action_open_file_with);
              toHide.add(R.id.action_download_file);
          switch (item.getItemId()) {
              case R.id.action_rename_file: {
                  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();
                  EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
                  dialog.show(getFragmentManager(), EditNameDialog.TAG);
                  int messageStringId = R.string.confirmation_remove_alert;
                  int posBtnStringId = R.string.confirmation_remove_remote;
                  int neuBtnStringId = -1;
-                 if (mTargetFile.isDirectory()) {
+                 if (mTargetFile.isFolder()) {
                      messageStringId = R.string.confirmation_remove_folder_alert;
                      posBtnStringId = R.string.confirmation_remove_remote_and_local;
                      neuBtnStringId = R.string.confirmation_remove_folder_local;
              }
              case R.id.action_sync_file: {
                  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());
                  ((FileDisplayActivity) getSherlockActivity()).showLoadingDialog();
                  return true;
       * @param directory File to be listed
       */
      public void listDirectory(OCFile directory) {
-         DataStorageManager storageManager = mContainerActivity.getStorageManager();
+         FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
          if (storageManager != null) {
  
              // Check input parameters for null
          
          
              // 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());
                  directory = storageManager.getFileById(directory.getParentId());
              }
                  mList.setSelectionFromTop(0, 0);
              }
              mFile = directory;
          }
      }
      
          public void startMediaPreview(OCFile file, int i, boolean b);
  
          public void startImagePreview(OCFile file);
+         
+         public void startSyncFolderOperation(OCFile folder);
  
          /**
           * Getter for the current DataStorageManager in the container activity
           */
-         public DataStorageManager getStorageManager();
+         public FileDataStorageManager getStorageManager();
          
          
          /**
      
      @Override
      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();
          mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
      }
@@@ -35,21 -35,20 +35,20 @@@ import android.view.View.OnTouchListene
  import com.actionbarsherlock.app.ActionBar;
  import com.actionbarsherlock.view.MenuItem;
  import com.actionbarsherlock.view.Window;
 +import com.owncloud.android.Log_OC;
 +import com.owncloud.android.R;
  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.files.services.FileDownloader;
 -import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
  import com.owncloud.android.files.services.FileUploader;
 +import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
  import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
  import com.owncloud.android.ui.activity.FileActivity;
  import com.owncloud.android.ui.activity.FileDisplayActivity;
  import com.owncloud.android.ui.dialog.LoadingDialog;
  import com.owncloud.android.ui.fragment.FileFragment;
  
 -import com.owncloud.android.Log_OC;
 -import com.owncloud.android.R;
  
  /**
   *  Holds a swiping galley where image files contained in an ownCloud directory are shown
@@@ -67,7 -66,7 +66,7 @@@ public class PreviewImageActivity exten
      
      private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
      
-     private DataStorageManager mStorageManager;
+     private FileDataStorageManager mStorageManager;
      
      private ViewPager mViewPager; 
      private PreviewImagePagerAdapter mPreviewImagePagerAdapter;    
          //OCFile parentFolder = mStorageManager.getFileById(getFile().getParentId());
          if (parentFolder == null) {
              // should not be necessary
-             parentFolder = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
+             parentFolder = mStorageManager.getFileByPath(OCFile.ROOT_PATH);
          }
          mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), mStorageManager);
          mViewPager = (ViewPager) findViewById(R.id.fragmentPager);
              mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());            
              
              // 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());
              
              if (file != null) {
@@@ -22,16 -22,15 +22,16 @@@ import java.util.Map
  import java.util.Set;
  import java.util.Vector;
  
- import com.owncloud.android.datamodel.DataStorageManager;
 +import com.owncloud.android.datamodel.OCFile;
 +import com.owncloud.android.ui.fragment.FileFragment;
 +
  import android.accounts.Account;
  import android.support.v4.app.Fragment;
  import android.support.v4.app.FragmentManager;
  import android.support.v4.app.FragmentStatePagerAdapter;
  import android.view.ViewGroup;
  
 -import com.owncloud.android.datamodel.OCFile;
 -import com.owncloud.android.ui.fragment.FileFragment;
+ import com.owncloud.android.datamodel.FileDataStorageManager;
  
  /**
   * Adapter class that provides Fragment instances  
@@@ -46,7 -45,7 +46,7 @@@ public class PreviewImagePagerAdapter e
      private Set<Object> mObsoleteFragments;
      private Set<Integer> mObsoletePositions;
      private Set<Integer> mDownloadErrors;
-     private DataStorageManager mStorageManager;
+     private FileDataStorageManager mStorageManager;
      
      private Map<Integer, FileFragment> mCachedFragments;
  
@@@ -57,7 -56,7 +57,7 @@@
       * @param parentFolder      Folder where images will be searched for.
       * @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);
          
          if (fragmentManager == null) {
@@@ -72,7 -71,7 +72,7 @@@
  
          mAccount = account;
          mStorageManager = storageManager;
-         mImageFiles = mStorageManager.getDirectoryImages(parentFolder); 
+         mImageFiles = mStorageManager.getFolderImages(parentFolder); 
          mObsoleteFragments = new HashSet<Object>();
          mObsoletePositions = new HashSet<Integer>();
          mDownloadErrors = new HashSet<Integer>();
  
  package com.owncloud.android.ui.preview;
  
- import com.owncloud.android.datamodel.DataStorageManager;
 +import com.owncloud.android.Log_OC;
 +import com.owncloud.android.R;
 +import com.owncloud.android.authentication.AccountUtils;
 +import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;
 +import com.owncloud.android.datamodel.FileDataStorageManager;
 +import com.owncloud.android.datamodel.OCFile;
 +import com.owncloud.android.media.MediaService;
 +import com.owncloud.android.ui.activity.FileActivity;
 +
  import android.accounts.Account;
  import android.app.AlertDialog;
  import android.content.DialogInterface;
@@@ -40,7 -30,15 +39,6 @@@ import android.os.Bundle
  import android.widget.MediaController;
  import android.widget.VideoView;
  
 -import com.owncloud.android.Log_OC;
 -import com.owncloud.android.R;
 -import com.owncloud.android.datamodel.FileDataStorageManager;
 -import com.owncloud.android.authentication.AccountUtils;
 -import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;
 -import com.owncloud.android.datamodel.OCFile;
 -import com.owncloud.android.media.MediaService;
 -import com.owncloud.android.ui.activity.FileActivity;
--
  /**
   *  Activity implementing a basic video player.
   * 
@@@ -61,7 -59,7 +59,7 @@@ public class PreviewVideoActivity exten
      
      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 boolean mAutoplay;                  // when 'true', the playback starts immediately with the activity
@@@ -43,11 -43,10 +43,11 @@@ import org.apache.http.params.CoreProto
  
  import com.owncloud.android.Log_OC;
  import com.owncloud.android.MainApp;
 -
  import com.owncloud.android.network.BearerAuthScheme;
  import com.owncloud.android.network.BearerCredentials;
  
 +
 +
  import android.net.Uri;
  
  public class WebdavClient extends HttpClient {
          try {
              method.setFollowRedirects(mFollowRedirects);
          } 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;
          }
          if (mSsoSessionCookie != null && mSsoSessionCookie.length() > 0) {
@@@ -25,11 -25,10 +25,11 @@@ import org.apache.jackrabbit.webdav.pro
  
  import com.owncloud.android.Log_OC;
  
 +
  import android.net.Uri;
  
  public class WebdavEntry {
-     private String mName, mPath, mUri, mContentType;
+     private String mName, mPath, mUri, mContentType, mEtag;
      private long mContentLength, mCreateTimestamp, mModifiedTimestamp;
  
      public WebdavEntry(MultiStatusResponse ms, String splitElement) {
              DavPropertySet propSet = ms.getProperties(status);
              @SuppressWarnings("rawtypes")
              DavProperty prop = propSet.get(DavPropertyName.DISPLAYNAME);
-             if (prop != null)
+             if (prop != null) {
                  mName = (String) prop.getName().toString();
+                 mName = mName.substring(1, mName.length()-1);
+             }
              else {
                  String[] tmp = mPath.split("/");
                  if (tmp.length > 0)
                          .parseResponseDate((String) prop.getValue());
                  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 {
              Log_OC.e("WebdavEntry",
      public long modifiedTimestamp() {
          return mModifiedTimestamp;
      }
+     
+     public String etag() {
+         return mEtag;
+     }
  
      private void resetData() {
          mName = mUri = mContentType = null;