Merge remote-tracking branch 'remotes/upstream/master' into avoidDuplicateFiles_Master
authortobiasKaminsky <tobias@kaminsky.me>
Sat, 21 Nov 2015 07:48:40 +0000 (08:48 +0100)
committertobiasKaminsky <tobias@kaminsky.me>
Sat, 21 Nov 2015 07:48:40 +0000 (08:48 +0100)
1  2 
res/values/strings.xml
src/com/owncloud/android/datamodel/FileDataStorageManager.java
src/com/owncloud/android/files/services/FileUploader.java
src/com/owncloud/android/operations/UploadFileOperation.java

diff --combined res/values/strings.xml
@@@ -74,6 -74,7 +74,7 @@@
      <string name="file_list_seconds_ago">seconds ago</string>
      <string name="file_list_empty">Nothing in here. Upload something!</string>
      <string name="file_list_loading">Loading&#8230;</string>
+     <string name="file_list_no_app_for_file_type">No App found for file type!</string>
      <string name="local_file_list_empty">There are no files in this folder.</string>
      <string name="filedetails_select_file">Tap on a file to display additional information.</string>
      <string name="filedetails_size">Size:</string>
      <string name="filedetails_created">Created:</string>
      <string name="filedetails_modified">Modified:</string>
      <string name="filedetails_download">Download</string>
-     <string name="filedetails_sync_file">Refresh file</string>
+     <string name="filedetails_sync_file">Synchronize</string>
      <string name="filedetails_renamed_in_upload_msg">File was renamed to %1$s during upload</string>
      <string name="list_layout">List Layout</string>
      <string name="action_share_file">Share link</string>
      <string name="action_unshare_file">Unshare link</string>
+     <string name="action_share_with_users">Share with users</string>
      <string name="common_yes">Yes</string>
      <string name="common_no">No</string>
      <string name="common_ok">OK</string>
-     <string name="common_cancel_download">Cancel download</string>
-     <string name="common_cancel_upload">Cancel upload</string>
+     <string name="common_cancel_sync">Cancel synchronization</string>
      <string name="common_cancel">Cancel</string>
      <string name="common_save_exit">Save &amp; Exit</string>
      <string name="common_error">Error</string>
      <string name="ssl_validator_label_validity_to">To:</string>
      <string name="ssl_validator_label_signature">Signature:</string>
      <string name="ssl_validator_label_signature_algorithm">Algorithm:</string>
+     <string name="digest_algorithm_not_available">This digest algorithm is not available on your phone.</string>
+     <string name="ssl_validator_label_certificate_fingerprint">Fingerprint:</string>
+     <string name="certificate_load_problem">There is a problem loading the certificate.</string>
      <string name="ssl_validator_null_cert">The certificate could not be shown.</string>
      <string name="ssl_validator_no_info_about_error">- No information about the error</string>
  
        <string name="prefs_category_security">Security</string>
  
        <string name="prefs_instant_video_upload_path_title">Upload Video Path</string>
-     <string name="download_folder_failed_content">Download of %1$s folder could not be completed</string>
+     <string name="sync_folder_failed_content">Synchronization of %1$s folder could not be completed</string>
  
        <string name="shared_subject_header">shared</string>
        <string name="with_you_subject_header">with you</string>
      
-       <string name="subject_token">%1$s shared \"%2$s\" with you</string>
-     <string name="saml_subject_token">\"%1$s\" has been shared with you</string>
+       <string name="subject_user_shared_with_you">%1$s shared \"%2$s\" with you</string>
+     <string name="subject_shared_with_you">\"%1$s\" has been shared with you</string>
  
      <string name="auth_refresh_button">Refresh connection</string>
      <string name="auth_host_address">Server address</string>
      <string name="file_list__footer__files">%1$d files</string>
      <string name="file_list__footer__files_and_folder">%1$d files, 1 folder</string>
      <string name="file_list__footer__files_and_folders">%1$d files, %2$d folders</string>
 +    <string name="prefs_instant_behaviour_dialogTitle">Upload file to server and ...</string>
 +    <string name="prefs_instant_behaviour_title">Behaviour</string>
 +    <string name="upload_copy_files">Copy file</string>
 +    <string name="upload_move_files">Move file</string>
 +
 +    <string name="pref_behaviour_entries_do_nothing">do nothing</string>
 +    <string name="pref_behaviour_entries_copy">copy file to OC folder</string>
 +    <string name="pref_behaviour_entries_move">move file to OC folder</string>
 +    <string name="pref_behaviour_entries_delete">delete origin file</string>
  
+     <string name="share_dialog_title">Sharing</string>
+     <string name="share_with_user_section_title">Share with Users and Groups</string>
+     <string name="share_no_users">No data shared with users yet</string>
+     <string name="share_add_user_or_group">Add User or Group</string>
+     <string name="share_search">Search</string>
+     <string name="search_users_and_groups_hint">Search users and groups</string>
+     <string name="share_group_clarification">%1$s (group)</string>
+     <string name="share_sharee_unavailable">Sorry, your server version does not allow share with users within clients.
+         \nPlease contact your administrator</string>
  </resources>
@@@ -24,8 -24,10 +24,10 @@@ import java.io.File
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
+ import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
+ import java.util.Set;
  import java.util.Vector;
  
  import android.accounts.Account;
@@@ -48,6 -50,8 +50,8 @@@ import com.owncloud.android.lib.common.
  import com.owncloud.android.lib.resources.files.FileUtils;
  import com.owncloud.android.lib.resources.shares.OCShare;
  import com.owncloud.android.lib.resources.shares.ShareType;
+ import com.owncloud.android.lib.resources.status.CapabilityBooleanType;
+ import com.owncloud.android.lib.resources.status.OCCapability;
  import com.owncloud.android.utils.FileStorageUtils;
  
  import java.io.FileInputStream;
@@@ -88,18 -92,10 +92,10 @@@ public class FileDataStorageManager 
          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 Vector<OCFile> getFolderImages(OCFile folder/*, boolean onlyOnDevice*/) {
-         Vector<OCFile> ret = new Vector<OCFile>(); 
+         Vector<OCFile> ret = new Vector<OCFile>();
          if (folder != null) {
              // TODO better implementation, filtering in the access to database instead of here
              // TODO Enable when "On Device" is recovered ?
              Vector<OCFile> tmp = getFolderContent(folder/*, onlyOnDevice*/);
-             OCFile current = null; 
+             OCFile current = null;
              for (int i=0; i<tmp.size(); i++) {
                  current = tmp.get(i);
                  if (current.isImage()) {
          boolean overriden = false;
          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_MODIFIED_AT_LAST_SYNC_FOR_DATA,
+                 file.getModificationTimestampAtLastSyncForData()
          );
          cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
          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() != DataStorageManager.ROOT_PARENT_ID)
          cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
          cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
          if (!file.isFolder())
          cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
          cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isFavorite() ? 1 : 0);
          cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
-         cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, file.isShareByLink() ? 1 : 0);
+         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
+         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
          cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
          cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
          cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
          cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail());
          cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading());
-         
+         cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict());
          boolean sameRemotePath = fileExists(file.getRemotePath());
-         if (sameRemotePath ||                fileExists(file.getFileId())) {           // for renamed files; no more delete and create
+         if (sameRemotePath ||
+                 fileExists(file.getFileId())) {  // for renamed files; no more delete and create
  
-             OCFile oldFile = null;
+             OCFile oldFile;
              if (sameRemotePath) {
                  oldFile = getFileByPath(file.getRemotePath());
                  file.setFileId(oldFile.getFileId());
              }
          }
  
- //        if (file.isFolder()) {
- //            updateFolderSize(file.getFileId());
- //        } else {
- //            updateFolderSize(file.getParentId());
- //        }
          return overriden;
      }
  
       */
      public void saveFolder(
              OCFile folder, Collection<OCFile> updatedFiles, Collection<OCFile> filesToRemove
-         ) {
-         
-         Log_OC.d(TAG,  "Saving folder " + folder.getRemotePath() + " with " + updatedFiles.size() 
+     ) {
+         Log_OC.d(TAG,  "Saving folder " + folder.getRemotePath() + " with " + updatedFiles.size()
                  + " children and " + filesToRemove.size() + " files to remove");
  
-         ArrayList<ContentProviderOperation> operations = 
+         ArrayList<ContentProviderOperation> operations =
                  new ArrayList<ContentProviderOperation>(updatedFiles.size());
  
          // prepare operations to insert or update files to save in the given folder
              ContentValues cv = new ContentValues();
              cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
              cv.put(
-                 ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, 
-                 file.getModificationTimestampAtLastSyncForData()
+                     ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,
+                     file.getModificationTimestampAtLastSyncForData()
              );
              cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
              cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
              cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
              cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isFavorite() ? 1 : 0);
              cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
-             cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, file.isShareByLink() ? 1 : 0);
+             cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
+             cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
              cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
              cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
              cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
              cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail());
              cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading());
+             cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict());
  
              boolean existsByPath = fileExists(file.getRemotePath());
              if (existsByPath || fileExists(file.getFileId())) {
          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());
-                     
-                     File localFolder = 
+                     File localFolder =
                              new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
                      if (localFolder.exists()) {
                          removeLocalFolder(localFolder);
          ContentValues cv = new ContentValues();
          cv.put(ProviderTableMeta.FILE_MODIFIED, folder.getModificationTimestamp());
          cv.put(
-             ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, 
-             folder.getModificationTimestampAtLastSyncForData()
+                 ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,
+                 folder.getModificationTimestampAtLastSyncForData()
          );
          cv.put(ProviderTableMeta.FILE_CREATION, folder.getCreationTimestamp());
          cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, 0);
          cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, folder.getLastSyncDateForData());
          cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, folder.isFavorite() ? 1 : 0);
          cv.put(ProviderTableMeta.FILE_ETAG, folder.getEtag());
-         cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, folder.isShareByLink() ? 1 : 0);
+         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, folder.isSharedViaLink() ? 1 : 0);
+         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, folder.isSharedWithSharee() ? 1 : 0);
          cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, folder.getPublicLink());
          cv.put(ProviderTableMeta.FILE_PERMISSIONS, folder.getPermissions());
          cv.put(ProviderTableMeta.FILE_REMOTE_ID, folder.getRemoteId());
              }
          }
  
-         //updateFolderSize(folder.getFileId());
      }
  
  
- //    /**
- //     * 
- //     * @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, 
- //                        new ContentValues(),    
-                             // won't be used, but cannot be null; crashes in KLP
- //                        ProviderTableMeta._ID + "=?",
- //                        new String[] { String.valueOf(id) });
- //            } else {
- //                try {
- //                    getContentProviderClient().update(ProviderTableMeta.CONTENT_URI_DIR, 
- //                            new ContentValues(),    
-                                 // won't be used, but cannot be null; crashes in KLP
- //                            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 boolean removeFile(OCFile file, boolean removeDBData, boolean removeLocalCopy) {
          boolean success = true;
          if (file != null) {
  
              } 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 + "=?";
+                     //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()};
                      int deleted = 0;
                      if (getContentProviderClient() != null) {
                          // maybe unnecessary, but should be checked TODO remove if unnecessary
                          file.setStoragePath(null);
                          saveFile(file);
+                         saveConflict(file, null);
                      }
                  }
              }
      private boolean 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 " + 
+         String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?" + " AND " +
                  ProviderTableMeta.FILE_PATH + "=?";
          String [] whereArgs = new String[]{mAccount.name, folder.getRemotePath()};
          int deleted = 0;
          return success;
      }
  
-     
      /**
       * Updates database and file system for a file or folder that was moved to a different location.
       *
  
              OCFile targetParent = getFileByPath(targetParentPath);
              if (targetParent == null) {
-                 throw new IllegalStateException("Parent folder of the target path does not exist!!");
+                 throw new IllegalStateException(
+                         "Parent folder of the target path does not exist!!");
              }
  
              /// 1. get all the descendants of the moved element in a single QUERY
                          // update link to downloaded content - but local move is not done here!
                          String targetLocalPath = defaultSavePath + targetPath +
                                  child.getStoragePath().substring(lengthOfOldStoragePath);
-                         
                          cv.put(ProviderTableMeta.FILE_STORAGE_PATH, targetLocalPath);
-                         
                          originalPathsToTriggerMediaScan.add(child.getStoragePath());
                          newPathsToTriggerMediaScan.add(targetLocalPath);
-                         
                      }
                      if (child.getRemotePath().equals(file.getRemotePath())) {
                          cv.put(
                  }
  
              } catch (Exception e) {
-                 Log_OC.e(TAG, "Fail to update " + file.getFileId() + " and descendants in database", e);
+                 Log_OC.e(TAG, "Fail to update " + file.getFileId() + " and descendants in database",
+                         e);
              }
  
              /// 4. move in local file system 
          return ret;
      }
  
-     
      private Vector<OCFile> getFolderContent(long parentId/*, boolean onlyOnDevice*/) {
  
          Vector<OCFile> ret = new Vector<OCFile>();
                  OCFile child = createFileInstance(c);
                  // TODO Enable when "On Device" is recovered ?
                  // if (child.isFolder() || !onlyOnDevice || onlyOnDevice && child.isDown()){
-                     ret.add(child);
+                 ret.add(child);
                  // }
              } while (c.moveToNext());
          }
              file.setFavorite(c.getInt(
                      c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false);
              file.setEtag(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG)));
-             file.setShareByLink(c.getInt(
-                     c.getColumnIndex(ProviderTableMeta.FILE_SHARE_BY_LINK)) == 1 ? true : false);
+             file.setShareViaLink(c.getInt(
+                     c.getColumnIndex(ProviderTableMeta.FILE_SHARED_VIA_LINK)) == 1 ? true : false);
+             file.setShareWithSharee(c.getInt(
+                     c.getColumnIndex(ProviderTableMeta.FILE_SHARED_WITH_SHAREE)) == 1 ? true : false);
              file.setPublicLink(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PUBLIC_LINK)));
              file.setPermissions(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PERMISSIONS)));
              file.setRemoteId(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_REMOTE_ID)));
                      c.getColumnIndex(ProviderTableMeta.FILE_UPDATE_THUMBNAIL)) == 1 ? true : false);
              file.setDownloading(c.getInt(
                      c.getColumnIndex(ProviderTableMeta.FILE_IS_DOWNLOADING)) == 1 ? true : false);
-                     
-         }
-         return file;
-     }
+             file.setEtagInConflict(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG_IN_CONFLICT)));
  
-     /**
-      * Returns if the file/folder is shared by link or not
-      *
-      * @param path Path of the file/folder
-      * @return
-      */
-     public boolean isShareByLink(String path) {
-         Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path);
-         OCFile file = null;
-         if (c.moveToFirst()) {
-             file = createFileInstance(c);
          }
-         c.close();
-         return file.isShareByLink();
-     }
-     /**
-      * Returns the public link of the file/folder
-      *
-      * @param path Path of the file/folder
-      * @return
-      */
-     public String getPublicLink(String path) {
-         Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path);
-         OCFile file = null;
-         if (c.moveToFirst()) {
-             file = createFileInstance(c);
-         }
-         c.close();
-         return file.getPublicLink();
+         return file;
      }
  
      // Methods for Shares
      public boolean saveShare(OCShare share) {
          boolean overriden = false;
          cv.put(ProviderTableMeta.OCSHARES_EXPIRATION_DATE, share.getExpirationDate());
          cv.put(ProviderTableMeta.OCSHARES_TOKEN, share.getToken());
          cv.put(
-             ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME, 
-             share.getSharedWithDisplayName()
+                 ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME,
+                 share.getSharedWithDisplayName()
          );
          cv.put(ProviderTableMeta.OCSHARES_IS_DIRECTORY, share.isFolder() ? 1 : 0);
          cv.put(ProviderTableMeta.OCSHARES_USER_ID, share.getUserId());
          cv.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, share.getIdRemoteShared());
          cv.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, mAccount.name);
  
-         if (shareExists(share.getIdRemoteShared())) {           // for renamed files; no more delete and create
+         if (shareExists(share.getIdRemoteShared())) {// for renamed files; no more delete and create
              overriden = true;
              if (getContentResolver() != null) {
                  getContentResolver().update(ProviderTableMeta.CONTENT_URI_SHARE, cv,
      }
  
  
-     public OCShare getFirstShareByPathAndType(String path, ShareType type) {
+     public OCShare getFirstShareByPathAndType(String path, ShareType type, String shareWith) {
          Cursor c = null;
+         String selection = ProviderTableMeta.OCSHARES_PATH + "=? AND "
+                 + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? AND "
+                 + ProviderTableMeta.OCSHARES_SHARE_WITH + "=? AND "
+                 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?" ;
+         String [] selectionArgs =  new String[]{path, Integer.toString(type.getValue()),
+                 shareWith, mAccount.name};
          if (getContentResolver() != null) {
              c = getContentResolver().query(
                      ProviderTableMeta.CONTENT_URI_SHARE,
                      null,
-                     ProviderTableMeta.OCSHARES_PATH + "=? AND "
-                             + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? AND "
-                             + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
-                     new String[]{path, Integer.toString(type.getValue()), mAccount.name},
+                     selection, selectionArgs,
                      null);
          } else {
              try {
                  c = getContentProviderClient().query(
                          ProviderTableMeta.CONTENT_URI_SHARE,
                          null,
-                         ProviderTableMeta.OCSHARES_PATH + "=? AND "
-                                 + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? AND "
-                                 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
-                         new String[]{path, Integer.toString(type.getValue()), mAccount.name},
+                         selection, selectionArgs,
                          null);
  
              } catch (RemoteException e) {
                      .getColumnIndex(ProviderTableMeta.OCSHARES_ITEM_SOURCE)));
              share.setShareType(ShareType.fromValue(c.getInt(c
                      .getColumnIndex(ProviderTableMeta.OCSHARES_SHARE_TYPE))));
+             share.setShareWith(c.getString(c
+                     .getColumnIndex(ProviderTableMeta.OCSHARES_SHARE_WITH)));
              share.setPermissions(c.getInt(c
                      .getColumnIndex(ProviderTableMeta.OCSHARES_PERMISSIONS)));
              share.setSharedDate(c.getLong(c
              share.setIsFolder(c.getInt(
                      c.getColumnIndex(ProviderTableMeta.OCSHARES_IS_DIRECTORY)) == 1);
              share.setUserId(c.getLong(c.getColumnIndex(ProviderTableMeta.OCSHARES_USER_ID)));
-             share.setIdRemoteShared(c.getLong(c.getColumnIndex(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED)));
+             share.setIdRemoteShared(c.getLong(
+                     c.getColumnIndex(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED)));
          }
          return share;
      }
          return shareExists(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, String.valueOf(remoteId));
      }
  
-     private void cleanSharedFiles() {
+     private void resetShareFlagsInAllFiles() {
          ContentValues cv = new ContentValues();
-         cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, false);
+         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, false);
+         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, false);
          cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
          String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
          String[] whereArgs = new String[]{mAccount.name};
  
          } else {
              try {
-                 getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
+                 getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where,
+                         whereArgs);
              } catch (RemoteException e) {
-                 Log_OC.e(TAG, "Exception in cleanSharedFiles" + e.getMessage());
+                 Log_OC.e(TAG, "Exception in resetShareFlagsInAllFiles" + e.getMessage());
              }
          }
      }
  
-     private void cleanSharedFilesInFolder(OCFile folder) {
+     private void resetShareFlagsInFolder(OCFile folder) {
          ContentValues cv = new ContentValues();
-         cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, false);
+         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, false);
+         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, false);
          cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
          String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
                  ProviderTableMeta.FILE_PARENT + "=?";
          String [] whereArgs = new String[] { mAccount.name , String.valueOf(folder.getFileId()) };
-         
          if (getContentResolver() != null) {
              getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
  
          } else {
              try {
-                 getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
+                 getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where,
+                         whereArgs);
              } catch (RemoteException e) {
-                 Log_OC.e(TAG, "Exception in cleanSharedFilesInFolder " + e.getMessage());
+                 Log_OC.e(TAG, "Exception in resetShareFlagsInFolder " + e.getMessage());
+             }
+         }
+     }
+     private void resetShareFlagInAFile(String filePath){
+         ContentValues cv = new ContentValues();
+         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, false);
+         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, false);
+         cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
+         String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
+                 ProviderTableMeta.FILE_PATH+ "=?";
+         String [] whereArgs = new String[] { mAccount.name , filePath };
+         if (getContentResolver() != null) {
+             getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
+         } else {
+             try {
+                 getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where,
+                         whereArgs);
+             } catch (RemoteException e) {
+                 Log_OC.e(TAG, "Exception in resetShareFlagsInFolder " + e.getMessage());
              }
          }
      }
  
          } else {
              try {
-                 getContentProviderClient().delete(ProviderTableMeta.CONTENT_URI_SHARE, where, whereArgs);
+                 getContentProviderClient().delete(ProviderTableMeta.CONTENT_URI_SHARE, where,
+                         whereArgs);
              } catch (RemoteException e) {
                  Log_OC.e(TAG, "Exception in cleanShares" + e.getMessage());
              }
      public void saveShares(Collection<OCShare> shares) {
          cleanShares();
          if (shares != null) {
-             ArrayList<ContentProviderOperation> operations = 
+             ArrayList<ContentProviderOperation> operations =
                      new ArrayList<ContentProviderOperation>(shares.size());
  
              // prepare operations to insert or update files to save in the given folder
                  cv.put(ProviderTableMeta.OCSHARES_EXPIRATION_DATE, share.getExpirationDate());
                  cv.put(ProviderTableMeta.OCSHARES_TOKEN, share.getToken());
                  cv.put(
-                     ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME, 
-                     share.getSharedWithDisplayName()
+                         ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME,
+                         share.getSharedWithDisplayName()
                  );
                  cv.put(ProviderTableMeta.OCSHARES_IS_DIRECTORY, share.isFolder() ? 1 : 0);
                  cv.put(ProviderTableMeta.OCSHARES_USER_ID, share.getUserId());
                      // updating an existing file
                      operations.add(
                              ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI_SHARE).
-                             withValues(cv).
-                             withSelection(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
-                                     new String[]{String.valueOf(share.getIdRemoteShared())})
-                             .build());
+                                     withValues(cv).
+                                     withSelection(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
+                                             new String[]{String.valueOf(share.getIdRemoteShared())})
+                                     .build());
                  } else {
                      // adding a new file
                      operations.add(
                              ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI_SHARE).
-                             withValues(cv).
-                             build()
+                                     withValues(cv).
+                                     build()
                      );
                  }
              }
              if (operations.size() > 0) {
                  @SuppressWarnings("unused")
                  ContentProviderResult[] results = null;
-                 Log_OC.d(TAG, "Sending " + operations.size() + 
+                 Log_OC.d(TAG, "Sending " + operations.size() +
                          " operations to FileContentProvider");
                  try {
                      if (getContentResolver() != null) {
-                         results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
+                         results = getContentResolver().applyBatch(MainApp.getAuthority(),
+                                 operations);
                      } else {
                          results = getContentProviderClient().applyBatch(operations);
                      }
      }
  
      public void updateSharedFiles(Collection<OCFile> sharedFiles) {
-         cleanSharedFiles();
+         resetShareFlagsInAllFiles();
  
          if (sharedFiles != null) {
-             ArrayList<ContentProviderOperation> operations = 
+             ArrayList<ContentProviderOperation> operations =
                      new ArrayList<ContentProviderOperation>(sharedFiles.size());
  
              // prepare operations to insert or update files to save in the given folder
                  ContentValues cv = new ContentValues();
                  cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
                  cv.put(
-                     ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, 
-                     file.getModificationTimestampAtLastSyncForData()
+                         ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,
+                         file.getModificationTimestampAtLastSyncForData()
                  );
                  cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
                  cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
                  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()
+                         ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA,
+                         file.getLastSyncDateForData()
                  );
                  cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isFavorite() ? 1 : 0);
                  cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
-                 cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, file.isShareByLink() ? 1 : 0);
+                 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
+                 cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
                  cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
                  cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
                  cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
                  cv.put(
-                     ProviderTableMeta.FILE_UPDATE_THUMBNAIL, 
-                     file.needsUpdateThumbnail() ? 1 : 0
+                         ProviderTableMeta.FILE_UPDATE_THUMBNAIL,
+                         file.needsUpdateThumbnail() ? 1 : 0
                  );
                  cv.put(
                          ProviderTableMeta.FILE_IS_DOWNLOADING,
                          file.isDownloading() ? 1 : 0
                  );
+                 cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict());
  
                  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 + "=?",
-                                     new String[]{String.valueOf(file.getFileId())})
-                             .build());
+                                     withValues(cv).
+                                     withSelection(ProviderTableMeta._ID + "=?",
+                                             new String[]{String.valueOf(file.getFileId())})
+                                     .build());
  
                  } else {
                      // adding a new file
                      operations.add(
                              ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).
-                             withValues(cv).
-                             build()
+                                     withValues(cv).
+                                     build()
                      );
                  }
              }
              if (operations.size() > 0) {
                  @SuppressWarnings("unused")
                  ContentProviderResult[] results = null;
-                 Log_OC.d(TAG, "Sending " + operations.size() + 
+                 Log_OC.d(TAG, "Sending " + operations.size() +
                          " operations to FileContentProvider");
                  try {
                      if (getContentResolver() != null) {
      public void removeShare(OCShare share) {
          Uri share_uri = ProviderTableMeta.CONTENT_URI_SHARE;
          String where = ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?" + " AND " +
-                 ProviderTableMeta.FILE_PATH + "=?";
-         String [] whereArgs = new String[]{mAccount.name, share.getPath()};
+                 ProviderTableMeta._ID + "=?";
+         String [] whereArgs = new String[]{mAccount.name, Long.toString(share.getId())};
          if (getContentProviderClient() != null) {
              try {
                  getContentProviderClient().delete(share_uri, where, whereArgs);
      }
  
      public void saveSharesDB(ArrayList<OCShare> shares) {
-         saveShares(shares);
-         ArrayList<OCFile> sharedFiles = new ArrayList<OCFile>();
+         ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
  
-         for (OCShare share : shares) {
-             // Get the path
-             String path = share.getPath();
-             if (share.isFolder()) {
-                 path = path + FileUtils.PATH_SEPARATOR;
+         // Reset flags & Remove shares for this files
+         String filePath = "";
+         for (OCShare share: shares) {
+             if (filePath != share.getPath()){
+                 filePath = share.getPath();
+                 resetShareFlagInAFile(filePath);
+                 operations = prepareRemoveSharesInFile(filePath, operations);
              }
+         }
+        // Add operations to insert shares
+        operations = prepareInsertShares(shares, operations);
+         // apply operations in batch
+         if (operations.size() > 0) {
+             Log_OC.d(TAG, "Sending " + operations.size() + " operations to FileContentProvider");
+             try {
+                 if (getContentResolver() != null) {
+                     getContentResolver().applyBatch(MainApp.getAuthority(), operations);
  
-             // Update OCFile with data from share: ShareByLink  and publicLink
-             OCFile file = getFileByPath(path);
-             if (file != null) {
-                 if (share.getShareType().equals(ShareType.PUBLIC_LINK)) {
-                     file.setShareByLink(true);
-                     sharedFiles.add(file);
+                 } else {
+                     getContentProviderClient().applyBatch(operations);
                  }
+             } catch (OperationApplicationException e) {
+                 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
+             } catch (RemoteException e) {
+                 Log_OC.e(TAG, "Exception in batch of operations  " + e.getMessage());
              }
          }
  
-         updateSharedFiles(sharedFiles);
+ //        // TODO: review if it is needed
+ //        // Update shared files
+ //        ArrayList<OCFile> sharedFiles = new ArrayList<OCFile>();
+ //
+ //        for (OCShare share : shares) {
+ //            // Get the path
+ //            String path = share.getPath();
+ //            if (share.isFolder()) {
+ //                path = path + FileUtils.PATH_SEPARATOR;
+ //            }
+ //
+ //            // Update OCFile with data from share: ShareByLink, publicLink and
+ //            OCFile file = getFileByPath(path);
+ //            if (file != null) {
+ //                if (share.getShareType().equals(ShareType.PUBLIC_LINK)) {
+ //                    file.setShareViaLink(true);
+ //                    sharedFiles.add(file);
+ //                }
+ //            }
+ //        }
+ //
+ //        // TODO: Review
+ //        updateSharedFiles(sharedFiles);
      }
  
  
      public void saveSharesInFolder(ArrayList<OCShare> shares, OCFile folder) {
-         cleanSharedFilesInFolder(folder);
+         resetShareFlagsInFolder(folder);
          ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
          operations = prepareRemoveSharesInFolder(folder, operations);
  
          if (shares != null) {
              // prepare operations to insert or update files to save in the given folder
+             operations = prepareInsertShares(shares, operations);
+         }
+         // apply operations in batch
+         if (operations.size() > 0) {
+             Log_OC.d(TAG, "Sending " + operations.size() + " operations to FileContentProvider");
+             try {
+                 if (getContentResolver() != null) {
+                     getContentResolver().applyBatch(MainApp.getAuthority(), operations);
+                 } else {
+                     getContentProviderClient().applyBatch(operations);
+                 }
+             } catch (OperationApplicationException e) {
+                 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
+             } catch (RemoteException e) {
+             }
+         }
+     }
+     /**
+      * Prepare operations to insert or update files to save in the given folder
+      * @param shares        List of shares to insert
+      * @param operations    List of operations
+      * @return
+      */
+     private ArrayList<ContentProviderOperation> prepareInsertShares(
+             ArrayList<OCShare> shares, ArrayList<ContentProviderOperation> operations) {
+         if (shares != null) {
+             // prepare operations to insert or update files to save in the given folder
              for (OCShare share : shares) {
                  ContentValues cv = new ContentValues();
                  cv.put(ProviderTableMeta.OCSHARES_FILE_SOURCE, share.getFileSource());
                  cv.put(ProviderTableMeta.OCSHARES_EXPIRATION_DATE, share.getExpirationDate());
                  cv.put(ProviderTableMeta.OCSHARES_TOKEN, share.getToken());
                  cv.put(
-                     ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME, 
-                     share.getSharedWithDisplayName()
+                         ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME,
+                         share.getSharedWithDisplayName()
                  );
                  cv.put(ProviderTableMeta.OCSHARES_IS_DIRECTORY, share.isFolder() ? 1 : 0);
                  cv.put(ProviderTableMeta.OCSHARES_USER_ID, share.getUserId());
                  cv.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, share.getIdRemoteShared());
                  cv.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, mAccount.name);
  
-                 /*
-                 if (shareExists(share.getIdRemoteShared())) {
-                     // updating an existing share resource
-                     operations.add(
-                             ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI_SHARE).
-                             withValues(cv).
-                             withSelection(  ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?", 
-                                     new String[] { String.valueOf(share.getIdRemoteShared()) })
-                                     .build());
-                 } else {
-                 */
                  // adding a new share resource
                  operations.add(
                          ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI_SHARE).
-                         withValues(cv).
-                         build()
+                                 withValues(cv).
+                                 build()
                  );
-                 //}
-             }
-         }
-         // apply operations in batch
-         if (operations.size() > 0) {
-             @SuppressWarnings("unused")
-             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 = getContentProviderClient().applyBatch(operations);
-                 }
-             } catch (OperationApplicationException e) {
-                 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
-             } catch (RemoteException e) {
-                 Log_OC.e(TAG, "Exception in batch of operations  " + e.getMessage());
              }
          }
-         //}
+         return operations;
      }
  
      private ArrayList<ContentProviderOperation> prepareRemoveSharesInFolder(
  
              // TODO Enable when "On Device" is recovered ?
              Vector<OCFile> files = getFolderContent(folder /*, false*/);
-             
              for (OCFile file : files) {
                  whereArgs[0] = file.getRemotePath();
                  preparedOperations.add(
                          ContentProviderOperation.newDelete(ProviderTableMeta.CONTENT_URI_SHARE).
-                         withSelection(where, whereArgs).
-                         build()
+                                 withSelection(where, whereArgs).
+                                 build()
                  );
              }
          }
          return preparedOperations;
-         
-         /*
-         if (operations.size() > 0) {
-             try {
-                 if (getContentResolver() != null) {
-                     getContentResolver().applyBatch(MainApp.getAuthority(), operations);
  
-                 } else {
-                     getContentProviderClient().applyBatch(operations);
-                 }
+     }
  
-             } catch (OperationApplicationException e) {
-                 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
+     private ArrayList<ContentProviderOperation> prepareRemoveSharesInFile(
+             String filePath, ArrayList<ContentProviderOperation> preparedOperations) {
+         String where = ProviderTableMeta.OCSHARES_PATH + "=?" + " AND "
+                 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
+         String[] whereArgs = new String[]{filePath, mAccount.name};
+         preparedOperations.add(
+                 ContentProviderOperation.newDelete(ProviderTableMeta.CONTENT_URI_SHARE).
+                         withSelection(where, whereArgs).
+                         build()
+         );
+         return preparedOperations;
+     }
+     public ArrayList<OCShare> getSharesWithForAFile(String filePath, String accountName){
+         // Condition
+         String where = ProviderTableMeta.OCSHARES_PATH + "=?" + " AND "
+                 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?"+ "AND"
+                 + " (" + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? OR "
+                 + ProviderTableMeta.OCSHARES_SHARE_TYPE +  "=? ) ";
+         String [] whereArgs = new String[]{ filePath, accountName ,
+                 Integer.toString(ShareType.USER.getValue()),
+                 Integer.toString(ShareType.GROUP.getValue()) };
+         Cursor c = null;
+         if (getContentResolver() != null) {
+             c = getContentResolver().query(
+                     ProviderTableMeta.CONTENT_URI_SHARE,
+                     null, where, whereArgs, null);
+         } else {
+             try {
+                 c = getContentProviderClient().query(
+                         ProviderTableMeta.CONTENT_URI_SHARE,
+                         null, where, whereArgs, null);
  
              } catch (RemoteException e) {
-                 Log_OC.e(TAG, "Exception in batch of operations  " + e.getMessage());
+                 Log_OC.e(TAG, "Could not get list of shares with: " + e.getMessage());
+                 c = null;
              }
-         }            
-         */
-             
-             /*
-             if (getContentResolver() != null) {
-                 
-                 getContentResolver().delete(ProviderTableMeta.CONTENT_URI_SHARE, 
-                                             where,
-                                             whereArgs);
-             } else {
-                 try {
-                     getContentProviderClient().delete(  ProviderTableMeta.CONTENT_URI_SHARE, 
-                                                         where,
-                                                         whereArgs);
+         }
+         ArrayList<OCShare> shares = new ArrayList<OCShare>();
+         OCShare share = null;
+         if (c.moveToFirst()) {
+             do {
+                 share = createShareInstance(c);
+                 shares.add(share);
+                 // }
+             } while (c.moveToNext());
+         }
+         c.close();
  
-                 } catch (RemoteException e) {
-                     Log_OC.e(TAG, "Exception deleting shares in a folder " + e.getMessage());
-                 }
-             }
-             */
-         //}
+         return shares;
      }
  
 -    public void triggerMediaScan(String path) {
 +    public static void triggerMediaScan(String path) {
          Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
          intent.setData(Uri.fromFile(new File(path)));
          MainApp.getAppContext().sendBroadcast(intent);
  
      }
  
+     public void saveConflict(OCFile file, String etagInConflict) {
+         if (!file.isDown()) {
+             etagInConflict = null;
+         }
+         ContentValues cv = new ContentValues();
+         cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, etagInConflict);
+         int updated = 0;
+         if (getContentResolver() != null) {
+             updated = getContentResolver().update(
+                     ProviderTableMeta.CONTENT_URI_FILE,
+                     cv,
+                     ProviderTableMeta._ID + "=?",
+                     new String[] { String.valueOf(file.getFileId())}
+             );
+         } else {
+             try {
+                 updated = getContentProviderClient().update(
+                         ProviderTableMeta.CONTENT_URI_FILE,
+                         cv,
+                         ProviderTableMeta._ID + "=?",
+                         new String[]{String.valueOf(file.getFileId())}
+                 );
+             } catch (RemoteException e) {
+                 Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage());
+             }
+         }
+         Log_OC.d(TAG, "Number of files updated with CONFLICT: " + updated);
+         if (updated > 0) {
+             if (etagInConflict != null) {
+                 /// set conflict in all ancestor folders
+                 long parentId = file.getParentId();
+                 Set<String> ancestorIds = new HashSet<String>();
+                 while (parentId != FileDataStorageManager.ROOT_PARENT_ID) {
+                     ancestorIds.add(Long.toString(parentId));
+                     parentId = getFileById(parentId).getParentId();
+                 }
+                 if (ancestorIds.size() > 0) {
+                     StringBuffer whereBuffer = new StringBuffer();
+                     whereBuffer.append(ProviderTableMeta._ID).append(" IN (");
+                     for (int i = 0; i < ancestorIds.size() - 1; i++) {
+                         whereBuffer.append("?,");
+                     }
+                     whereBuffer.append("?");
+                     whereBuffer.append(")");
+                     if (getContentResolver() != null) {
+                         updated = getContentResolver().update(
+                                 ProviderTableMeta.CONTENT_URI_FILE,
+                                 cv,
+                                 whereBuffer.toString(),
+                                 ancestorIds.toArray(new String[]{})
+                         );
+                     } else {
+                         try {
+                             updated = getContentProviderClient().update(
+                                     ProviderTableMeta.CONTENT_URI_FILE,
+                                     cv,
+                                     whereBuffer.toString(),
+                                     ancestorIds.toArray(new String[]{})
+                             );
+                         } catch (RemoteException e) {
+                             Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage());
+                         }
+                     }
+                 } // else file is ROOT folder, no parent to set in conflict
+             } else {
+                 /// update conflict in ancestor folders
+                 // (not directly unset; maybe there are more conflicts below them)
+                 String parentPath = file.getRemotePath();
+                 if (parentPath.endsWith(OCFile.PATH_SEPARATOR)) {
+                     parentPath = parentPath.substring(0, parentPath.length() - 1);
+                 }
+                 parentPath = parentPath.substring(0, parentPath.lastIndexOf(OCFile.PATH_SEPARATOR) + 1);
+                 Log_OC.d(TAG, "checking parents to remove conflict; STARTING with " + parentPath);
+                 while (parentPath.length() > 0) {
+                     String where =
+                             ProviderTableMeta.FILE_ETAG_IN_CONFLICT + " IS NOT NULL AND " +
+                                     ProviderTableMeta.FILE_CONTENT_TYPE + " != 'DIR' AND " +
+                                     ProviderTableMeta.FILE_ACCOUNT_OWNER + " = ? AND " +
+                                     ProviderTableMeta.FILE_PATH + " LIKE ?";
+                     Cursor descendentsInConflict = getContentResolver().query(
+                             ProviderTableMeta.CONTENT_URI_FILE,
+                             new String[]{ProviderTableMeta._ID},
+                             where,
+                             new String[]{mAccount.name, parentPath + "%"},
+                             null
+                     );
+                     if (descendentsInConflict == null || descendentsInConflict.getCount() == 0) {
+                         Log_OC.d(TAG, "NO MORE conflicts in " + parentPath);
+                         if (getContentResolver() != null) {
+                             updated = getContentResolver().update(
+                                     ProviderTableMeta.CONTENT_URI_FILE,
+                                     cv,
+                                     ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
+                                             ProviderTableMeta.FILE_PATH + "=?",
+                                     new String[]{mAccount.name, parentPath}
+                             );
+                         } else {
+                             try {
+                                 updated = getContentProviderClient().update(
+                                         ProviderTableMeta.CONTENT_URI_FILE,
+                                         cv,
+                                         ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
+                                                 ProviderTableMeta.FILE_PATH + "=?"
+                                         , new String[]{mAccount.name, parentPath}
+                                 );
+                             } catch (RemoteException e) {
+                                 Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage());
+                             }
+                         }
+                     } else {
+                         Log_OC.d(TAG, "STILL " + descendentsInConflict.getCount() + " in " + parentPath);
+                     }
+                     if (descendentsInConflict != null) {
+                         descendentsInConflict.close();
+                     }
+                     parentPath = parentPath.substring(0, parentPath.length() - 1);  // trim last /
+                     parentPath = parentPath.substring(0, parentPath.lastIndexOf(OCFile.PATH_SEPARATOR) + 1);
+                     Log_OC.d(TAG, "checking parents to remove conflict; NEXT " + parentPath);
+                 }
+             }
+         }
+     }
+     public OCCapability saveCapabilities(OCCapability capability){
+         // Prepare capabilities data
+         ContentValues cv = new ContentValues();
+         cv.put(ProviderTableMeta.CAPABILITIES_ACCOUNT_NAME, mAccount.name);
+         cv.put(ProviderTableMeta.CAPABILITIES_VERSION_MAYOR, capability.getVersionMayor());
+         cv.put(ProviderTableMeta.CAPABILITIES_VERSION_MINOR, capability.getVersionMinor());
+         cv.put(ProviderTableMeta.CAPABILITIES_VERSION_MICRO, capability.getVersionMicro());
+         cv.put(ProviderTableMeta.CAPABILITIES_VERSION_STRING, capability.getVersionString());
+         cv.put(ProviderTableMeta.CAPABILITIES_VERSION_EDITION, capability.getVersionEdition());
+         cv.put(ProviderTableMeta.CAPABILITIES_CORE_POLLINTERVAL, capability.getCorePollinterval());
+         cv.put(ProviderTableMeta.CAPABILITIES_SHARING_API_ENABLED, capability.getFilesSharingApiEnabled().getValue());
+         cv.put(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_ENABLED,
+                 capability.getFilesSharingPublicEnabled().getValue());
+         cv.put(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_PASSWORD_ENFORCED,
+                 capability.getFilesSharingPublicPasswordEnforced().getValue());
+         cv.put(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENABLED,
+                 capability.getFilesSharingPublicExpireDateEnabled().getValue());
+         cv.put(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_DAYS,
+                 capability.getFilesSharingPublicExpireDateDays());
+         cv.put(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENFORCED,
+                 capability.getFilesSharingPublicExpireDateEnforced().getValue());
+         cv.put(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_SEND_MAIL,
+                 capability.getFilesSharingPublicSendMail().getValue());
+         cv.put(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_UPLOAD,
+                 capability.getFilesSharingPublicUpload().getValue());
+         cv.put(ProviderTableMeta.CAPABILITIES_SHARING_USER_SEND_MAIL,
+                 capability.getFilesSharingUserSendMail().getValue());
+         cv.put(ProviderTableMeta.CAPABILITIES_SHARING_RESHARING, capability.getFilesSharingResharing().getValue());
+         cv.put(ProviderTableMeta.CAPABILITIES_SHARING_FEDERATION_OUTGOING,
+                 capability.getFilesSharingFederationOutgoing().getValue());
+         cv.put(ProviderTableMeta.CAPABILITIES_SHARING_FEDERATION_INCOMING,
+                 capability.getFilesSharingFederationIncoming().getValue());
+         cv.put(ProviderTableMeta.CAPABILITIES_FILES_BIGFILECHUNKING, capability.getFilesBigFileChuncking().getValue());
+         cv.put(ProviderTableMeta.CAPABILITIES_FILES_UNDELETE, capability.getFilesUndelete().getValue());
+         cv.put(ProviderTableMeta.CAPABILITIES_FILES_VERSIONING, capability.getFilesVersioning().getValue());
+         if (capabilityExists(mAccount.name)) {
+             if (getContentResolver() != null) {
+                 getContentResolver().update(ProviderTableMeta.CONTENT_URI_CAPABILITIES, cv,
+                         ProviderTableMeta.CAPABILITIES_ACCOUNT_NAME + "=?",
+                         new String[]{mAccount.name});
+             } else {
+                 try {
+                     getContentProviderClient().update(ProviderTableMeta.CONTENT_URI_CAPABILITIES,
+                             cv, ProviderTableMeta.CAPABILITIES_ACCOUNT_NAME + "=?",
+                             new String[]{mAccount.name});
+                 } catch (RemoteException e) {
+                     Log_OC.e(TAG,
+                             "Fail to insert insert file to database "
+                                     + e.getMessage());
+                 }
+             }
+         } else {
+             Uri result_uri = null;
+             if (getContentResolver() != null) {
+                 result_uri = getContentResolver().insert(
+                         ProviderTableMeta.CONTENT_URI_CAPABILITIES, cv);
+             } else {
+                 try {
+                     result_uri = getContentProviderClient().insert(
+                             ProviderTableMeta.CONTENT_URI_CAPABILITIES, cv);
+                 } catch (RemoteException e) {
+                     Log_OC.e(TAG,
+                             "Fail to insert insert capability to database "
+                                     + e.getMessage());
+                 }
+             }
+             if (result_uri != null) {
+                 long new_id = Long.parseLong(result_uri.getPathSegments()
+                         .get(1));
+                 capability.setId(new_id);
+                 capability.setAccountName(mAccount.name);
+             }
+         }
+         return capability;
+     }
+     private boolean capabilityExists(String accountName) {
+         Cursor c = getCapabilityCursorForAccount(accountName);
+         boolean exists = false;
+         if (c != null) {
+             exists = c.moveToFirst();
+             c.close();
+         }
+         return exists;
+     }
+     private Cursor getCapabilityCursorForAccount(String accountName){
+         Cursor c = null;
+         if (getContentResolver() != null) {
+             c = getContentResolver()
+                     .query(ProviderTableMeta.CONTENT_URI_CAPABILITIES,
+                             null,
+                             ProviderTableMeta.CAPABILITIES_ACCOUNT_NAME + "=? ",
+                             new String[]{accountName}, null);
+         } else {
+             try {
+                 c = getContentProviderClient().query(
+                         ProviderTableMeta.CONTENT_URI_CAPABILITIES,
+                         null,
+                         ProviderTableMeta.CAPABILITIES_ACCOUNT_NAME + "=? ",
+                         new String[]{accountName}, null);
+             } catch (RemoteException e) {
+                 Log_OC.e(TAG,
+                         "Couldn't determine capability existance, assuming non existance: "
+                                 + e.getMessage());
+             }
+         }
+         return c;
+     }
+     public OCCapability getCapability(String accountName){
+         OCCapability capability = null;
+         Cursor c = getCapabilityCursorForAccount(accountName);
+         if (c.moveToFirst()) {
+             capability = createCapabilityInstance(c);
+         }
+         c.close();
+         return capability;
+     }
+     private OCCapability createCapabilityInstance(Cursor c) {
+         OCCapability capability = null;
+         if (c != null) {
+             capability = new OCCapability();
+             capability.setId(c.getLong(c.getColumnIndex(ProviderTableMeta._ID)));
+             capability.setAccountName(c.getString(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_ACCOUNT_NAME)));
+             capability.setVersionMayor(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_VERSION_MAYOR)));
+             capability.setVersionMinor(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_VERSION_MINOR)));
+             capability.setVersionMicro(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_VERSION_MICRO)));
+             capability.setVersionString(c.getString(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_VERSION_STRING)));
+             capability.setVersionEdition(c.getString(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_VERSION_EDITION)));
+             capability.setCorePollinterval(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_CORE_POLLINTERVAL)));
+             capability.setFilesSharingApiEnabled(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_SHARING_API_ENABLED))));
+             capability.setFilesSharingPublicEnabled(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_ENABLED))));
+             capability.setFilesSharingPublicPasswordEnforced(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_PASSWORD_ENFORCED))));
+             capability.setFilesSharingPublicExpireDateEnabled(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENABLED))));
+             capability.setFilesSharingPublicExpireDateDays(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_DAYS)));
+             capability.setFilesSharingPublicExpireDateEnforced(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENFORCED))));
+             capability.setFilesSharingPublicSendMail(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_SEND_MAIL))));
+             capability.setFilesSharingPublicUpload(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_UPLOAD))));
+             capability.setFilesSharingUserSendMail(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_SHARING_USER_SEND_MAIL))));
+             capability.setFilesSharingResharing(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_SHARING_RESHARING))));
+             capability.setFilesSharingFederationOutgoing(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_SHARING_FEDERATION_OUTGOING))));
+             capability.setFilesSharingFederationIncoming(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_SHARING_FEDERATION_INCOMING))));
+             capability.setFilesBigFileChuncking(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_FILES_BIGFILECHUNKING))));
+             capability.setFilesUndelete(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_FILES_UNDELETE))));
+             capability.setFilesVersioning(CapabilityBooleanType.fromValue(c.getInt(c
+                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_FILES_VERSIONING))));
+         }
+         return capability;
+     }
  }
  package com.owncloud.android.files.services;
  
  import java.io.File;
- import java.io.IOException;
  import java.util.AbstractList;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.Vector;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.concurrent.ConcurrentMap;
  
  import android.accounts.Account;
  import android.accounts.AccountManager;
- import android.accounts.AccountsException;
  import android.accounts.OnAccountsUpdateListener;
  import android.app.NotificationManager;
  import android.app.PendingIntent;
@@@ -46,6 -42,7 +42,7 @@@ import android.os.Looper
  import android.os.Message;
  import android.os.Process;
  import android.support.v4.app.NotificationCompat;
+ import android.util.Pair;
  import android.webkit.MimeTypeMap;
  
  import com.owncloud.android.R;
@@@ -86,6 -83,7 +83,7 @@@ public class FileUploader extends Servi
      public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
      public static final String EXTRA_OLD_REMOTE_PATH = "OLD_REMOTE_PATH";
      public static final String EXTRA_OLD_FILE_PATH = "OLD_FILE_PATH";
+     public static final String EXTRA_LINKED_TO_PATH = "LINKED_TO";
      public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
  
      public static final String KEY_FILE = "FILE";
      public static final int LOCAL_BEHAVIOUR_COPY = 0;
      public static final int LOCAL_BEHAVIOUR_MOVE = 1;
      public static final int LOCAL_BEHAVIOUR_FORGET = 2;
 +    public static final int LOCAL_BEHAVIOUR_REMOVE = 3;
  
      public static final int UPLOAD_SINGLE_FILE = 0;
      public static final int UPLOAD_MULTIPLE_FILES = 1;
      private ServiceHandler mServiceHandler;
      private IBinder mBinder;
      private OwnCloudClient mUploadClient = null;
-     private Account mLastAccount = null;
+     private Account mCurrentAccount = null;
      private FileDataStorageManager mStorageManager;
  
-     private ConcurrentMap<String, UploadFileOperation> mPendingUploads =
-             new ConcurrentHashMap<String, UploadFileOperation>();
+     private IndexedForest<UploadFileOperation> mPendingUploads = new IndexedForest<UploadFileOperation>();
      private UploadFileOperation mCurrentUpload = null;
  
      private NotificationManager mNotificationManager;
      }
  
      /**
-      * Builds a key for mPendingUploads from the account and file to upload
-      *
-      * @param account   Account where the file to upload is stored
-      * @param file      File to upload
-      */
-     private String buildRemoteName(Account account, OCFile file) {
-         return account.name + file.getRemotePath();
-     }
-     private String buildRemoteName(Account account, String remotePath) {
-         return account.name + remotePath;
-     }
-     /**
       * Checks if an ownCloud server version should support chunked uploads.
       *
       * @param version OwnCloud version instance corresponding to an ownCloud
       *            server.
       * @return 'True' if the ownCloud server with version supports chunked
       *         uploads.
+      *
+      * TODO - move to OCClient
       */
      private static boolean chunkedUploadIsSupported(OwnCloudVersion version) {
          return (version != null && version.compareTo(OwnCloudVersion.owncloud_v4_5) >= 0);
              files = new OCFile[localPaths.length];
              for (int i = 0; i < localPaths.length; i++) {
                  files[i] = obtainNewOCFileToUpload(remotePaths[i], localPaths[i],
-                         ((mimeTypes != null) ? mimeTypes[i] : null), storageManager);
+                         ((mimeTypes != null) ? mimeTypes[i] : null));
                  if (files[i] == null) {
                      // TODO @andomaex add failure Notification
                      return Service.START_NOT_STICKY;
          UploadFileOperation newUpload = null;
          try {
              for (int i = 0; i < files.length; i++) {
-                 uploadKey = buildRemoteName(account, files[i].getRemotePath());
-                 newUpload = new UploadFileOperation(account, files[i], chunked, isInstant,
+                 newUpload = new UploadFileOperation(
+                         account,
+                         files[i],
+                         chunked,
+                         isInstant,
                          forceOverwrite, localAction,
-                         getApplicationContext());
+                         getApplicationContext()
+                 );
                  if (isInstant) {
                      newUpload.setRemoteFolderToBeCreated();
                  }
-                 // Grants that the file only upload once time
-                 mPendingUploads.putIfAbsent(uploadKey, newUpload);
                  newUpload.addDatatransferProgressListener(this);
-                 newUpload.addDatatransferProgressListener((FileUploaderBinder)mBinder);
+                 newUpload.addDatatransferProgressListener((FileUploaderBinder) mBinder);
+                 Pair<String, String> putResult = mPendingUploads.putIfAbsent(
+                         account, files[i].getRemotePath(), newUpload
+                 );
+                 uploadKey = putResult.first;
                  requestedUploads.add(uploadKey);
              }
  
              msg.obj = requestedUploads;
              mServiceHandler.sendMessage(msg);
          }
-         Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size());
          return Service.START_NOT_STICKY;
      }
  
          /**
           * Cancels a pending or current upload of a remote file.
           *
-          * @param account Owncloud account where the remote file will be stored.
-          * @param file A file in the queue of pending uploads
+          * @param account   ownCloud account where the remote file will be stored.
+          * @param file      A file in the queue of pending uploads
           */
          public void cancel(Account account, OCFile file) {
-             UploadFileOperation upload;
-             synchronized (mPendingUploads) {
-                 upload = mPendingUploads.remove(buildRemoteName(account, file));
-             }
+             Pair<UploadFileOperation, String> removeResult = mPendingUploads.remove(account, file.getRemotePath());
+             UploadFileOperation upload = removeResult.first;
              if (upload != null) {
                  upload.cancel();
+             } else {
+                 if (mCurrentUpload != null && mCurrentAccount != null &&
+                         mCurrentUpload.getRemotePath().startsWith(file.getRemotePath()) &&
+                         account.name.equals(mCurrentAccount.name)) {
+                     mCurrentUpload.cancel();
+                 }
              }
          }
  
          /**
-          * Cancels a pending or current upload for an account
+          * Cancels all the uploads for an account
           *
-          * @param account Owncloud accountName where the remote file will be stored.
+          * @param account   ownCloud account.
           */
          public void cancel(Account account) {
              Log_OC.d(TAG, "Account= " + account.name);
                  }
              }
              // Cancel pending uploads
-             cancelUploadForAccount(account.name);
+             cancelUploadsForAccount(account);
          }
  
          public void clearListeners() {
              mBoundListeners.clear();
          }
  
          /**
           * Returns True when the file described by 'file' is being uploaded to
           * the ownCloud account 'account' or waiting for it
           * @param file      A file that could be in the queue of pending uploads
           */
          public boolean isUploading(Account account, OCFile file) {
-             if (account == null || file == null)
-                 return false;
-             String targetKey = buildRemoteName(account, file);
-             synchronized (mPendingUploads) {
-                 if (file.isFolder()) {
-                     // this can be slow if there are many uploads :(
-                     Iterator<String> it = mPendingUploads.keySet().iterator();
-                     boolean found = false;
-                     while (it.hasNext() && !found) {
-                         found = it.next().startsWith(targetKey);
-                     }
-                     return found;
-                 } else {
-                     return (mPendingUploads.containsKey(targetKey));
-                 }
-             }
+             if (account == null || file == null) return false;
+             return (mPendingUploads.contains(account, file.getRemotePath()));
          }
  
  
          }
  
          /**
-          * Review uploads and cancel it if its account doesn't exist
+          * Builds a key for the map of listeners.
+          *
+          * TODO remove and replace key with file.getFileId() after changing current policy (upload file, then
+          * add to local database) to better policy (add to local database, then upload)
+          *
+          * @param account       ownCloud account where the file to upload belongs.
+          * @param file          File to upload
+          * @return              Key
           */
-         public void checkAccountOfCurrentUpload() {
-             if (mCurrentUpload != null &&
-                     !AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) {
-                 mCurrentUpload.cancel();
-             }
-             // The rest of uploads are cancelled when they try to start
+         private String buildRemoteName(Account account, OCFile file) {
+             return account.name + file.getRemotePath();
          }
      }
  
      /**
      /**
       * Core upload method: sends the file(s) to upload
       *
-      * @param uploadKey Key to access the upload to perform, contained in
-      *            mPendingUploads
+      * @param uploadKey Key to access the upload to perform, contained in mPendingUploads
       */
      public void uploadFile(String uploadKey) {
  
-         synchronized (mPendingUploads) {
-             mCurrentUpload = mPendingUploads.get(uploadKey);
-         }
+         mCurrentUpload = mPendingUploads.get(uploadKey);
  
          if (mCurrentUpload != null) {
              // Detect if the account exists
              if (AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) {
                  Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().name + " exists");
                  RemoteOperationResult uploadResult = null, grantResult;
  
                  try {
-                     /// prepare client object to send requests to the ownCloud server
-                     if (mUploadClient == null ||
-                             !mLastAccount.equals(mCurrentUpload.getAccount())) {
-                         mLastAccount = mCurrentUpload.getAccount();
-                         mStorageManager =
-                                 new FileDataStorageManager(mLastAccount, getContentResolver());
-                         OwnCloudAccount ocAccount = new OwnCloudAccount(mLastAccount, this);
-                         mUploadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
-                                 getClientFor(ocAccount, this);
-                     }
+                     /// prepare client object to send the request to the ownCloud server
+                     if (mCurrentAccount == null || !mCurrentAccount.equals(mCurrentUpload.getAccount())) {
+                         mCurrentAccount = mCurrentUpload.getAccount();
+                         mStorageManager = new FileDataStorageManager(
+                                 mCurrentAccount,
+                                 getContentResolver()
+                         );
+                     }   // else, reuse storage manager from previous operation
+                     // always get client from client manager, to get fresh credentials in case of update
+                     OwnCloudAccount ocAccount = new OwnCloudAccount(mCurrentAccount, this);
+                     mUploadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+                             getClientFor(ocAccount, this);
  
                      /// check the existence of the parent folder for the file to upload
                      String remoteParentPath = new File(mCurrentUpload.getRemotePath()).getParent();
                          uploadResult = mCurrentUpload.execute(mUploadClient);
                          if (uploadResult.isSuccess()) {
                              saveUploadedFile();
+                         } else if (uploadResult.getCode() == ResultCode.SYNC_CONFLICT) {
+                             mStorageManager.saveConflict(mCurrentUpload.getFile(),
+                                     mCurrentUpload.getFile().getEtagInConflict());
                          }
                      } else {
                          uploadResult = grantResult;
                      }
  
-                 } catch (AccountsException e) {
-                     Log_OC.e(TAG, "Error while trying to get autorization for " +
-                             mLastAccount.name, e);
-                     uploadResult = new RemoteOperationResult(e);
-                 } catch (IOException e) {
-                     Log_OC.e(TAG, "Error while trying to get autorization for " +
-                             mLastAccount.name, e);
+                 } catch (Exception e) {
+                     Log_OC.e(TAG, "Error uploading", e);
                      uploadResult = new RemoteOperationResult(e);
  
                  } finally {
-                     synchronized (mPendingUploads) {
-                         mPendingUploads.remove(uploadKey);
-                         Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map.");
-                     }
-                     if (uploadResult != null && uploadResult.isException()) {
-                         // enforce the creation of a new client object for next uploads;
-                         // this grant that a new socket will be created in the future if
-                         // the current exception is due to an abrupt lose of network connection
-                         mUploadClient = null;
+                     Pair<UploadFileOperation, String> removeResult;
+                     if (mCurrentUpload.wasRenamed()) {
+                         removeResult = mPendingUploads.removePayload(
+                                 mCurrentAccount,
+                                 mCurrentUpload.getOldFile().getRemotePath()
+                         );
+                     } else {
+                         removeResult = mPendingUploads.removePayload(
+                                 mCurrentAccount,
+                                 mCurrentUpload.getRemotePath()
+                         );
                      }
-                 }
  
-                 /// notify result
-                 notifyUploadResult(uploadResult, mCurrentUpload);
-                 sendFinalBroadcast(mCurrentUpload, uploadResult);
+                     /// notify result
+                     notifyUploadResult(mCurrentUpload, uploadResult);
+                     sendBroadcastUploadFinished(mCurrentUpload, uploadResult, removeResult.second);
+                 }
  
              } else {
                  // Cancel the transfer
                  Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().toString() +
                          " doesn't exist");
-                 cancelUploadForAccount(mCurrentUpload.getAccount().name);
+                 cancelUploadsForAccount(mCurrentUpload.getAccount());
  
              }
          }
       * synchronized with the server, specially the modification time and Etag
       * (where available)
       *
-      * TODO refactor this ugly thing
+      * TODO move into UploadFileOperation
       */
      private void saveUploadedFile() {
          OCFile file = mCurrentUpload.getFile();
          if (result.isSuccess()) {
              updateOCFile(file, (RemoteFile) result.getData().get(0));
              file.setLastSyncDateForProperties(syncDate);
+         } else {
+             Log_OC.e(TAG, "Error reading properties of file after successful upload; this is gonna hurt...");
          }
  
          // / maybe this would be better as part of UploadFileOperation... or
              if (oldFile.fileExists()) {
                  oldFile.setStoragePath(null);
                  mStorageManager.saveFile(oldFile);
+                 mStorageManager.saveConflict(oldFile, null);
  
              } // else: it was just an automatic renaming due to a name
              // coincidence; nothing else is needed, the storagePath is right
          }
          file.setNeedsUpdateThumbnail(true);
          mStorageManager.saveFile(file);
+         mStorageManager.saveConflict(file, null);
+         
+         mStorageManager.triggerMediaScan(file.getStoragePath());
      }
  
      private void updateOCFile(OCFile file, RemoteFile remoteFile) {
          file.setMimetype(remoteFile.getMimeType());
          file.setModificationTimestamp(remoteFile.getModifiedTimestamp());
          file.setModificationTimestampAtLastSyncForData(remoteFile.getModifiedTimestamp());
-         // file.setEtag(remoteFile.getEtag());    // TODO Etag, where available
+         file.setEtag(remoteFile.getEtag());
          file.setRemoteId(remoteFile.getRemoteId());
      }
  
-     private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType,
-                                            FileDataStorageManager storageManager) {
+     private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType) {
  
          // MIME type
          if (mimeType == null || mimeType.length() <= 0) {
      /**
       * Updates the status notification with the result of an upload operation.
       *
-      * @param uploadResult Result of the upload operation.
-      * @param upload Finished upload operation
+      * @param uploadResult  Result of the upload operation.
+      * @param upload        Finished upload operation
       */
-     private void notifyUploadResult(
-             RemoteOperationResult uploadResult, UploadFileOperation upload) {
+     private void notifyUploadResult(UploadFileOperation upload,
+                                     RemoteOperationResult uploadResult) {
          Log_OC.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.getCode());
          // / cancelled operation or success -> silent removal of progress notification
          mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
       * Sends a broadcast in order to the interested activities can update their
       * view
       *
-      * @param upload Finished upload operation
-      * @param uploadResult Result of the upload operation
+      * @param upload                    Finished upload operation
+      * @param uploadResult              Result of the upload operation
+      * @param unlinkedFromRemotePath    Path in the uploads tree where the upload was unlinked from
       */
-     private void sendFinalBroadcast(UploadFileOperation upload, RemoteOperationResult uploadResult) {
+     private void sendBroadcastUploadFinished(
+             UploadFileOperation upload,
+             RemoteOperationResult uploadResult,
+             String unlinkedFromRemotePath) {
          Intent end = new Intent(getUploadFinishMessage());
          end.putExtra(EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote
          // path, after
          end.putExtra(EXTRA_OLD_FILE_PATH, upload.getOriginalStoragePath());
          end.putExtra(ACCOUNT_NAME, upload.getAccount().name);
          end.putExtra(EXTRA_UPLOAD_RESULT, uploadResult.isSuccess());
+         if (unlinkedFromRemotePath != null) {
+             end.putExtra(EXTRA_LINKED_TO_PATH, unlinkedFromRemotePath);
+         }
          sendStickyBroadcast(end);
      }
  
       * @param localPath         Full path to a file in the local file system.
       * @param mimeType          MIME type of the file.
       * @return true if is needed to add the pdf file extension to the file
+      *
+      * TODO - move to OCFile or Utils class
       */
      private boolean isPdfFileFromContentProviderWithoutExtension(String localPath,
                                                                   String mimeType) {
  
      /**
       * Remove uploads of an account
-      * @param accountName       Name of an OC account
+      *
+      * @param account       Downloads account to remove
       */
-     private void cancelUploadForAccount(String accountName){
-         // this can be slow if there are many uploads :(
-         Iterator<String> it = mPendingUploads.keySet().iterator();
-         Log_OC.d(TAG, "Number of pending updloads= "  + mPendingUploads.size());
-         while (it.hasNext()) {
-             String key = it.next();
-             Log_OC.d(TAG, "mPendingUploads CANCELLED " + key);
-             if (key.startsWith(accountName)) {
-                 synchronized (mPendingUploads) {
-                     mPendingUploads.remove(key);
-                 }
-             }
-         }
+     private void cancelUploadsForAccount(Account account){
+         // Cancel pending uploads
+         mPendingUploads.remove(account);
      }
  }
@@@ -31,7 -31,7 +31,7 @@@ import java.util.Iterator
  import java.util.Set;
  import java.util.concurrent.atomic.AtomicBoolean;
  
- import org.apache.commons.httpclient.methods.PutMethod;
+ import org.apache.commons.httpclient.HttpStatus;
  import org.apache.commons.httpclient.methods.RequestEntity;
  
  import android.accounts.Account;
@@@ -39,7 -39,6 +39,7 @@@ import android.content.Context
  import android.net.Uri;
  
  import com.owncloud.android.MainApp;
 +import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
  import com.owncloud.android.files.services.FileUploader;
  import com.owncloud.android.lib.common.OwnCloudClient;
@@@ -76,7 -75,6 +76,6 @@@ public class UploadFileOperation extend
      private boolean mWasRenamed = false;
      private String mOriginalFileName = null;
      private String mOriginalStoragePath = null;
-     PutMethod mPutMethod = null;
      private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
      private AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
      private Context mContext;
                      (new File(mFile.getStoragePath())).length() >
                              ChunkedUploadRemoteFileOperation.CHUNK_SIZE ) {
                  mUploadOperation = new ChunkedUploadRemoteFileOperation(mFile.getStoragePath(),
-                         mFile.getRemotePath(), mFile.getMimetype());
+                         mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict());
              } else {
                  mUploadOperation = new UploadRemoteFileOperation(mFile.getStoragePath(),
-                         mFile.getRemotePath(), mFile.getMimetype());
+                         mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict());
              }
              Iterator <OnDatatransferProgressListener> listener = mDataTransferListeners.iterator();
              while (listener.hasNext()) {
                  mUploadOperation.addDatatransferProgressListener(listener.next());
              }
-             if (!mCancellationRequested.get()) {
-                 result = mUploadOperation.execute(client);
-                 /// move local temporal file or original file to its corresponding
-                 // location in the ownCloud local folder
-                 if (result.isSuccess()) {
-                     if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
-                         mFile.setStoragePath(null);
-                     } else if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_REMOVE){
-                         mFile.setStoragePath(null);
-                         originalFile.delete();
-                     } else {
-                         mFile.setStoragePath(expectedPath);
-                         File fileToMove = null;
-                         if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY
-                             // ; see where temporalFile was
-                             // set
-                             fileToMove = temporalFile;
-                         } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
-                             fileToMove = originalFile;
-                         }
-                         if (!expectedFile.equals(fileToMove)) {
-                             File expectedFolder = expectedFile.getParentFile();
-                             expectedFolder.mkdirs();
-                             if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) {
-                                 mFile.setStoragePath(null); // forget the local file
-                                 // by now, treat this as a success; the file was
-                                 // uploaded; the user won't like that the local file
-                                 // is not linked, but this should be a very rare
-                                 // fail;
-                                 // the best option could be show a warning message
-                                 // (but not a fail)
-                                 // result = new
-                                 // RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED);
-                                 // return result;
-                             }
+             if (mCancellationRequested.get()) {
+                 throw new OperationCancelledException();
+             }
 -            result = mUploadOperation.execute(client);
 -
+             /// move local temporal file or original file to its corresponding
+             // location in the ownCloud local folder
+             if (result.isSuccess()) {
+                 if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
+                     mFile.setStoragePath(null);
 -
++                } else if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_REMOVE){
++                    mFile.setStoragePath(null);
++                    originalFile.delete();
+                 } else {
+                     mFile.setStoragePath(expectedPath);
+                     File fileToMove = null;
+                     if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY
+                         // ; see where temporalFile was
+                         // set
+                         fileToMove = temporalFile;
+                     } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
+                         fileToMove = originalFile;
+                     }
+                     if (!expectedFile.equals(fileToMove)) {
+                         File expectedFolder = expectedFile.getParentFile();
+                         expectedFolder.mkdirs();
+                         if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) {
+                             mFile.setStoragePath(null); // forget the local file
+                             // by now, treat this as a success; the file was
+                             // uploaded; the user won't like that the local file
+                             // is not linked, but this should be a very rare
+                             // fail;
+                             // the best option could be show a warning message
+                             // (but not a fail)
+                             // result = new
+                             // RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED);
+                             // return result;
                          }
                      }
-                     FileDataStorageManager.triggerMediaScan(originalFile.getAbsolutePath());
-                     FileDataStorageManager.triggerMediaScan(expectedFile.getAbsolutePath());
                  }
 -
++                FileDataStorageManager.triggerMediaScan(originalFile.getAbsolutePath());
++                FileDataStorageManager.triggerMediaScan(expectedFile.getAbsolutePath());
+             } else if (result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED ) {
+                 result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
              }
  
          } catch (Exception e) {
-             // TODO something cleaner with cancellations
-             if (mCancellationRequested.get()) {
-                 result = new RemoteOperationResult(new OperationCancelledException());
-             } else {
-                 result = new RemoteOperationResult(e);
-             }
+             result = new RemoteOperationResult(e);
  
          } finally {
              if (temporalFile != null && !originalFile.equals(temporalFile)) {
          newFile.setModificationTimestamp(mFile.getModificationTimestamp());
          newFile.setModificationTimestampAtLastSyncForData(
                  mFile.getModificationTimestampAtLastSyncForData());
-         // newFile.setEtag(mFile.getEtag())
+         newFile.setEtag(mFile.getEtag());
          newFile.setFavorite(mFile.isFavorite());
          newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
          newFile.setLastSyncDateForData(mFile.getLastSyncDateForData());