1 /* ownCloud Android client application 
   2  *   Copyright (C) 2012  Bartek Przybylski 
   3  *   Copyright (C) 2012-2013 ownCloud Inc. 
   5  *   This program is free software: you can redistribute it and/or modify 
   6  *   it under the terms of the GNU General Public License version 2, 
   7  *   as published by the Free Software Foundation. 
   9  *   This program is distributed in the hope that it will be useful, 
  10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of 
  11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  12  *   GNU General Public License for more details. 
  14  *   You should have received a copy of the GNU General Public License 
  15  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  19 package com
.owncloud
.android
.datamodel
; 
  22 import java
.util
.ArrayList
; 
  23 import java
.util
.Collection
; 
  24 import java
.util
.Collections
; 
  25 import java
.util
.Iterator
; 
  26 import java
.util
.Vector
; 
  28 import com
.owncloud
.android
.MainApp
; 
  29 import com
.owncloud
.android
.db
.ProviderMeta
.ProviderTableMeta
; 
  30 import com
.owncloud
.android
.utils
.FileStorageUtils
; 
  31 import com
.owncloud
.android
.utils
.Log_OC
; 
  34 import android
.accounts
.Account
; 
  35 import android
.content
.ContentProviderClient
; 
  36 import android
.content
.ContentProviderOperation
; 
  37 import android
.content
.ContentProviderResult
; 
  38 import android
.content
.ContentResolver
; 
  39 import android
.content
.ContentUris
; 
  40 import android
.content
.ContentValues
; 
  41 import android
.content
.OperationApplicationException
; 
  42 import android
.database
.Cursor
; 
  43 import android
.net
.Uri
; 
  44 import android
.os
.RemoteException
; 
  46 public class FileDataStorageManager 
{ 
  48     public static final int ROOT_PARENT_ID 
= 0; 
  50     private ContentResolver mContentResolver
; 
  51     private ContentProviderClient mContentProviderClient
; 
  52     private Account mAccount
; 
  54     private static String TAG 
= FileDataStorageManager
.class.getSimpleName(); 
  57     public FileDataStorageManager(Account account
, ContentResolver cr
) { 
  58         mContentProviderClient 
= null
; 
  59         mContentResolver 
= cr
; 
  63     public FileDataStorageManager(Account account
, ContentProviderClient cp
) { 
  64         mContentProviderClient 
= cp
; 
  65         mContentResolver 
= null
; 
  70     public void setAccount(Account account
) { 
  74     public Account 
getAccount() { 
  78     public void setContentResolver(ContentResolver cr
) { 
  79         mContentResolver 
= cr
; 
  82     public ContentResolver 
getContentResolver() { 
  83         return mContentResolver
; 
  86     public void setContentProviderClient(ContentProviderClient cp
) { 
  87         mContentProviderClient 
= cp
; 
  90     public ContentProviderClient 
getContentProviderClient() { 
  91         return mContentProviderClient
; 
  95     public OCFile 
getFileByPath(String path
) { 
  96         Cursor c 
= getCursorForValue(ProviderTableMeta
.FILE_PATH
, path
); 
  98         if (c
.moveToFirst()) { 
  99             file 
= createFileInstance(c
); 
 102         if (file 
== null 
&& OCFile
.ROOT_PATH
.equals(path
)) { 
 103             return createRootDir(); // root should always exist 
 109     public OCFile 
getFileById(long id
) { 
 110         Cursor c 
= getCursorForValue(ProviderTableMeta
._ID
, String
.valueOf(id
)); 
 112         if (c
.moveToFirst()) { 
 113             file 
= createFileInstance(c
); 
 119     public OCFile 
getFileByLocalPath(String path
) { 
 120         Cursor c 
= getCursorForValue(ProviderTableMeta
.FILE_STORAGE_PATH
, path
); 
 122         if (c
.moveToFirst()) { 
 123             file 
= createFileInstance(c
); 
 129     public boolean fileExists(long id
) { 
 130         return fileExists(ProviderTableMeta
._ID
, String
.valueOf(id
)); 
 133     public boolean fileExists(String path
) { 
 134         return fileExists(ProviderTableMeta
.FILE_PATH
, path
); 
 138     public Vector
<OCFile
> getFolderContent(OCFile f
) { 
 139         if (f 
!= null 
&& f
.isFolder() && f
.getFileId() != -1) { 
 140             return getFolderContent(f
.getFileId()); 
 143             return new Vector
<OCFile
>(); 
 148     public Vector
<OCFile
> getFolderImages(OCFile folder
) { 
 149         Vector
<OCFile
> ret 
= new Vector
<OCFile
>();  
 150         if (folder 
!= null
) { 
 151             // TODO better implementation, filtering in the access to database (if possible) instead of here  
 152             Vector
<OCFile
> tmp 
= getFolderContent(folder
); 
 153             OCFile current 
= null
;  
 154             for (int i
=0; i
<tmp
.size(); i
++) { 
 155                 current 
= tmp
.get(i
); 
 156                 if (current
.isImage()) { 
 165     public boolean saveFile(OCFile file
) { 
 166         boolean overriden 
= false
; 
 167         ContentValues cv 
= new ContentValues(); 
 168         cv
.put(ProviderTableMeta
.FILE_MODIFIED
, file
.getModificationTimestamp()); 
 169         cv
.put(ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
, file
.getModificationTimestampAtLastSyncForData()); 
 170         cv
.put(ProviderTableMeta
.FILE_CREATION
, file
.getCreationTimestamp()); 
 171         cv
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, file
.getFileLength()); 
 172         cv
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, file
.getMimetype()); 
 173         cv
.put(ProviderTableMeta
.FILE_NAME
, file
.getFileName()); 
 174         //if (file.getParentId() != DataStorageManager.ROOT_PARENT_ID) 
 175             cv
.put(ProviderTableMeta
.FILE_PARENT
, file
.getParentId()); 
 176         cv
.put(ProviderTableMeta
.FILE_PATH
, file
.getRemotePath()); 
 177         if (!file
.isFolder()) 
 178             cv
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, file
.getStoragePath()); 
 179         cv
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, mAccount
.name
); 
 180         cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, file
.getLastSyncDateForProperties()); 
 181         cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
, file
.getLastSyncDateForData()); 
 182         cv
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, file
.keepInSync() ? 
1 : 0); 
 183         cv
.put(ProviderTableMeta
.FILE_ETAG
, file
.getEtag()); 
 184         cv
.put(ProviderTableMeta
.FILE_SHARE_BY_LINK
, file
.isShareByLink() ? 
1 : 0); 
 185         cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, file
.getPublicLink()); 
 187         boolean sameRemotePath 
= fileExists(file
.getRemotePath()); 
 188         if (sameRemotePath 
|| 
 189                 fileExists(file
.getFileId())        ) {           // for renamed files; no more delete and create 
 191             OCFile oldFile 
= null
; 
 192             if (sameRemotePath
) { 
 193                 oldFile 
= getFileByPath(file
.getRemotePath()); 
 194                 file
.setFileId(oldFile
.getFileId()); 
 196                 oldFile 
= getFileById(file
.getFileId()); 
 200             if (getContentResolver() != null
) { 
 201                 getContentResolver().update(ProviderTableMeta
.CONTENT_URI
, cv
, 
 202                         ProviderTableMeta
._ID 
+ "=?", 
 203                         new String
[] { String
.valueOf(file
.getFileId()) }); 
 206                     getContentProviderClient().update(ProviderTableMeta
.CONTENT_URI
, 
 207                             cv
, ProviderTableMeta
._ID 
+ "=?", 
 208                             new String
[] { String
.valueOf(file
.getFileId()) }); 
 209                 } catch (RemoteException e
) { 
 211                             "Fail to insert insert file to database " 
 216             Uri result_uri 
= null
; 
 217             if (getContentResolver() != null
) { 
 218                 result_uri 
= getContentResolver().insert( 
 219                         ProviderTableMeta
.CONTENT_URI_FILE
, cv
); 
 222                     result_uri 
= getContentProviderClient().insert( 
 223                             ProviderTableMeta
.CONTENT_URI_FILE
, cv
); 
 224                 } catch (RemoteException e
) { 
 226                             "Fail to insert insert file to database " 
 230             if (result_uri 
!= null
) { 
 231                 long new_id 
= Long
.parseLong(result_uri
.getPathSegments() 
 233                 file
.setFileId(new_id
); 
 237         if (file
.isFolder()) { 
 238             updateFolderSize(file
.getFileId()); 
 240             updateFolderSize(file
.getParentId()); 
 248      * Inserts or updates the list of files contained in a given folder. 
 250      * CALLER IS THE RESPONSIBLE FOR GRANTING RIGHT UPDATE OF INFORMATION, NOT THIS METHOD. 
 251      * HERE ONLY DATA CONSISTENCY SHOULD BE GRANTED 
 255      * @param removeNotUpdated 
 257     public void saveFolder(OCFile folder
, Collection
<OCFile
> updatedFiles
, Collection
<OCFile
> filesToRemove
) { 
 259         Log_OC
.d(TAG
,  "Saving folder " + folder
.getRemotePath() + " with " + updatedFiles
.size() + " children and " + filesToRemove
.size() + " files to remove"); 
 261         ArrayList
<ContentProviderOperation
> operations 
= new ArrayList
<ContentProviderOperation
>(updatedFiles
.size()); 
 263         // prepare operations to insert or update files to save in the given folder 
 264         for (OCFile file 
: updatedFiles
) { 
 265             ContentValues cv 
= new ContentValues(); 
 266             cv
.put(ProviderTableMeta
.FILE_MODIFIED
, file
.getModificationTimestamp()); 
 267             cv
.put(ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
, file
.getModificationTimestampAtLastSyncForData()); 
 268             cv
.put(ProviderTableMeta
.FILE_CREATION
, file
.getCreationTimestamp()); 
 269             cv
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, file
.getFileLength()); 
 270             cv
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, file
.getMimetype()); 
 271             cv
.put(ProviderTableMeta
.FILE_NAME
, file
.getFileName()); 
 272             //cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId()); 
 273             cv
.put(ProviderTableMeta
.FILE_PARENT
, folder
.getFileId()); 
 274             cv
.put(ProviderTableMeta
.FILE_PATH
, file
.getRemotePath()); 
 275             if (!file
.isFolder()) { 
 276                 cv
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, file
.getStoragePath()); 
 278             cv
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, mAccount
.name
); 
 279             cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, file
.getLastSyncDateForProperties()); 
 280             cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
, file
.getLastSyncDateForData()); 
 281             cv
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, file
.keepInSync() ? 
1 : 0); 
 282             cv
.put(ProviderTableMeta
.FILE_ETAG
, file
.getEtag()); 
 283             cv
.put(ProviderTableMeta
.FILE_SHARE_BY_LINK
, file
.isShareByLink() ? 
1 : 0); 
 284             cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, file
.getPublicLink()); 
 286             boolean existsByPath 
= fileExists(file
.getRemotePath()); 
 287             if (existsByPath 
|| fileExists(file
.getFileId())) { 
 288                 // updating an existing file 
 289                 operations
.add(ContentProviderOperation
.newUpdate(ProviderTableMeta
.CONTENT_URI
). 
 291                         withSelection(  ProviderTableMeta
._ID 
+ "=?",  
 292                                 new String
[] { String
.valueOf(file
.getFileId()) }) 
 297                 operations
.add(ContentProviderOperation
.newInsert(ProviderTableMeta
.CONTENT_URI
).withValues(cv
).build()); 
 301         // prepare operations to remove files in the given folder 
 302         String where 
= ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=?" + " AND " + ProviderTableMeta
.FILE_PATH 
+ "=?"; 
 303         String 
[] whereArgs 
= null
; 
 304         for (OCFile file 
: filesToRemove
) { 
 305             if (file
.getParentId() == folder
.getFileId()) { 
 306                 whereArgs 
= new String
[]{mAccount
.name
, file
.getRemotePath()}; 
 307                 //Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, "" + file.getFileId()); 
 308                 if (file
.isFolder()) { 
 309                     operations
.add(ContentProviderOperation
 
 310                                     .newDelete(ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_DIR
, file
.getFileId())).withSelection(where
, whereArgs
) 
 312                     // TODO remove local folder 
 314                     operations
.add(ContentProviderOperation
 
 315                                     .newDelete(ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_FILE
, file
.getFileId())).withSelection(where
, whereArgs
) 
 318                         new File(file
.getStoragePath()).delete(); 
 319                         // TODO move the deletion of local contents after success of deletions 
 325         // update metadata of folder 
 326         ContentValues cv 
= new ContentValues(); 
 327         cv
.put(ProviderTableMeta
.FILE_MODIFIED
, folder
.getModificationTimestamp()); 
 328         cv
.put(ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
, folder
.getModificationTimestampAtLastSyncForData()); 
 329         cv
.put(ProviderTableMeta
.FILE_CREATION
, folder
.getCreationTimestamp()); 
 330         cv
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, 0);   // FileContentProvider calculates the right size 
 331         cv
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, folder
.getMimetype()); 
 332         cv
.put(ProviderTableMeta
.FILE_NAME
, folder
.getFileName()); 
 333         cv
.put(ProviderTableMeta
.FILE_PARENT
, folder
.getParentId()); 
 334         cv
.put(ProviderTableMeta
.FILE_PATH
, folder
.getRemotePath()); 
 335         cv
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, mAccount
.name
); 
 336         cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, folder
.getLastSyncDateForProperties()); 
 337         cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
, folder
.getLastSyncDateForData()); 
 338         cv
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, folder
.keepInSync() ? 
1 : 0); 
 339         cv
.put(ProviderTableMeta
.FILE_ETAG
, folder
.getEtag()); 
 340         cv
.put(ProviderTableMeta
.FILE_SHARE_BY_LINK
, folder
.isShareByLink() ? 
1 : 0); 
 341         cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, folder
.getPublicLink()); 
 343         operations
.add(ContentProviderOperation
.newUpdate(ProviderTableMeta
.CONTENT_URI
). 
 345                 withSelection(  ProviderTableMeta
._ID 
+ "=?",  
 346                         new String
[] { String
.valueOf(folder
.getFileId()) }) 
 349         // apply operations in batch 
 350         ContentProviderResult
[] results 
= null
; 
 351         Log_OC
.d(TAG
, "Sending " + operations
.size() + " operations to FileContentProvider"); 
 353             if (getContentResolver() != null
) { 
 354                 results 
= getContentResolver().applyBatch(MainApp
.getAuthority(), operations
); 
 357                 results 
= getContentProviderClient().applyBatch(operations
); 
 360         } catch (OperationApplicationException e
) { 
 361             Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage()); 
 363         } catch (RemoteException e
) { 
 364             Log_OC
.e(TAG
, "Exception in batch of operations  " + e
.getMessage()); 
 367         // update new id in file objects for insertions 
 368         if (results 
!= null
) { 
 370             Iterator
<OCFile
> filesIt 
= updatedFiles
.iterator(); 
 372             for (int i
=0; i
<results
.length
; i
++) { 
 373                 if (filesIt
.hasNext()) { 
 374                     file 
= filesIt
.next(); 
 378                 if (results
[i
].uri 
!= null
) { 
 379                     newId 
= Long
.parseLong(results
[i
].uri
.getPathSegments().get(1)); 
 380                     //updatedFiles.get(i).setFileId(newId); 
 382                         file
.setFileId(newId
); 
 388         updateFolderSize(folder
.getFileId()); 
 397     private void updateFolderSize(long id
) { 
 398         if (id 
> FileDataStorageManager
.ROOT_PARENT_ID
) { 
 399             Log_OC
.d(TAG
, "Updating size of " + id
); 
 400             if (getContentResolver() != null
) { 
 401                 getContentResolver().update(ProviderTableMeta
.CONTENT_URI_DIR
,  
 402                         new ContentValues(),    // won't be used, but cannot be null; crashes in KLP 
 403                         ProviderTableMeta
._ID 
+ "=?", 
 404                         new String
[] { String
.valueOf(id
) }); 
 407                     getContentProviderClient().update(ProviderTableMeta
.CONTENT_URI_DIR
,  
 408                             new ContentValues(),    // won't be used, but cannot be null; crashes in KLP 
 409                             ProviderTableMeta
._ID 
+ "=?", 
 410                             new String
[] { String
.valueOf(id
) }); 
 412                 } catch (RemoteException e
) { 
 413                     Log_OC
.e(TAG
, "Exception in update of folder size through compatibility patch " + e
.getMessage()); 
 417             Log_OC
.e(TAG
,  "not updating size for folder " + id
); 
 422     public void removeFile(OCFile file
, boolean removeDBData
, boolean removeLocalCopy
) { 
 424             if (file
.isFolder()) { 
 425                 removeFolder(file
, removeDBData
, removeLocalCopy
); 
 429                     //Uri file_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, ""+file.getFileId()); 
 430                     Uri file_uri 
= ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_FILE
, file
.getFileId()); 
 431                     String where 
= ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=?" + " AND " + ProviderTableMeta
.FILE_PATH 
+ "=?"; 
 432                     String 
[] whereArgs 
= new String
[]{mAccount
.name
, file
.getRemotePath()}; 
 433                     if (getContentProviderClient() != null
) { 
 435                             getContentProviderClient().delete(file_uri
, where
, whereArgs
); 
 436                         } catch (RemoteException e
) { 
 440                         getContentResolver().delete(file_uri
, where
, whereArgs
); 
 442                     updateFolderSize(file
.getParentId()); 
 444                 if (removeLocalCopy 
&& file
.isDown() && file
.getStoragePath() != null
) { 
 445                     boolean success 
= new File(file
.getStoragePath()).delete(); 
 446                     if (!removeDBData 
&& success
) { 
 447                         // maybe unnecessary, but should be checked TODO remove if unnecessary 
 448                         file
.setStoragePath(null
); 
 457     public void removeFolder(OCFile folder
, boolean removeDBData
, boolean removeLocalContent
) { 
 458         if (folder 
!= null 
&& folder
.isFolder()) { 
 459             if (removeDBData 
&&  folder
.getFileId() != -1) { 
 460                 removeFolderInDb(folder
); 
 462             if (removeLocalContent
) { 
 463                 File localFolder 
= new File(FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, folder
)); 
 464                 removeLocalFolder(localFolder
); 
 469     private void removeFolderInDb(OCFile folder
) { 
 470         Uri folder_uri 
= Uri
.withAppendedPath(ProviderTableMeta
.CONTENT_URI_DIR
, ""+ folder
.getFileId());   // URI for recursive deletion 
 471         String where 
= ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=?" + " AND " + ProviderTableMeta
.FILE_PATH 
+ "=?"; 
 472         String 
[] whereArgs 
= new String
[]{mAccount
.name
, folder
.getRemotePath()}; 
 473         if (getContentProviderClient() != null
) { 
 475                 getContentProviderClient().delete(folder_uri
, where
, whereArgs
); 
 476             } catch (RemoteException e
) { 
 480             getContentResolver().delete(folder_uri
, where
, whereArgs
);  
 482         updateFolderSize(folder
.getParentId()); 
 485     private void removeLocalFolder(File folder
) { 
 486         if (folder
.exists()) { 
 487             File
[] files 
= folder
.listFiles(); 
 489                 for (File file 
: files
) { 
 490                     if (file
.isDirectory()) { 
 491                         removeLocalFolder(file
); 
 502      * Updates database for a folder that was moved to a different location. 
 504      * TODO explore better (faster) implementations 
 505      * TODO throw exceptions up ! 
 507     public void moveFolder(OCFile folder
, String newPath
) { 
 508         // TODO check newPath 
 510         if (folder 
!= null 
&& folder
.isFolder() && folder
.fileExists() && !OCFile
.ROOT_PATH
.equals(folder
.getFileName())) { 
 511             /// 1. get all the descendants of 'dir' in a single QUERY (including 'dir') 
 513             if (getContentProviderClient() != null
) { 
 515                     c 
= getContentProviderClient().query(ProviderTableMeta
.CONTENT_URI
,  
 517                             ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=? AND " + ProviderTableMeta
.FILE_PATH 
+ " LIKE ? ", 
 518                             new String
[] { mAccount
.name
, folder
.getRemotePath() + "%"  }, ProviderTableMeta
.FILE_PATH 
+ " ASC "); 
 519                 } catch (RemoteException e
) { 
 520                     Log_OC
.e(TAG
, e
.getMessage()); 
 523                 c 
= getContentResolver().query(ProviderTableMeta
.CONTENT_URI
,  
 525                         ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=? AND " + ProviderTableMeta
.FILE_PATH 
+ " LIKE ? ", 
 526                         new String
[] { mAccount
.name
, folder
.getRemotePath() + "%"  }, ProviderTableMeta
.FILE_PATH 
+ " ASC "); 
 529             /// 2. prepare a batch of update operations to change all the descendants 
 530             ArrayList
<ContentProviderOperation
> operations 
= new ArrayList
<ContentProviderOperation
>(c
.getCount()); 
 531             int lengthOfOldPath 
= folder
.getRemotePath().length(); 
 532             String defaultSavePath 
= FileStorageUtils
.getSavePath(mAccount
.name
); 
 533             int lengthOfOldStoragePath 
= defaultSavePath
.length() + lengthOfOldPath
; 
 534             if (c
.moveToFirst()) { 
 536                     ContentValues cv 
= new ContentValues(); // don't take the constructor out of the loop and clear the object 
 537                     OCFile child 
= createFileInstance(c
); 
 538                     cv
.put(ProviderTableMeta
.FILE_PATH
, newPath 
+ child
.getRemotePath().substring(lengthOfOldPath
)); 
 539                     if (child
.getStoragePath() != null 
&& child
.getStoragePath().startsWith(defaultSavePath
)) { 
 540                         cv
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, defaultSavePath 
+ newPath 
+ child
.getStoragePath().substring(lengthOfOldStoragePath
)); 
 542                     operations
.add(ContentProviderOperation
.newUpdate(ProviderTableMeta
.CONTENT_URI
). 
 544                             withSelection(  ProviderTableMeta
._ID 
+ "=?",  
 545                                     new String
[] { String
.valueOf(child
.getFileId()) }) 
 547                 } while (c
.moveToNext()); 
 551             /// 3. apply updates in batch 
 553                 if (getContentResolver() != null
) { 
 554                     getContentResolver().applyBatch(MainApp
.getAuthority(), operations
); 
 557                     getContentProviderClient().applyBatch(operations
); 
 560             } catch (OperationApplicationException e
) { 
 561                 Log_OC
.e(TAG
, "Fail to update descendants of " + folder
.getFileId() + " in database", e
); 
 563             } catch (RemoteException e
) { 
 564                 Log_OC
.e(TAG
, "Fail to update desendants of " + folder
.getFileId() + " in database", e
); 
 571     private Vector
<OCFile
> getFolderContent(long parentId
) { 
 573         Vector
<OCFile
> ret 
= new Vector
<OCFile
>(); 
 575         Uri req_uri 
= Uri
.withAppendedPath( 
 576                 ProviderTableMeta
.CONTENT_URI_DIR
, 
 577                 String
.valueOf(parentId
)); 
 580         if (getContentProviderClient() != null
) { 
 582                 c 
= getContentProviderClient().query(req_uri
, null
,  
 583                         ProviderTableMeta
.FILE_PARENT 
+ "=?" , 
 584                         new String
[] { String
.valueOf(parentId
)}, null
); 
 585             } catch (RemoteException e
) { 
 586                 Log_OC
.e(TAG
, e
.getMessage()); 
 590             c 
= getContentResolver().query(req_uri
, null
,  
 591                     ProviderTableMeta
.FILE_PARENT 
+ "=?" , 
 592                     new String
[] { String
.valueOf(parentId
)}, null
); 
 595         if (c
.moveToFirst()) { 
 597                 OCFile child 
= createFileInstance(c
); 
 599             } while (c
.moveToNext()); 
 604         Collections
.sort(ret
); 
 610     private OCFile 
createRootDir() { 
 611         OCFile file 
= new OCFile(OCFile
.ROOT_PATH
); 
 612         file
.setMimetype("DIR"); 
 613         file
.setParentId(FileDataStorageManager
.ROOT_PARENT_ID
); 
 618     private boolean fileExists(String cmp_key
, String value
) { 
 620         if (getContentResolver() != null
) { 
 621             c 
= getContentResolver() 
 622                     .query(ProviderTableMeta
.CONTENT_URI
, 
 625                                     + ProviderTableMeta
.FILE_ACCOUNT_OWNER
 
 627                                     new String
[] { value
, mAccount
.name 
}, null
); 
 630                 c 
= getContentProviderClient().query( 
 631                         ProviderTableMeta
.CONTENT_URI
, 
 634                                 + ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=?", 
 635                                 new String
[] { value
, mAccount
.name 
}, null
); 
 636             } catch (RemoteException e
) { 
 638                         "Couldn't determine file existance, assuming non existance: " 
 643         boolean retval 
= c
.moveToFirst(); 
 648     private Cursor 
getCursorForValue(String key
, String value
) { 
 650         if (getContentResolver() != null
) { 
 651             c 
= getContentResolver() 
 652                     .query(ProviderTableMeta
.CONTENT_URI
, 
 655                                     + ProviderTableMeta
.FILE_ACCOUNT_OWNER
 
 657                                     new String
[] { value
, mAccount
.name 
}, null
); 
 660                 c 
= getContentProviderClient().query( 
 661                         ProviderTableMeta
.CONTENT_URI
, 
 663                         key 
+ "=? AND " + ProviderTableMeta
.FILE_ACCOUNT_OWNER
 
 664                         + "=?", new String
[] { value
, mAccount
.name 
}, 
 666             } catch (RemoteException e
) { 
 667                 Log_OC
.e(TAG
, "Could not get file details: " + e
.getMessage()); 
 674     private OCFile 
createFileInstance(Cursor c
) { 
 677             file 
= new OCFile(c
.getString(c
 
 678                     .getColumnIndex(ProviderTableMeta
.FILE_PATH
))); 
 679             file
.setFileId(c
.getLong(c
.getColumnIndex(ProviderTableMeta
._ID
))); 
 680             file
.setParentId(c
.getLong(c
 
 681                     .getColumnIndex(ProviderTableMeta
.FILE_PARENT
))); 
 682             file
.setMimetype(c
.getString(c
 
 683                     .getColumnIndex(ProviderTableMeta
.FILE_CONTENT_TYPE
))); 
 684             if (!file
.isFolder()) { 
 685                 file
.setStoragePath(c
.getString(c
 
 686                         .getColumnIndex(ProviderTableMeta
.FILE_STORAGE_PATH
))); 
 687                 if (file
.getStoragePath() == null
) { 
 688                     // try to find existing file and bind it with current account; - with the current update of SynchronizeFolderOperation, this won't be necessary anymore after a full synchronization of the account 
 689                     File f 
= new File(FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, file
)); 
 691                         file
.setStoragePath(f
.getAbsolutePath()); 
 692                         file
.setLastSyncDateForData(f
.lastModified()); 
 696             file
.setFileLength(c
.getLong(c
 
 697                     .getColumnIndex(ProviderTableMeta
.FILE_CONTENT_LENGTH
))); 
 698             file
.setCreationTimestamp(c
.getLong(c
 
 699                     .getColumnIndex(ProviderTableMeta
.FILE_CREATION
))); 
 700             file
.setModificationTimestamp(c
.getLong(c
 
 701                     .getColumnIndex(ProviderTableMeta
.FILE_MODIFIED
))); 
 702             file
.setModificationTimestampAtLastSyncForData(c
.getLong(c
 
 703                     .getColumnIndex(ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
))); 
 704             file
.setLastSyncDateForProperties(c
.getLong(c
 
 705                     .getColumnIndex(ProviderTableMeta
.FILE_LAST_SYNC_DATE
))); 
 706             file
.setLastSyncDateForData(c
.getLong(c
. 
 707                     getColumnIndex(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
))); 
 708             file
.setKeepInSync(c
.getInt( 
 709                     c
.getColumnIndex(ProviderTableMeta
.FILE_KEEP_IN_SYNC
)) == 1 ? true 
: false
); 
 710             file
.setEtag(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_ETAG
))); 
 711             file
.setShareByLink(c
.getInt( 
 712                     c
.getColumnIndex(ProviderTableMeta
.FILE_SHARE_BY_LINK
)) == 1 ? true 
: false
); 
 713             file
.setPublicLink(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_PUBLIC_LINK
))); 
 720      * Returns if the file/folder is shared by link or not 
 721      * @param path  Path of the file/folder 
 724     public boolean isFileShareByLink(String path
) { 
 725         Cursor c 
= getCursorForValue(ProviderTableMeta
.FILE_STORAGE_PATH
, path
); 
 727         if (c
.moveToFirst()) { 
 728             file 
= createFileInstance(c
); 
 731         return file
.isShareByLink(); 
 735      * Returns the public link of the file/folder 
 736      * @param path  Path of the file/folder 
 739     public String 
getFilePublicLink(String path
) { 
 740         Cursor c 
= getCursorForValue(ProviderTableMeta
.FILE_STORAGE_PATH
, path
); 
 742         if (c
.moveToFirst()) { 
 743             file 
= createFileInstance(c
); 
 746         return file
.getPublicLink();