2 * ownCloud Android client application
4 * Copyright (C) 2012 Bartek Przybylski
5 * Copyright (C) 2015 ownCloud Inc.
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2,
9 * as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 package com
.owncloud
.android
.datamodel
;
24 import java
.util
.ArrayList
;
25 import java
.util
.Collection
;
26 import java
.util
.Collections
;
27 import java
.util
.HashSet
;
28 import java
.util
.Iterator
;
29 import java
.util
.List
;
31 import java
.util
.Vector
;
33 import android
.accounts
.Account
;
34 import android
.content
.ContentProviderClient
;
35 import android
.content
.ContentProviderOperation
;
36 import android
.content
.ContentProviderResult
;
37 import android
.content
.ContentResolver
;
38 import android
.content
.ContentUris
;
39 import android
.content
.ContentValues
;
40 import android
.content
.Intent
;
41 import android
.content
.OperationApplicationException
;
42 import android
.database
.Cursor
;
43 import android
.net
.Uri
;
44 import android
.os
.RemoteException
;
45 import android
.provider
.MediaStore
;
47 import com
.owncloud
.android
.MainApp
;
48 import com
.owncloud
.android
.db
.ProviderMeta
.ProviderTableMeta
;
49 import com
.owncloud
.android
.lib
.common
.utils
.Log_OC
;
50 import com
.owncloud
.android
.lib
.resources
.files
.FileUtils
;
51 import com
.owncloud
.android
.lib
.resources
.shares
.OCShare
;
52 import com
.owncloud
.android
.lib
.resources
.shares
.ShareType
;
53 import com
.owncloud
.android
.utils
.FileStorageUtils
;
55 import java
.io
.FileInputStream
;
56 import java
.io
.FileOutputStream
;
57 import java
.io
.IOException
;
58 import java
.io
.InputStream
;
59 import java
.io
.OutputStream
;
61 public class FileDataStorageManager
{
63 public static final int ROOT_PARENT_ID
= 0;
65 private ContentResolver mContentResolver
;
66 private ContentProviderClient mContentProviderClient
;
67 private Account mAccount
;
69 private static String TAG
= FileDataStorageManager
.class.getSimpleName();
72 public FileDataStorageManager(Account account
, ContentResolver cr
) {
73 mContentProviderClient
= null
;
74 mContentResolver
= cr
;
78 public FileDataStorageManager(Account account
, ContentProviderClient cp
) {
79 mContentProviderClient
= cp
;
80 mContentResolver
= null
;
85 public void setAccount(Account account
) {
89 public Account
getAccount() {
93 public ContentResolver
getContentResolver() {
94 return mContentResolver
;
97 public ContentProviderClient
getContentProviderClient() {
98 return mContentProviderClient
;
102 public OCFile
getFileByPath(String path
) {
103 Cursor c
= getCursorForValue(ProviderTableMeta
.FILE_PATH
, path
);
105 if (c
.moveToFirst()) {
106 file
= createFileInstance(c
);
109 if (file
== null
&& OCFile
.ROOT_PATH
.equals(path
)) {
110 return createRootDir(); // root should always exist
116 public OCFile
getFileById(long id
) {
117 Cursor c
= getCursorForValue(ProviderTableMeta
._ID
, String
.valueOf(id
));
119 if (c
.moveToFirst()) {
120 file
= createFileInstance(c
);
126 public OCFile
getFileByLocalPath(String path
) {
127 Cursor c
= getCursorForValue(ProviderTableMeta
.FILE_STORAGE_PATH
, path
);
129 if (c
.moveToFirst()) {
130 file
= createFileInstance(c
);
136 public boolean fileExists(long id
) {
137 return fileExists(ProviderTableMeta
._ID
, String
.valueOf(id
));
140 public boolean fileExists(String path
) {
141 return fileExists(ProviderTableMeta
.FILE_PATH
, path
);
145 public Vector
<OCFile
> getFolderContent(OCFile f
, boolean onlyOnDevice
) {
146 if (f
!= null
&& f
.isFolder() && f
.getFileId() != -1) {
147 return getFolderContent(f
.getFileId(), onlyOnDevice
);
150 return new Vector
<OCFile
>();
155 public Vector
<OCFile
> getFolderImages(OCFile folder
, boolean onlyOnDevice
) {
156 Vector
<OCFile
> ret
= new Vector
<OCFile
>();
157 if (folder
!= null
) {
158 // TODO better implementation, filtering in the access to database instead of here
159 Vector
<OCFile
> tmp
= getFolderContent(folder
, onlyOnDevice
);
160 OCFile current
= null
;
161 for (int i
=0; i
<tmp
.size(); i
++) {
162 current
= tmp
.get(i
);
163 if (current
.isImage()) {
171 public boolean saveFile(OCFile file
) {
172 boolean overriden
= false
;
173 ContentValues cv
= new ContentValues();
174 cv
.put(ProviderTableMeta
.FILE_MODIFIED
, file
.getModificationTimestamp());
176 ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
,
177 file
.getModificationTimestampAtLastSyncForData()
179 cv
.put(ProviderTableMeta
.FILE_CREATION
, file
.getCreationTimestamp());
180 cv
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, file
.getFileLength());
181 cv
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, file
.getMimetype());
182 cv
.put(ProviderTableMeta
.FILE_NAME
, file
.getFileName());
183 cv
.put(ProviderTableMeta
.FILE_PARENT
, file
.getParentId());
184 cv
.put(ProviderTableMeta
.FILE_PATH
, file
.getRemotePath());
185 if (!file
.isFolder())
186 cv
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, file
.getStoragePath());
187 cv
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, mAccount
.name
);
188 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, file
.getLastSyncDateForProperties());
189 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
, file
.getLastSyncDateForData());
190 cv
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, file
.isFavorite() ?
1 : 0);
191 cv
.put(ProviderTableMeta
.FILE_ETAG
, file
.getEtag());
192 cv
.put(ProviderTableMeta
.FILE_SHARED_VIA_LINK
, file
.isSharedViaLink() ?
1 : 0);
193 cv
.put(ProviderTableMeta
.FILE_SHARED_WITH_SHAREE
, file
.isSharedWithSharee() ?
1 : 0);
194 cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, file
.getPublicLink());
195 cv
.put(ProviderTableMeta
.FILE_PERMISSIONS
, file
.getPermissions());
196 cv
.put(ProviderTableMeta
.FILE_REMOTE_ID
, file
.getRemoteId());
197 cv
.put(ProviderTableMeta
.FILE_UPDATE_THUMBNAIL
, file
.needsUpdateThumbnail());
198 cv
.put(ProviderTableMeta
.FILE_IS_DOWNLOADING
, file
.isDownloading());
199 cv
.put(ProviderTableMeta
.FILE_ETAG_IN_CONFLICT
, file
.getEtagInConflict());
201 boolean sameRemotePath
= fileExists(file
.getRemotePath());
202 if (sameRemotePath
||
203 fileExists(file
.getFileId())) { // for renamed files; no more delete and create
206 if (sameRemotePath
) {
207 oldFile
= getFileByPath(file
.getRemotePath());
208 file
.setFileId(oldFile
.getFileId());
210 oldFile
= getFileById(file
.getFileId());
214 if (getContentResolver() != null
) {
215 getContentResolver().update(ProviderTableMeta
.CONTENT_URI
, cv
,
216 ProviderTableMeta
._ID
+ "=?",
217 new String
[]{String
.valueOf(file
.getFileId())});
220 getContentProviderClient().update(ProviderTableMeta
.CONTENT_URI
,
221 cv
, ProviderTableMeta
._ID
+ "=?",
222 new String
[]{String
.valueOf(file
.getFileId())});
223 } catch (RemoteException e
) {
225 "Fail to insert insert file to database "
230 Uri result_uri
= null
;
231 if (getContentResolver() != null
) {
232 result_uri
= getContentResolver().insert(
233 ProviderTableMeta
.CONTENT_URI_FILE
, cv
);
236 result_uri
= getContentProviderClient().insert(
237 ProviderTableMeta
.CONTENT_URI_FILE
, cv
);
238 } catch (RemoteException e
) {
240 "Fail to insert insert file to database "
244 if (result_uri
!= null
) {
245 long new_id
= Long
.parseLong(result_uri
.getPathSegments()
247 file
.setFileId(new_id
);
256 * Inserts or updates the list of files contained in a given folder.
258 * CALLER IS THE RESPONSIBLE FOR GRANTING RIGHT UPDATE OF INFORMATION, NOT THIS METHOD.
259 * HERE ONLY DATA CONSISTENCY SHOULD BE GRANTED
262 * @param updatedFiles
263 * @param filesToRemove
265 public void saveFolder(
266 OCFile folder
, Collection
<OCFile
> updatedFiles
, Collection
<OCFile
> filesToRemove
269 Log_OC
.d(TAG
, "Saving folder " + folder
.getRemotePath() + " with " + updatedFiles
.size()
270 + " children and " + filesToRemove
.size() + " files to remove");
272 ArrayList
<ContentProviderOperation
> operations
=
273 new ArrayList
<ContentProviderOperation
>(updatedFiles
.size());
275 // prepare operations to insert or update files to save in the given folder
276 for (OCFile file
: updatedFiles
) {
277 ContentValues cv
= new ContentValues();
278 cv
.put(ProviderTableMeta
.FILE_MODIFIED
, file
.getModificationTimestamp());
280 ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
,
281 file
.getModificationTimestampAtLastSyncForData()
283 cv
.put(ProviderTableMeta
.FILE_CREATION
, file
.getCreationTimestamp());
284 cv
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, file
.getFileLength());
285 cv
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, file
.getMimetype());
286 cv
.put(ProviderTableMeta
.FILE_NAME
, file
.getFileName());
287 //cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
288 cv
.put(ProviderTableMeta
.FILE_PARENT
, folder
.getFileId());
289 cv
.put(ProviderTableMeta
.FILE_PATH
, file
.getRemotePath());
290 if (!file
.isFolder()) {
291 cv
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, file
.getStoragePath());
293 cv
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, mAccount
.name
);
294 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, file
.getLastSyncDateForProperties());
295 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
, file
.getLastSyncDateForData());
296 cv
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, file
.isFavorite() ?
1 : 0);
297 cv
.put(ProviderTableMeta
.FILE_ETAG
, file
.getEtag());
298 cv
.put(ProviderTableMeta
.FILE_SHARED_VIA_LINK
, file
.isSharedViaLink() ?
1 : 0);
299 cv
.put(ProviderTableMeta
.FILE_SHARED_WITH_SHAREE
, file
.isSharedWithSharee() ?
1 : 0);
300 cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, file
.getPublicLink());
301 cv
.put(ProviderTableMeta
.FILE_PERMISSIONS
, file
.getPermissions());
302 cv
.put(ProviderTableMeta
.FILE_REMOTE_ID
, file
.getRemoteId());
303 cv
.put(ProviderTableMeta
.FILE_UPDATE_THUMBNAIL
, file
.needsUpdateThumbnail());
304 cv
.put(ProviderTableMeta
.FILE_IS_DOWNLOADING
, file
.isDownloading());
305 cv
.put(ProviderTableMeta
.FILE_ETAG_IN_CONFLICT
, file
.getEtagInConflict());
307 boolean existsByPath
= fileExists(file
.getRemotePath());
308 if (existsByPath
|| fileExists(file
.getFileId())) {
309 // updating an existing file
310 operations
.add(ContentProviderOperation
.newUpdate(ProviderTableMeta
.CONTENT_URI
).
312 withSelection(ProviderTableMeta
._ID
+ "=?",
313 new String
[]{String
.valueOf(file
.getFileId())})
318 operations
.add(ContentProviderOperation
.newInsert(ProviderTableMeta
.CONTENT_URI
).
319 withValues(cv
).build());
323 // prepare operations to remove files in the given folder
324 String where
= ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=?" + " AND " +
325 ProviderTableMeta
.FILE_PATH
+ "=?";
326 String
[] whereArgs
= null
;
327 for (OCFile file
: filesToRemove
) {
328 if (file
.getParentId() == folder
.getFileId()) {
329 whereArgs
= new String
[]{mAccount
.name
, file
.getRemotePath()};
330 if (file
.isFolder()) {
331 operations
.add(ContentProviderOperation
.newDelete(
332 ContentUris
.withAppendedId(
333 ProviderTableMeta
.CONTENT_URI_DIR
, file
.getFileId()
335 ).withSelection(where
, whereArgs
).build());
338 new File(FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, file
));
339 if (localFolder
.exists()) {
340 removeLocalFolder(localFolder
);
343 operations
.add(ContentProviderOperation
.newDelete(
344 ContentUris
.withAppendedId(
345 ProviderTableMeta
.CONTENT_URI_FILE
, file
.getFileId()
347 ).withSelection(where
, whereArgs
).build());
350 String path
= file
.getStoragePath();
351 new File(path
).delete();
352 triggerMediaScan(path
); // notify MediaScanner about removed file
358 // update metadata of folder
359 ContentValues cv
= new ContentValues();
360 cv
.put(ProviderTableMeta
.FILE_MODIFIED
, folder
.getModificationTimestamp());
362 ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
,
363 folder
.getModificationTimestampAtLastSyncForData()
365 cv
.put(ProviderTableMeta
.FILE_CREATION
, folder
.getCreationTimestamp());
366 cv
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, 0);
367 cv
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, folder
.getMimetype());
368 cv
.put(ProviderTableMeta
.FILE_NAME
, folder
.getFileName());
369 cv
.put(ProviderTableMeta
.FILE_PARENT
, folder
.getParentId());
370 cv
.put(ProviderTableMeta
.FILE_PATH
, folder
.getRemotePath());
371 cv
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, mAccount
.name
);
372 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, folder
.getLastSyncDateForProperties());
373 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
, folder
.getLastSyncDateForData());
374 cv
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, folder
.isFavorite() ?
1 : 0);
375 cv
.put(ProviderTableMeta
.FILE_ETAG
, folder
.getEtag());
376 cv
.put(ProviderTableMeta
.FILE_SHARED_VIA_LINK
, folder
.isSharedViaLink() ?
1 : 0);
377 cv
.put(ProviderTableMeta
.FILE_SHARED_WITH_SHAREE
, folder
.isSharedWithSharee() ?
1 : 0);
378 cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, folder
.getPublicLink());
379 cv
.put(ProviderTableMeta
.FILE_PERMISSIONS
, folder
.getPermissions());
380 cv
.put(ProviderTableMeta
.FILE_REMOTE_ID
, folder
.getRemoteId());
382 operations
.add(ContentProviderOperation
.newUpdate(ProviderTableMeta
.CONTENT_URI
).
384 withSelection(ProviderTableMeta
._ID
+ "=?",
385 new String
[]{String
.valueOf(folder
.getFileId())})
388 // apply operations in batch
389 ContentProviderResult
[] results
= null
;
390 Log_OC
.d(TAG
, "Sending " + operations
.size() + " operations to FileContentProvider");
392 if (getContentResolver() != null
) {
393 results
= getContentResolver().applyBatch(MainApp
.getAuthority(), operations
);
396 results
= getContentProviderClient().applyBatch(operations
);
399 } catch (OperationApplicationException e
) {
400 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
402 } catch (RemoteException e
) {
403 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
406 // update new id in file objects for insertions
407 if (results
!= null
) {
409 Iterator
<OCFile
> filesIt
= updatedFiles
.iterator();
411 for (int i
= 0; i
< results
.length
; i
++) {
412 if (filesIt
.hasNext()) {
413 file
= filesIt
.next();
417 if (results
[i
].uri
!= null
) {
418 newId
= Long
.parseLong(results
[i
].uri
.getPathSegments().get(1));
419 //updatedFiles.get(i).setFileId(newId);
421 file
.setFileId(newId
);
430 public boolean removeFile(OCFile file
, boolean removeDBData
, boolean removeLocalCopy
) {
431 boolean success
= true
;
433 if (file
.isFolder()) {
434 success
= removeFolder(file
, removeDBData
, removeLocalCopy
);
438 //Uri file_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE,
439 // ""+file.getFileId());
440 Uri file_uri
= ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_FILE
,
442 String where
= ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=?" + " AND " +
443 ProviderTableMeta
.FILE_PATH
+ "=?";
444 String
[] whereArgs
= new String
[]{mAccount
.name
, file
.getRemotePath()};
446 if (getContentProviderClient() != null
) {
448 deleted
= getContentProviderClient().delete(file_uri
, where
, whereArgs
);
449 } catch (RemoteException e
) {
453 deleted
= getContentResolver().delete(file_uri
, where
, whereArgs
);
455 success
&= (deleted
> 0);
457 String localPath
= file
.getStoragePath();
458 if (removeLocalCopy
&& file
.isDown() && localPath
!= null
&& success
) {
459 success
= new File(localPath
).delete();
461 deleteFileInMediaScan(localPath
);
463 if (!removeDBData
&& success
) {
464 // maybe unnecessary, but should be checked TODO remove if unnecessary
465 file
.setStoragePath(null
);
467 saveConflict(file
, null
);
476 public boolean removeFolder(OCFile folder
, boolean removeDBData
, boolean removeLocalContent
) {
477 boolean success
= true
;
478 if (folder
!= null
&& folder
.isFolder()) {
479 if (removeDBData
&& folder
.getFileId() != -1) {
480 success
= removeFolderInDb(folder
);
482 if (removeLocalContent
&& success
) {
483 success
= removeLocalFolder(folder
);
489 private boolean removeFolderInDb(OCFile folder
) {
490 Uri folder_uri
= Uri
.withAppendedPath(ProviderTableMeta
.CONTENT_URI_DIR
, "" +
491 folder
.getFileId()); // URI for recursive deletion
492 String where
= ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=?" + " AND " +
493 ProviderTableMeta
.FILE_PATH
+ "=?";
494 String
[] whereArgs
= new String
[]{mAccount
.name
, folder
.getRemotePath()};
496 if (getContentProviderClient() != null
) {
498 deleted
= getContentProviderClient().delete(folder_uri
, where
, whereArgs
);
499 } catch (RemoteException e
) {
503 deleted
= getContentResolver().delete(folder_uri
, where
, whereArgs
);
508 private boolean removeLocalFolder(OCFile folder
) {
509 boolean success
= true
;
510 String localFolderPath
= FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, folder
);
511 File localFolder
= new File(localFolderPath
);
512 if (localFolder
.exists()) {
513 // stage 1: remove the local files already registered in the files database
514 Vector
<OCFile
> files
= getFolderContent(folder
.getFileId(), false
);
516 for (OCFile file
: files
) {
517 if (file
.isFolder()) {
518 success
&= removeLocalFolder(file
);
521 File localFile
= new File(file
.getStoragePath());
522 success
&= localFile
.delete();
524 // notify MediaScanner about removed file
525 deleteFileInMediaScan(file
.getStoragePath());
526 file
.setStoragePath(null
);
534 // stage 2: remove the folder itself and any local file inside out of sync;
535 // for instance, after clearing the app cache or reinstalling
536 success
&= removeLocalFolder(localFolder
);
541 private boolean removeLocalFolder(File localFolder
) {
542 boolean success
= true
;
543 File
[] localFiles
= localFolder
.listFiles();
544 if (localFiles
!= null
) {
545 for (File localFile
: localFiles
) {
546 if (localFile
.isDirectory()) {
547 success
&= removeLocalFolder(localFile
);
549 String path
= localFile
.getAbsolutePath();
550 success
&= localFile
.delete();
554 success
&= localFolder
.delete();
560 * Updates database and file system for a file or folder that was moved to a different location.
562 * TODO explore better (faster) implementations
563 * TODO throw exceptions up !
565 public void moveLocalFile(OCFile file
, String targetPath
, String targetParentPath
) {
567 if (file
!= null
&& file
.fileExists() && !OCFile
.ROOT_PATH
.equals(file
.getFileName())) {
569 OCFile targetParent
= getFileByPath(targetParentPath
);
570 if (targetParent
== null
) {
571 throw new IllegalStateException(
572 "Parent folder of the target path does not exist!!");
575 /// 1. get all the descendants of the moved element in a single QUERY
577 if (getContentProviderClient() != null
) {
579 c
= getContentProviderClient().query(
580 ProviderTableMeta
.CONTENT_URI
,
582 ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=? AND " +
583 ProviderTableMeta
.FILE_PATH
+ " LIKE ? ",
586 file
.getRemotePath() + "%"
588 ProviderTableMeta
.FILE_PATH
+ " ASC "
590 } catch (RemoteException e
) {
591 Log_OC
.e(TAG
, e
.getMessage());
595 c
= getContentResolver().query(
596 ProviderTableMeta
.CONTENT_URI
,
598 ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=? AND " +
599 ProviderTableMeta
.FILE_PATH
+ " LIKE ? ",
602 file
.getRemotePath() + "%"
604 ProviderTableMeta
.FILE_PATH
+ " ASC "
608 /// 2. prepare a batch of update operations to change all the descendants
609 ArrayList
<ContentProviderOperation
> operations
=
610 new ArrayList
<ContentProviderOperation
>(c
.getCount());
611 String defaultSavePath
= FileStorageUtils
.getSavePath(mAccount
.name
);
612 List
<String
> originalPathsToTriggerMediaScan
= new ArrayList
<String
>();
613 List
<String
> newPathsToTriggerMediaScan
= new ArrayList
<String
>();
614 if (c
.moveToFirst()) {
615 int lengthOfOldPath
= file
.getRemotePath().length();
616 int lengthOfOldStoragePath
= defaultSavePath
.length() + lengthOfOldPath
;
618 ContentValues cv
= new ContentValues(); // keep construction in the loop
619 OCFile child
= createFileInstance(c
);
621 ProviderTableMeta
.FILE_PATH
,
622 targetPath
+ child
.getRemotePath().substring(lengthOfOldPath
)
624 if (child
.getStoragePath() != null
&&
625 child
.getStoragePath().startsWith(defaultSavePath
)) {
626 // update link to downloaded content - but local move is not done here!
627 String targetLocalPath
= defaultSavePath
+ targetPath
+
628 child
.getStoragePath().substring(lengthOfOldStoragePath
);
630 cv
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, targetLocalPath
);
632 originalPathsToTriggerMediaScan
.add(child
.getStoragePath());
633 newPathsToTriggerMediaScan
.add(targetLocalPath
);
636 if (child
.getRemotePath().equals(file
.getRemotePath())) {
638 ProviderTableMeta
.FILE_PARENT
,
639 targetParent
.getFileId()
643 ContentProviderOperation
.newUpdate(ProviderTableMeta
.CONTENT_URI
).
646 ProviderTableMeta
._ID
+ "=?",
647 new String
[]{String
.valueOf(child
.getFileId())}
651 } while (c
.moveToNext());
655 /// 3. apply updates in batch
657 if (getContentResolver() != null
) {
658 getContentResolver().applyBatch(MainApp
.getAuthority(), operations
);
661 getContentProviderClient().applyBatch(operations
);
664 } catch (Exception e
) {
665 Log_OC
.e(TAG
, "Fail to update " + file
.getFileId() + " and descendants in database",
669 /// 4. move in local file system
670 String originalLocalPath
= FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, file
);
671 String targetLocalPath
= defaultSavePath
+ targetPath
;
672 File localFile
= new File(originalLocalPath
);
673 boolean renamed
= false
;
674 if (localFile
.exists()) {
675 File targetFile
= new File(targetLocalPath
);
676 File targetFolder
= targetFile
.getParentFile();
677 if (!targetFolder
.exists()) {
678 targetFolder
.mkdirs();
680 renamed
= localFile
.renameTo(targetFile
);
684 Iterator
<String
> it
= originalPathsToTriggerMediaScan
.iterator();
685 while (it
.hasNext()) {
686 // Notify MediaScanner about removed file
687 deleteFileInMediaScan(it
.next());
689 it
= newPathsToTriggerMediaScan
.iterator();
690 while (it
.hasNext()) {
691 // Notify MediaScanner about new file/folder
692 triggerMediaScan(it
.next());
699 public void copyLocalFile(OCFile file
, String targetPath
) {
701 if (file
!= null
&& file
.fileExists() && !OCFile
.ROOT_PATH
.equals(file
.getFileName())) {
702 String localPath
= FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, file
);
703 File localFile
= new File(localPath
);
704 boolean copied
= false
;
705 String defaultSavePath
= FileStorageUtils
.getSavePath(mAccount
.name
);
706 if (localFile
.exists()) {
707 File targetFile
= new File(defaultSavePath
+ targetPath
);
708 File targetFolder
= targetFile
.getParentFile();
709 if (!targetFolder
.exists()) {
710 targetFolder
.mkdirs();
712 copied
= copyFile(localFile
, targetFile
);
714 Log_OC
.d(TAG
, "Local file COPIED : " + copied
);
718 private boolean copyFile(File src
, File target
) {
721 InputStream
in = null
;
722 OutputStream out
= null
;
725 in = new FileInputStream(src
);
726 out
= new FileOutputStream(target
);
727 byte[] buf
= new byte[1024];
729 while ((len
= in.read(buf
)) > 0) {
730 out
.write(buf
, 0, len
);
732 } catch (IOException ex
) {
735 if (in != null
) try {
737 } catch (IOException e
) {
738 e
.printStackTrace(System
.err
);
740 if (out
!= null
) try {
742 } catch (IOException e
) {
743 e
.printStackTrace(System
.err
);
751 private Vector
<OCFile
> getFolderContent(long parentId
, boolean onlyOnDevice
) {
753 Vector
<OCFile
> ret
= new Vector
<OCFile
>();
755 Uri req_uri
= Uri
.withAppendedPath(
756 ProviderTableMeta
.CONTENT_URI_DIR
,
757 String
.valueOf(parentId
));
760 if (getContentProviderClient() != null
) {
762 c
= getContentProviderClient().query(req_uri
, null
,
763 ProviderTableMeta
.FILE_PARENT
+ "=?",
764 new String
[]{String
.valueOf(parentId
)}, null
);
765 } catch (RemoteException e
) {
766 Log_OC
.e(TAG
, e
.getMessage());
770 c
= getContentResolver().query(req_uri
, null
,
771 ProviderTableMeta
.FILE_PARENT
+ "=?",
772 new String
[]{String
.valueOf(parentId
)}, null
);
775 if (c
.moveToFirst()) {
777 OCFile child
= createFileInstance(c
);
778 if (child
.isFolder() || !onlyOnDevice
|| onlyOnDevice
&& child
.isDown()){
781 } while (c
.moveToNext());
786 Collections
.sort(ret
);
792 private OCFile
createRootDir() {
793 OCFile file
= new OCFile(OCFile
.ROOT_PATH
);
794 file
.setMimetype("DIR");
795 file
.setParentId(FileDataStorageManager
.ROOT_PARENT_ID
);
800 private boolean fileExists(String cmp_key
, String value
) {
802 if (getContentResolver() != null
) {
803 c
= getContentResolver()
804 .query(ProviderTableMeta
.CONTENT_URI
,
807 + ProviderTableMeta
.FILE_ACCOUNT_OWNER
809 new String
[]{value
, mAccount
.name
}, null
);
812 c
= getContentProviderClient().query(
813 ProviderTableMeta
.CONTENT_URI
,
816 + ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=?",
817 new String
[]{value
, mAccount
.name
}, null
);
818 } catch (RemoteException e
) {
820 "Couldn't determine file existance, assuming non existance: "
825 boolean retval
= c
.moveToFirst();
830 private Cursor
getCursorForValue(String key
, String value
) {
832 if (getContentResolver() != null
) {
833 c
= getContentResolver()
834 .query(ProviderTableMeta
.CONTENT_URI
,
837 + ProviderTableMeta
.FILE_ACCOUNT_OWNER
839 new String
[]{value
, mAccount
.name
}, null
);
842 c
= getContentProviderClient().query(
843 ProviderTableMeta
.CONTENT_URI
,
845 key
+ "=? AND " + ProviderTableMeta
.FILE_ACCOUNT_OWNER
846 + "=?", new String
[]{value
, mAccount
.name
},
848 } catch (RemoteException e
) {
849 Log_OC
.e(TAG
, "Could not get file details: " + e
.getMessage());
857 private OCFile
createFileInstance(Cursor c
) {
860 file
= new OCFile(c
.getString(c
861 .getColumnIndex(ProviderTableMeta
.FILE_PATH
)));
862 file
.setFileId(c
.getLong(c
.getColumnIndex(ProviderTableMeta
._ID
)));
863 file
.setParentId(c
.getLong(c
864 .getColumnIndex(ProviderTableMeta
.FILE_PARENT
)));
865 file
.setMimetype(c
.getString(c
866 .getColumnIndex(ProviderTableMeta
.FILE_CONTENT_TYPE
)));
867 if (!file
.isFolder()) {
868 file
.setStoragePath(c
.getString(c
869 .getColumnIndex(ProviderTableMeta
.FILE_STORAGE_PATH
)));
870 if (file
.getStoragePath() == null
) {
871 // try to find existing file and bind it with current account;
872 // with the current update of SynchronizeFolderOperation, this won't be
873 // necessary anymore after a full synchronization of the account
874 File f
= new File(FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, file
));
876 file
.setStoragePath(f
.getAbsolutePath());
877 file
.setLastSyncDateForData(f
.lastModified());
881 file
.setFileLength(c
.getLong(c
882 .getColumnIndex(ProviderTableMeta
.FILE_CONTENT_LENGTH
)));
883 file
.setCreationTimestamp(c
.getLong(c
884 .getColumnIndex(ProviderTableMeta
.FILE_CREATION
)));
885 file
.setModificationTimestamp(c
.getLong(c
886 .getColumnIndex(ProviderTableMeta
.FILE_MODIFIED
)));
887 file
.setModificationTimestampAtLastSyncForData(c
.getLong(c
888 .getColumnIndex(ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
)));
889 file
.setLastSyncDateForProperties(c
.getLong(c
890 .getColumnIndex(ProviderTableMeta
.FILE_LAST_SYNC_DATE
)));
891 file
.setLastSyncDateForData(c
.getLong(c
.
892 getColumnIndex(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
)));
893 file
.setFavorite(c
.getInt(
894 c
.getColumnIndex(ProviderTableMeta
.FILE_KEEP_IN_SYNC
)) == 1 ? true
: false
);
895 file
.setEtag(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_ETAG
)));
896 file
.setShareViaLink(c
.getInt(
897 c
.getColumnIndex(ProviderTableMeta
.FILE_SHARED_VIA_LINK
)) == 1 ? true
: false
);
898 file
.setShareWithSharee(c
.getInt(
899 c
.getColumnIndex(ProviderTableMeta
.FILE_SHARED_WITH_SHAREE
)) == 1 ? true
: false
);
900 file
.setPublicLink(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_PUBLIC_LINK
)));
901 file
.setPermissions(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_PERMISSIONS
)));
902 file
.setRemoteId(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_REMOTE_ID
)));
903 file
.setNeedsUpdateThumbnail(c
.getInt(
904 c
.getColumnIndex(ProviderTableMeta
.FILE_UPDATE_THUMBNAIL
)) == 1 ? true
: false
);
905 file
.setDownloading(c
.getInt(
906 c
.getColumnIndex(ProviderTableMeta
.FILE_IS_DOWNLOADING
)) == 1 ? true
: false
);
907 file
.setEtagInConflict(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_ETAG_IN_CONFLICT
)));
913 // Methods for Shares
914 public boolean saveShare(OCShare share
) {
915 boolean overriden
= false
;
916 ContentValues cv
= new ContentValues();
917 cv
.put(ProviderTableMeta
.OCSHARES_FILE_SOURCE
, share
.getFileSource());
918 cv
.put(ProviderTableMeta
.OCSHARES_ITEM_SOURCE
, share
.getItemSource());
919 cv
.put(ProviderTableMeta
.OCSHARES_SHARE_TYPE
, share
.getShareType().getValue());
920 cv
.put(ProviderTableMeta
.OCSHARES_SHARE_WITH
, share
.getShareWith());
921 cv
.put(ProviderTableMeta
.OCSHARES_PATH
, share
.getPath());
922 cv
.put(ProviderTableMeta
.OCSHARES_PERMISSIONS
, share
.getPermissions());
923 cv
.put(ProviderTableMeta
.OCSHARES_SHARED_DATE
, share
.getSharedDate());
924 cv
.put(ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
, share
.getExpirationDate());
925 cv
.put(ProviderTableMeta
.OCSHARES_TOKEN
, share
.getToken());
927 ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
,
928 share
.getSharedWithDisplayName()
930 cv
.put(ProviderTableMeta
.OCSHARES_IS_DIRECTORY
, share
.isFolder() ?
1 : 0);
931 cv
.put(ProviderTableMeta
.OCSHARES_USER_ID
, share
.getUserId());
932 cv
.put(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
, share
.getIdRemoteShared());
933 cv
.put(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
, mAccount
.name
);
935 if (shareExists(share
.getIdRemoteShared())) {// for renamed files; no more delete and create
937 if (getContentResolver() != null
) {
938 getContentResolver().update(ProviderTableMeta
.CONTENT_URI_SHARE
, cv
,
939 ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
+ "=?",
940 new String
[]{String
.valueOf(share
.getIdRemoteShared())});
943 getContentProviderClient().update(ProviderTableMeta
.CONTENT_URI_SHARE
,
944 cv
, ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
+ "=?",
945 new String
[]{String
.valueOf(share
.getIdRemoteShared())});
946 } catch (RemoteException e
) {
948 "Fail to insert insert file to database "
953 Uri result_uri
= null
;
954 if (getContentResolver() != null
) {
955 result_uri
= getContentResolver().insert(
956 ProviderTableMeta
.CONTENT_URI_SHARE
, cv
);
959 result_uri
= getContentProviderClient().insert(
960 ProviderTableMeta
.CONTENT_URI_SHARE
, cv
);
961 } catch (RemoteException e
) {
963 "Fail to insert insert file to database "
967 if (result_uri
!= null
) {
968 long new_id
= Long
.parseLong(result_uri
.getPathSegments()
978 public OCShare
getFirstShareByPathAndType(String path
, ShareType type
, String shareWith
) {
981 String selection
= ProviderTableMeta
.OCSHARES_PATH
+ "=? AND "
982 + ProviderTableMeta
.OCSHARES_SHARE_TYPE
+ "=? AND "
983 + ProviderTableMeta
.OCSHARES_SHARE_WITH
+ "=? AND "
984 + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
+ "=?" ;
986 String
[] selectionArgs
= new String
[]{path
, Integer
.toString(type
.getValue()),
987 shareWith
, mAccount
.name
};
989 if (getContentResolver() != null
) {
990 c
= getContentResolver().query(
991 ProviderTableMeta
.CONTENT_URI_SHARE
,
993 selection
, selectionArgs
,
997 c
= getContentProviderClient().query(
998 ProviderTableMeta
.CONTENT_URI_SHARE
,
1000 selection
, selectionArgs
,
1003 } catch (RemoteException e
) {
1004 Log_OC
.e(TAG
, "Could not get file details: " + e
.getMessage());
1008 OCShare share
= null
;
1009 if (c
.moveToFirst()) {
1010 share
= createShareInstance(c
);
1016 private OCShare
createShareInstance(Cursor c
) {
1017 OCShare share
= null
;
1019 share
= new OCShare(c
.getString(c
1020 .getColumnIndex(ProviderTableMeta
.OCSHARES_PATH
)));
1021 share
.setId(c
.getLong(c
.getColumnIndex(ProviderTableMeta
._ID
)));
1022 share
.setFileSource(c
.getLong(c
1023 .getColumnIndex(ProviderTableMeta
.OCSHARES_ITEM_SOURCE
)));
1024 share
.setShareType(ShareType
.fromValue(c
.getInt(c
1025 .getColumnIndex(ProviderTableMeta
.OCSHARES_SHARE_TYPE
))));
1026 share
.setShareWith(c
.getString(c
1027 .getColumnIndex(ProviderTableMeta
.OCSHARES_SHARE_WITH
)));
1028 share
.setPermissions(c
.getInt(c
1029 .getColumnIndex(ProviderTableMeta
.OCSHARES_PERMISSIONS
)));
1030 share
.setSharedDate(c
.getLong(c
1031 .getColumnIndex(ProviderTableMeta
.OCSHARES_SHARED_DATE
)));
1032 share
.setExpirationDate(c
.getLong(c
1033 .getColumnIndex(ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
)));
1034 share
.setToken(c
.getString(c
1035 .getColumnIndex(ProviderTableMeta
.OCSHARES_TOKEN
)));
1036 share
.setSharedWithDisplayName(c
.getString(c
1037 .getColumnIndex(ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
)));
1038 share
.setIsFolder(c
.getInt(
1039 c
.getColumnIndex(ProviderTableMeta
.OCSHARES_IS_DIRECTORY
)) == 1);
1040 share
.setUserId(c
.getLong(c
.getColumnIndex(ProviderTableMeta
.OCSHARES_USER_ID
)));
1041 share
.setIdRemoteShared(c
.getLong(
1042 c
.getColumnIndex(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
)));
1047 private boolean shareExists(String cmp_key
, String value
) {
1049 if (getContentResolver() != null
) {
1050 c
= getContentResolver()
1051 .query(ProviderTableMeta
.CONTENT_URI_SHARE
,
1054 + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
1056 new String
[]{value
, mAccount
.name
}, null
);
1059 c
= getContentProviderClient().query(
1060 ProviderTableMeta
.CONTENT_URI_SHARE
,
1063 + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
+ "=?",
1064 new String
[]{value
, mAccount
.name
}, null
);
1065 } catch (RemoteException e
) {
1067 "Couldn't determine file existance, assuming non existance: "
1072 boolean retval
= c
.moveToFirst();
1077 private boolean shareExists(long remoteId
) {
1078 return shareExists(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
, String
.valueOf(remoteId
));
1081 private void resetShareFlagsInAllFiles() {
1082 ContentValues cv
= new ContentValues();
1083 cv
.put(ProviderTableMeta
.FILE_SHARED_VIA_LINK
, false
);
1084 cv
.put(ProviderTableMeta
.FILE_SHARED_WITH_SHAREE
, false
);
1085 cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, "");
1086 String where
= ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=?";
1087 String
[] whereArgs
= new String
[]{mAccount
.name
};
1089 if (getContentResolver() != null
) {
1090 getContentResolver().update(ProviderTableMeta
.CONTENT_URI
, cv
, where
, whereArgs
);
1094 getContentProviderClient().update(ProviderTableMeta
.CONTENT_URI
, cv
, where
,
1096 } catch (RemoteException e
) {
1097 Log_OC
.e(TAG
, "Exception in resetShareFlagsInAllFiles" + e
.getMessage());
1102 private void resetShareFlagsInFolder(OCFile folder
) {
1103 ContentValues cv
= new ContentValues();
1104 cv
.put(ProviderTableMeta
.FILE_SHARED_VIA_LINK
, false
);
1105 cv
.put(ProviderTableMeta
.FILE_SHARED_WITH_SHAREE
, false
);
1106 cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, "");
1107 String where
= ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=? AND " +
1108 ProviderTableMeta
.FILE_PARENT
+ "=?";
1109 String
[] whereArgs
= new String
[] { mAccount
.name
, String
.valueOf(folder
.getFileId()) };
1111 if (getContentResolver() != null
) {
1112 getContentResolver().update(ProviderTableMeta
.CONTENT_URI
, cv
, where
, whereArgs
);
1116 getContentProviderClient().update(ProviderTableMeta
.CONTENT_URI
, cv
, where
,
1118 } catch (RemoteException e
) {
1119 Log_OC
.e(TAG
, "Exception in resetShareFlagsInFolder " + e
.getMessage());
1124 private void resetShareFlagInAFile(String filePath
){
1125 ContentValues cv
= new ContentValues();
1126 cv
.put(ProviderTableMeta
.FILE_SHARED_VIA_LINK
, false
);
1127 cv
.put(ProviderTableMeta
.FILE_SHARED_WITH_SHAREE
, false
);
1128 cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, "");
1129 String where
= ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=? AND " +
1130 ProviderTableMeta
.FILE_PATH
+ "=?";
1131 String
[] whereArgs
= new String
[] { mAccount
.name
, filePath
};
1133 if (getContentResolver() != null
) {
1134 getContentResolver().update(ProviderTableMeta
.CONTENT_URI
, cv
, where
, whereArgs
);
1138 getContentProviderClient().update(ProviderTableMeta
.CONTENT_URI
, cv
, where
,
1140 } catch (RemoteException e
) {
1141 Log_OC
.e(TAG
, "Exception in resetShareFlagsInFolder " + e
.getMessage());
1146 private void cleanShares() {
1147 String where
= ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
+ "=?";
1148 String
[] whereArgs
= new String
[]{mAccount
.name
};
1150 if (getContentResolver() != null
) {
1151 getContentResolver().delete(ProviderTableMeta
.CONTENT_URI_SHARE
, where
, whereArgs
);
1155 getContentProviderClient().delete(ProviderTableMeta
.CONTENT_URI_SHARE
, where
,
1157 } catch (RemoteException e
) {
1158 Log_OC
.e(TAG
, "Exception in cleanShares" + e
.getMessage());
1163 public void saveShares(Collection
<OCShare
> shares
) {
1165 if (shares
!= null
) {
1166 ArrayList
<ContentProviderOperation
> operations
=
1167 new ArrayList
<ContentProviderOperation
>(shares
.size());
1169 // prepare operations to insert or update files to save in the given folder
1170 for (OCShare share
: shares
) {
1171 ContentValues cv
= new ContentValues();
1172 cv
.put(ProviderTableMeta
.OCSHARES_FILE_SOURCE
, share
.getFileSource());
1173 cv
.put(ProviderTableMeta
.OCSHARES_ITEM_SOURCE
, share
.getItemSource());
1174 cv
.put(ProviderTableMeta
.OCSHARES_SHARE_TYPE
, share
.getShareType().getValue());
1175 cv
.put(ProviderTableMeta
.OCSHARES_SHARE_WITH
, share
.getShareWith());
1176 cv
.put(ProviderTableMeta
.OCSHARES_PATH
, share
.getPath());
1177 cv
.put(ProviderTableMeta
.OCSHARES_PERMISSIONS
, share
.getPermissions());
1178 cv
.put(ProviderTableMeta
.OCSHARES_SHARED_DATE
, share
.getSharedDate());
1179 cv
.put(ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
, share
.getExpirationDate());
1180 cv
.put(ProviderTableMeta
.OCSHARES_TOKEN
, share
.getToken());
1182 ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
,
1183 share
.getSharedWithDisplayName()
1185 cv
.put(ProviderTableMeta
.OCSHARES_IS_DIRECTORY
, share
.isFolder() ?
1 : 0);
1186 cv
.put(ProviderTableMeta
.OCSHARES_USER_ID
, share
.getUserId());
1187 cv
.put(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
, share
.getIdRemoteShared());
1188 cv
.put(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
, mAccount
.name
);
1190 if (shareExists(share
.getIdRemoteShared())) {
1191 // updating an existing file
1193 ContentProviderOperation
.newUpdate(ProviderTableMeta
.CONTENT_URI_SHARE
).
1195 withSelection(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
+ "=?",
1196 new String
[]{String
.valueOf(share
.getIdRemoteShared())})
1199 // adding a new file
1201 ContentProviderOperation
.newInsert(ProviderTableMeta
.CONTENT_URI_SHARE
).
1208 // apply operations in batch
1209 if (operations
.size() > 0) {
1210 @SuppressWarnings("unused")
1211 ContentProviderResult
[] results
= null
;
1212 Log_OC
.d(TAG
, "Sending " + operations
.size() +
1213 " operations to FileContentProvider");
1215 if (getContentResolver() != null
) {
1216 results
= getContentResolver().applyBatch(MainApp
.getAuthority(),
1219 results
= getContentProviderClient().applyBatch(operations
);
1222 } catch (OperationApplicationException e
) {
1223 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
1225 } catch (RemoteException e
) {
1226 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
1233 public void updateSharedFiles(Collection
<OCFile
> sharedFiles
) {
1234 resetShareFlagsInAllFiles();
1236 if (sharedFiles
!= null
) {
1237 ArrayList
<ContentProviderOperation
> operations
=
1238 new ArrayList
<ContentProviderOperation
>(sharedFiles
.size());
1240 // prepare operations to insert or update files to save in the given folder
1241 for (OCFile file
: sharedFiles
) {
1242 ContentValues cv
= new ContentValues();
1243 cv
.put(ProviderTableMeta
.FILE_MODIFIED
, file
.getModificationTimestamp());
1245 ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
,
1246 file
.getModificationTimestampAtLastSyncForData()
1248 cv
.put(ProviderTableMeta
.FILE_CREATION
, file
.getCreationTimestamp());
1249 cv
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, file
.getFileLength());
1250 cv
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, file
.getMimetype());
1251 cv
.put(ProviderTableMeta
.FILE_NAME
, file
.getFileName());
1252 cv
.put(ProviderTableMeta
.FILE_PARENT
, file
.getParentId());
1253 cv
.put(ProviderTableMeta
.FILE_PATH
, file
.getRemotePath());
1254 if (!file
.isFolder()) {
1255 cv
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, file
.getStoragePath());
1257 cv
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, mAccount
.name
);
1258 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, file
.getLastSyncDateForProperties());
1260 ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
,
1261 file
.getLastSyncDateForData()
1263 cv
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, file
.isFavorite() ?
1 : 0);
1264 cv
.put(ProviderTableMeta
.FILE_ETAG
, file
.getEtag());
1265 cv
.put(ProviderTableMeta
.FILE_SHARED_VIA_LINK
, file
.isSharedViaLink() ?
1 : 0);
1266 cv
.put(ProviderTableMeta
.FILE_SHARED_WITH_SHAREE
, file
.isSharedWithSharee() ?
1 : 0);
1267 cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, file
.getPublicLink());
1268 cv
.put(ProviderTableMeta
.FILE_PERMISSIONS
, file
.getPermissions());
1269 cv
.put(ProviderTableMeta
.FILE_REMOTE_ID
, file
.getRemoteId());
1271 ProviderTableMeta
.FILE_UPDATE_THUMBNAIL
,
1272 file
.needsUpdateThumbnail() ?
1 : 0
1275 ProviderTableMeta
.FILE_IS_DOWNLOADING
,
1276 file
.isDownloading() ?
1 : 0
1278 cv
.put(ProviderTableMeta
.FILE_ETAG_IN_CONFLICT
, file
.getEtagInConflict());
1280 boolean existsByPath
= fileExists(file
.getRemotePath());
1281 if (existsByPath
|| fileExists(file
.getFileId())) {
1282 // updating an existing file
1284 ContentProviderOperation
.newUpdate(ProviderTableMeta
.CONTENT_URI
).
1286 withSelection(ProviderTableMeta
._ID
+ "=?",
1287 new String
[]{String
.valueOf(file
.getFileId())})
1291 // adding a new file
1293 ContentProviderOperation
.newInsert(ProviderTableMeta
.CONTENT_URI
).
1300 // apply operations in batch
1301 if (operations
.size() > 0) {
1302 @SuppressWarnings("unused")
1303 ContentProviderResult
[] results
= null
;
1304 Log_OC
.d(TAG
, "Sending " + operations
.size() +
1305 " operations to FileContentProvider");
1307 if (getContentResolver() != null
) {
1308 results
= getContentResolver().applyBatch(MainApp
.getAuthority(), operations
);
1310 results
= getContentProviderClient().applyBatch(operations
);
1313 } catch (OperationApplicationException e
) {
1314 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
1316 } catch (RemoteException e
) {
1317 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
1324 public void removeShare(OCShare share
) {
1325 Uri share_uri
= ProviderTableMeta
.CONTENT_URI_SHARE
;
1326 String where
= ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
+ "=?" + " AND " +
1327 ProviderTableMeta
._ID
+ "=?";
1328 String
[] whereArgs
= new String
[]{mAccount
.name
, Long
.toString(share
.getId())};
1329 if (getContentProviderClient() != null
) {
1331 getContentProviderClient().delete(share_uri
, where
, whereArgs
);
1332 } catch (RemoteException e
) {
1333 e
.printStackTrace();
1336 getContentResolver().delete(share_uri
, where
, whereArgs
);
1340 public void saveSharesDB(ArrayList
<OCShare
> shares
) {
1341 ArrayList
<ContentProviderOperation
> operations
= new ArrayList
<ContentProviderOperation
>();
1343 // Reset flags & Remove shares for this files
1344 String filePath
= "";
1345 for (OCShare share
: shares
) {
1346 if (filePath
!= share
.getPath()){
1347 filePath
= share
.getPath();
1348 resetShareFlagInAFile(filePath
);
1349 operations
= prepareRemoveSharesInFile(filePath
, operations
);
1353 // Add operations to insert shares
1354 operations
= prepareInsertShares(shares
, operations
);
1356 // apply operations in batch
1357 if (operations
.size() > 0) {
1358 Log_OC
.d(TAG
, "Sending " + operations
.size() + " operations to FileContentProvider");
1360 if (getContentResolver() != null
) {
1361 getContentResolver().applyBatch(MainApp
.getAuthority(), operations
);
1364 getContentProviderClient().applyBatch(operations
);
1367 } catch (OperationApplicationException e
) {
1368 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
1370 } catch (RemoteException e
) {
1371 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
1375 // // TODO: review if it is needed
1376 // // Update shared files
1377 // ArrayList<OCFile> sharedFiles = new ArrayList<OCFile>();
1379 // for (OCShare share : shares) {
1381 // String path = share.getPath();
1382 // if (share.isFolder()) {
1383 // path = path + FileUtils.PATH_SEPARATOR;
1386 // // Update OCFile with data from share: ShareByLink, publicLink and
1387 // OCFile file = getFileByPath(path);
1388 // if (file != null) {
1389 // if (share.getShareType().equals(ShareType.PUBLIC_LINK)) {
1390 // file.setShareViaLink(true);
1391 // sharedFiles.add(file);
1397 // updateSharedFiles(sharedFiles);
1401 public void saveSharesInFolder(ArrayList
<OCShare
> shares
, OCFile folder
) {
1402 resetShareFlagsInFolder(folder
);
1403 ArrayList
<ContentProviderOperation
> operations
= new ArrayList
<ContentProviderOperation
>();
1404 operations
= prepareRemoveSharesInFolder(folder
, operations
);
1406 if (shares
!= null
) {
1407 // prepare operations to insert or update files to save in the given folder
1408 operations
= prepareInsertShares(shares
, operations
);
1411 // apply operations in batch
1412 if (operations
.size() > 0) {
1413 Log_OC
.d(TAG
, "Sending " + operations
.size() + " operations to FileContentProvider");
1415 if (getContentResolver() != null
) {
1416 getContentResolver().applyBatch(MainApp
.getAuthority(), operations
);
1419 getContentProviderClient().applyBatch(operations
);
1422 } catch (OperationApplicationException e
) {
1423 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
1425 } catch (RemoteException e
) {
1433 * Prepare operations to insert or update files to save in the given folder
1434 * @param shares List of shares to insert
1435 * @param operations List of operations
1438 private ArrayList
<ContentProviderOperation
> prepareInsertShares(
1439 ArrayList
<OCShare
> shares
, ArrayList
<ContentProviderOperation
> operations
) {
1441 if (shares
!= null
) {
1442 // prepare operations to insert or update files to save in the given folder
1443 for (OCShare share
: shares
) {
1444 ContentValues cv
= new ContentValues();
1445 cv
.put(ProviderTableMeta
.OCSHARES_FILE_SOURCE
, share
.getFileSource());
1446 cv
.put(ProviderTableMeta
.OCSHARES_ITEM_SOURCE
, share
.getItemSource());
1447 cv
.put(ProviderTableMeta
.OCSHARES_SHARE_TYPE
, share
.getShareType().getValue());
1448 cv
.put(ProviderTableMeta
.OCSHARES_SHARE_WITH
, share
.getShareWith());
1449 cv
.put(ProviderTableMeta
.OCSHARES_PATH
, share
.getPath());
1450 cv
.put(ProviderTableMeta
.OCSHARES_PERMISSIONS
, share
.getPermissions());
1451 cv
.put(ProviderTableMeta
.OCSHARES_SHARED_DATE
, share
.getSharedDate());
1452 cv
.put(ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
, share
.getExpirationDate());
1453 cv
.put(ProviderTableMeta
.OCSHARES_TOKEN
, share
.getToken());
1455 ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
,
1456 share
.getSharedWithDisplayName()
1458 cv
.put(ProviderTableMeta
.OCSHARES_IS_DIRECTORY
, share
.isFolder() ?
1 : 0);
1459 cv
.put(ProviderTableMeta
.OCSHARES_USER_ID
, share
.getUserId());
1460 cv
.put(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
, share
.getIdRemoteShared());
1461 cv
.put(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
, mAccount
.name
);
1463 // adding a new share resource
1465 ContentProviderOperation
.newInsert(ProviderTableMeta
.CONTENT_URI_SHARE
).
1474 private ArrayList
<ContentProviderOperation
> prepareRemoveSharesInFolder(
1475 OCFile folder
, ArrayList
<ContentProviderOperation
> preparedOperations
) {
1476 if (folder
!= null
) {
1477 String where
= ProviderTableMeta
.OCSHARES_PATH
+ "=?" + " AND "
1478 + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
+ "=?";
1479 String
[] whereArgs
= new String
[]{ "", mAccount
.name
};
1481 Vector
<OCFile
> files
= getFolderContent(folder
, false
);
1483 for (OCFile file
: files
) {
1484 whereArgs
[0] = file
.getRemotePath();
1485 preparedOperations
.add(
1486 ContentProviderOperation
.newDelete(ProviderTableMeta
.CONTENT_URI_SHARE
).
1487 withSelection(where
, whereArgs
).
1492 return preparedOperations
;
1496 private ArrayList
<ContentProviderOperation
> prepareRemoveSharesInFile(
1497 String filePath
, ArrayList
<ContentProviderOperation
> preparedOperations
) {
1499 String where
= ProviderTableMeta
.OCSHARES_PATH
+ "=?" + " AND "
1500 + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
+ "=?";
1501 String
[] whereArgs
= new String
[]{filePath
, mAccount
.name
};
1503 preparedOperations
.add(
1504 ContentProviderOperation
.newDelete(ProviderTableMeta
.CONTENT_URI_SHARE
).
1505 withSelection(where
, whereArgs
).
1509 return preparedOperations
;
1513 public ArrayList
<OCShare
> getSharesWithForAFile(String filePath
, String accountName
){
1515 String where
= ProviderTableMeta
.OCSHARES_PATH
+ "=?" + " AND "
1516 + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
+ "=?"+ "AND"
1517 + " (" + ProviderTableMeta
.OCSHARES_SHARE_TYPE
+ "=? OR "
1518 + ProviderTableMeta
.OCSHARES_SHARE_TYPE
+ "=? ) ";
1519 String
[] whereArgs
= new String
[]{ filePath
, accountName
,
1520 Integer
.toString(ShareType
.USER
.getValue()),
1521 Integer
.toString(ShareType
.GROUP
.getValue()) };
1524 if (getContentResolver() != null
) {
1525 c
= getContentResolver().query(
1526 ProviderTableMeta
.CONTENT_URI_SHARE
,
1527 null
, where
, whereArgs
, null
);
1530 c
= getContentProviderClient().query(
1531 ProviderTableMeta
.CONTENT_URI_SHARE
,
1532 null
, where
, whereArgs
, null
);
1534 } catch (RemoteException e
) {
1535 Log_OC
.e(TAG
, "Could not get list of shares with: " + e
.getMessage());
1539 ArrayList
<OCShare
> shares
= new ArrayList
<OCShare
>();
1540 OCShare share
= null
;
1541 if (c
.moveToFirst()) {
1543 share
= createShareInstance(c
);
1546 } while (c
.moveToNext());
1553 public static void triggerMediaScan(String path
) {
1554 Intent intent
= new Intent(Intent
.ACTION_MEDIA_SCANNER_SCAN_FILE
);
1555 intent
.setData(Uri
.fromFile(new File(path
)));
1556 MainApp
.getAppContext().sendBroadcast(intent
);
1559 public void deleteFileInMediaScan(String path
) {
1561 String mimetypeString
= FileStorageUtils
.getMimeTypeFromName(path
);
1562 ContentResolver contentResolver
= getContentResolver();
1564 if (contentResolver
!= null
) {
1565 if (mimetypeString
.startsWith("image/")) {
1567 contentResolver
.delete(MediaStore
.Images
.Media
.EXTERNAL_CONTENT_URI
,
1568 MediaStore
.Images
.Media
.DATA
+ "=?", new String
[]{path
});
1569 } else if (mimetypeString
.startsWith("audio/")) {
1571 contentResolver
.delete(MediaStore
.Audio
.Media
.EXTERNAL_CONTENT_URI
,
1572 MediaStore
.Audio
.Media
.DATA
+ "=?", new String
[]{path
});
1573 } else if (mimetypeString
.startsWith("video/")) {
1575 contentResolver
.delete(MediaStore
.Video
.Media
.EXTERNAL_CONTENT_URI
,
1576 MediaStore
.Video
.Media
.DATA
+ "=?", new String
[]{path
});
1579 ContentProviderClient contentProviderClient
= getContentProviderClient();
1581 if (mimetypeString
.startsWith("image/")) {
1583 contentProviderClient
.delete(MediaStore
.Images
.Media
.EXTERNAL_CONTENT_URI
,
1584 MediaStore
.Images
.Media
.DATA
+ "=?", new String
[]{path
});
1585 } else if (mimetypeString
.startsWith("audio/")) {
1587 contentProviderClient
.delete(MediaStore
.Audio
.Media
.EXTERNAL_CONTENT_URI
,
1588 MediaStore
.Audio
.Media
.DATA
+ "=?", new String
[]{path
});
1589 } else if (mimetypeString
.startsWith("video/")) {
1591 contentProviderClient
.delete(MediaStore
.Video
.Media
.EXTERNAL_CONTENT_URI
,
1592 MediaStore
.Video
.Media
.DATA
+ "=?", new String
[]{path
});
1594 } catch (RemoteException e
) {
1595 Log_OC
.e(TAG
, "Exception deleting media file in MediaStore " + e
.getMessage());
1601 public void saveConflict(OCFile file
, String etagInConflict
) {
1602 if (!file
.isDown()) {
1603 etagInConflict
= null
;
1605 ContentValues cv
= new ContentValues();
1606 cv
.put(ProviderTableMeta
.FILE_ETAG_IN_CONFLICT
, etagInConflict
);
1608 if (getContentResolver() != null
) {
1609 updated
= getContentResolver().update(
1610 ProviderTableMeta
.CONTENT_URI_FILE
,
1612 ProviderTableMeta
._ID
+ "=?",
1613 new String
[] { String
.valueOf(file
.getFileId())}
1617 updated
= getContentProviderClient().update(
1618 ProviderTableMeta
.CONTENT_URI_FILE
,
1620 ProviderTableMeta
._ID
+ "=?",
1621 new String
[]{String
.valueOf(file
.getFileId())}
1623 } catch (RemoteException e
) {
1624 Log_OC
.e(TAG
, "Failed saving conflict in database " + e
.getMessage());
1628 Log_OC
.d(TAG
, "Number of files updated with CONFLICT: " + updated
);
1631 if (etagInConflict
!= null
) {
1632 /// set conflict in all ancestor folders
1634 long parentId
= file
.getParentId();
1635 Set
<String
> ancestorIds
= new HashSet
<String
>();
1636 while (parentId
!= FileDataStorageManager
.ROOT_PARENT_ID
) {
1637 ancestorIds
.add(Long
.toString(parentId
));
1638 parentId
= getFileById(parentId
).getParentId();
1641 if (ancestorIds
.size() > 0) {
1642 StringBuffer whereBuffer
= new StringBuffer();
1643 whereBuffer
.append(ProviderTableMeta
._ID
).append(" IN (");
1644 for (int i
= 0; i
< ancestorIds
.size() - 1; i
++) {
1645 whereBuffer
.append("?,");
1647 whereBuffer
.append("?");
1648 whereBuffer
.append(")");
1650 if (getContentResolver() != null
) {
1651 updated
= getContentResolver().update(
1652 ProviderTableMeta
.CONTENT_URI_FILE
,
1654 whereBuffer
.toString(),
1655 ancestorIds
.toArray(new String
[]{})
1659 updated
= getContentProviderClient().update(
1660 ProviderTableMeta
.CONTENT_URI_FILE
,
1662 whereBuffer
.toString(),
1663 ancestorIds
.toArray(new String
[]{})
1665 } catch (RemoteException e
) {
1666 Log_OC
.e(TAG
, "Failed saving conflict in database " + e
.getMessage());
1669 } // else file is ROOT folder, no parent to set in conflict
1672 /// update conflict in ancestor folders
1673 // (not directly unset; maybe there are more conflicts below them)
1674 String parentPath
= file
.getRemotePath();
1675 if (parentPath
.endsWith(OCFile
.PATH_SEPARATOR
)) {
1676 parentPath
= parentPath
.substring(0, parentPath
.length() - 1);
1678 parentPath
= parentPath
.substring(0, parentPath
.lastIndexOf(OCFile
.PATH_SEPARATOR
) + 1);
1680 Log_OC
.d(TAG
, "checking parents to remove conflict; STARTING with " + parentPath
);
1681 while (parentPath
.length() > 0) {
1684 ProviderTableMeta
.FILE_ETAG_IN_CONFLICT
+ " IS NOT NULL AND " +
1685 ProviderTableMeta
.FILE_CONTENT_TYPE
+ " != 'DIR' AND " +
1686 ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ " = ? AND " +
1687 ProviderTableMeta
.FILE_PATH
+ " LIKE ?";
1688 Cursor descendentsInConflict
= getContentResolver().query(
1689 ProviderTableMeta
.CONTENT_URI_FILE
,
1690 new String
[]{ProviderTableMeta
._ID
},
1692 new String
[]{mAccount
.name
, parentPath
+ "%"},
1695 if (descendentsInConflict
== null
|| descendentsInConflict
.getCount() == 0) {
1696 Log_OC
.d(TAG
, "NO MORE conflicts in " + parentPath
);
1697 if (getContentResolver() != null
) {
1698 updated
= getContentResolver().update(
1699 ProviderTableMeta
.CONTENT_URI_FILE
,
1701 ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=? AND " +
1702 ProviderTableMeta
.FILE_PATH
+ "=?",
1703 new String
[]{mAccount
.name
, parentPath
}
1707 updated
= getContentProviderClient().update(
1708 ProviderTableMeta
.CONTENT_URI_FILE
,
1710 ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=? AND " +
1711 ProviderTableMeta
.FILE_PATH
+ "=?"
1712 , new String
[]{mAccount
.name
, parentPath
}
1714 } catch (RemoteException e
) {
1715 Log_OC
.e(TAG
, "Failed saving conflict in database " + e
.getMessage());
1720 Log_OC
.d(TAG
, "STILL " + descendentsInConflict
.getCount() + " in " + parentPath
);
1723 if (descendentsInConflict
!= null
) {
1724 descendentsInConflict
.close();
1727 parentPath
= parentPath
.substring(0, parentPath
.length() - 1); // trim last /
1728 parentPath
= parentPath
.substring(0, parentPath
.lastIndexOf(OCFile
.PATH_SEPARATOR
) + 1);
1729 Log_OC
.d(TAG
, "checking parents to remove conflict; NEXT " + parentPath
);