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
;
49 import com
.owncloud
.android
.db
.ProviderMeta
.ProviderTableMeta
;
50 import com
.owncloud
.android
.lib
.common
.utils
.Log_OC
;
51 import com
.owncloud
.android
.lib
.resources
.files
.FileUtils
;
52 import com
.owncloud
.android
.lib
.resources
.shares
.OCShare
;
53 import com
.owncloud
.android
.lib
.resources
.shares
.ShareType
;
54 import com
.owncloud
.android
.utils
.FileStorageUtils
;
56 import java
.io
.FileInputStream
;
57 import java
.io
.FileOutputStream
;
58 import java
.io
.IOException
;
59 import java
.io
.InputStream
;
60 import java
.io
.OutputStream
;
62 public class FileDataStorageManager
{
64 public static final int ROOT_PARENT_ID
= 0;
66 private ContentResolver mContentResolver
;
67 private ContentProviderClient mContentProviderClient
;
68 private Account mAccount
;
70 private static String TAG
= FileDataStorageManager
.class.getSimpleName();
73 public FileDataStorageManager(Account account
, ContentResolver cr
) {
74 mContentProviderClient
= null
;
75 mContentResolver
= cr
;
79 public FileDataStorageManager(Account account
, ContentProviderClient cp
) {
80 mContentProviderClient
= cp
;
81 mContentResolver
= null
;
86 public void setAccount(Account account
) {
90 public Account
getAccount() {
94 public void setContentResolver(ContentResolver cr
) {
95 mContentResolver
= cr
;
98 public ContentResolver
getContentResolver() {
99 return mContentResolver
;
102 public void setContentProviderClient(ContentProviderClient cp
) {
103 mContentProviderClient
= cp
;
106 public ContentProviderClient
getContentProviderClient() {
107 return mContentProviderClient
;
111 public OCFile
getFileByPath(String path
) {
112 Cursor c
= getCursorForValue(ProviderTableMeta
.FILE_PATH
, path
);
114 if (c
.moveToFirst()) {
115 file
= createFileInstance(c
);
118 if (file
== null
&& OCFile
.ROOT_PATH
.equals(path
)) {
119 return createRootDir(); // root should always exist
125 public OCFile
getFileById(long id
) {
126 Cursor c
= getCursorForValue(ProviderTableMeta
._ID
, String
.valueOf(id
));
128 if (c
.moveToFirst()) {
129 file
= createFileInstance(c
);
135 public OCFile
getFileByLocalPath(String path
) {
136 Cursor c
= getCursorForValue(ProviderTableMeta
.FILE_STORAGE_PATH
, path
);
138 if (c
.moveToFirst()) {
139 file
= createFileInstance(c
);
145 public boolean fileExists(long id
) {
146 return fileExists(ProviderTableMeta
._ID
, String
.valueOf(id
));
149 public boolean fileExists(String path
) {
150 return fileExists(ProviderTableMeta
.FILE_PATH
, path
);
154 public Vector
<OCFile
> getFolderContent(OCFile f
/*, boolean onlyOnDevice*/) {
155 if (f
!= null
&& f
.isFolder() && f
.getFileId() != -1) {
156 // TODO Enable when "On Device" is recovered ?
157 return getFolderContent(f
.getFileId()/*, onlyOnDevice*/);
160 return new Vector
<OCFile
>();
165 public Vector
<OCFile
> getFolderImages(OCFile folder
/*, boolean onlyOnDevice*/) {
166 Vector
<OCFile
> ret
= new Vector
<OCFile
>();
167 if (folder
!= null
) {
168 // TODO better implementation, filtering in the access to database instead of here
169 // TODO Enable when "On Device" is recovered ?
170 Vector
<OCFile
> tmp
= getFolderContent(folder
/*, onlyOnDevice*/);
171 OCFile current
= null
;
172 for (int i
=0; i
<tmp
.size(); i
++) {
173 current
= tmp
.get(i
);
174 if (current
.isImage()) {
182 public boolean saveFile(OCFile file
) {
183 boolean overriden
= false
;
184 ContentValues cv
= new ContentValues();
185 cv
.put(ProviderTableMeta
.FILE_MODIFIED
, file
.getModificationTimestamp());
187 ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
,
188 file
.getModificationTimestampAtLastSyncForData()
190 cv
.put(ProviderTableMeta
.FILE_CREATION
, file
.getCreationTimestamp());
191 cv
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, file
.getFileLength());
192 cv
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, file
.getMimetype());
193 cv
.put(ProviderTableMeta
.FILE_NAME
, file
.getFileName());
194 //if (file.getParentId() != DataStorageManager.ROOT_PARENT_ID)
195 cv
.put(ProviderTableMeta
.FILE_PARENT
, file
.getParentId());
196 cv
.put(ProviderTableMeta
.FILE_PATH
, file
.getRemotePath());
197 if (!file
.isFolder())
198 cv
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, file
.getStoragePath());
199 cv
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, mAccount
.name
);
200 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, file
.getLastSyncDateForProperties());
201 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
, file
.getLastSyncDateForData());
202 cv
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, file
.isFavorite() ?
1 : 0);
203 cv
.put(ProviderTableMeta
.FILE_ETAG
, file
.getEtag());
204 cv
.put(ProviderTableMeta
.FILE_SHARE_BY_LINK
, file
.isShareByLink() ?
1 : 0);
205 cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, file
.getPublicLink());
206 cv
.put(ProviderTableMeta
.FILE_PERMISSIONS
, file
.getPermissions());
207 cv
.put(ProviderTableMeta
.FILE_REMOTE_ID
, file
.getRemoteId());
208 cv
.put(ProviderTableMeta
.FILE_UPDATE_THUMBNAIL
, file
.needsUpdateThumbnail());
209 cv
.put(ProviderTableMeta
.FILE_IS_DOWNLOADING
, file
.isDownloading());
210 cv
.put(ProviderTableMeta
.FILE_IN_CONFLICT
, file
.isInConflict());
212 boolean sameRemotePath
= fileExists(file
.getRemotePath());
213 if (sameRemotePath
|| fileExists(file
.getFileId())) { // for renamed files; no more delete and create
215 OCFile oldFile
= null
;
216 if (sameRemotePath
) {
217 oldFile
= getFileByPath(file
.getRemotePath());
218 file
.setFileId(oldFile
.getFileId());
220 oldFile
= getFileById(file
.getFileId());
224 if (getContentResolver() != null
) {
225 getContentResolver().update(ProviderTableMeta
.CONTENT_URI
, cv
,
226 ProviderTableMeta
._ID
+ "=?",
227 new String
[]{String
.valueOf(file
.getFileId())});
230 getContentProviderClient().update(ProviderTableMeta
.CONTENT_URI
,
231 cv
, ProviderTableMeta
._ID
+ "=?",
232 new String
[]{String
.valueOf(file
.getFileId())});
233 } catch (RemoteException e
) {
235 "Fail to insert insert file to database "
240 Uri result_uri
= null
;
241 if (getContentResolver() != null
) {
242 result_uri
= getContentResolver().insert(
243 ProviderTableMeta
.CONTENT_URI_FILE
, cv
);
246 result_uri
= getContentProviderClient().insert(
247 ProviderTableMeta
.CONTENT_URI_FILE
, cv
);
248 } catch (RemoteException e
) {
250 "Fail to insert insert file to database "
254 if (result_uri
!= null
) {
255 long new_id
= Long
.parseLong(result_uri
.getPathSegments()
257 file
.setFileId(new_id
);
261 // if (file.isFolder()) {
262 // updateFolderSize(file.getFileId());
264 // updateFolderSize(file.getParentId());
272 * Inserts or updates the list of files contained in a given folder.
274 * CALLER IS THE RESPONSIBLE FOR GRANTING RIGHT UPDATE OF INFORMATION, NOT THIS METHOD.
275 * HERE ONLY DATA CONSISTENCY SHOULD BE GRANTED
278 * @param updatedFiles
279 * @param filesToRemove
281 public void saveFolder(
282 OCFile folder
, Collection
<OCFile
> updatedFiles
, Collection
<OCFile
> filesToRemove
285 Log_OC
.d(TAG
, "Saving folder " + folder
.getRemotePath() + " with " + updatedFiles
.size()
286 + " children and " + filesToRemove
.size() + " files to remove");
288 ArrayList
<ContentProviderOperation
> operations
=
289 new ArrayList
<ContentProviderOperation
>(updatedFiles
.size());
291 // prepare operations to insert or update files to save in the given folder
292 for (OCFile file
: updatedFiles
) {
293 ContentValues cv
= new ContentValues();
294 cv
.put(ProviderTableMeta
.FILE_MODIFIED
, file
.getModificationTimestamp());
296 ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
,
297 file
.getModificationTimestampAtLastSyncForData()
299 cv
.put(ProviderTableMeta
.FILE_CREATION
, file
.getCreationTimestamp());
300 cv
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, file
.getFileLength());
301 cv
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, file
.getMimetype());
302 cv
.put(ProviderTableMeta
.FILE_NAME
, file
.getFileName());
303 //cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
304 cv
.put(ProviderTableMeta
.FILE_PARENT
, folder
.getFileId());
305 cv
.put(ProviderTableMeta
.FILE_PATH
, file
.getRemotePath());
306 if (!file
.isFolder()) {
307 cv
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, file
.getStoragePath());
309 cv
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, mAccount
.name
);
310 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, file
.getLastSyncDateForProperties());
311 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
, file
.getLastSyncDateForData());
312 cv
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, file
.isFavorite() ?
1 : 0);
313 cv
.put(ProviderTableMeta
.FILE_ETAG
, file
.getEtag());
314 cv
.put(ProviderTableMeta
.FILE_SHARE_BY_LINK
, file
.isShareByLink() ?
1 : 0);
315 cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, file
.getPublicLink());
316 cv
.put(ProviderTableMeta
.FILE_PERMISSIONS
, file
.getPermissions());
317 cv
.put(ProviderTableMeta
.FILE_REMOTE_ID
, file
.getRemoteId());
318 cv
.put(ProviderTableMeta
.FILE_UPDATE_THUMBNAIL
, file
.needsUpdateThumbnail());
319 cv
.put(ProviderTableMeta
.FILE_IS_DOWNLOADING
, file
.isDownloading());
320 cv
.put(ProviderTableMeta
.FILE_IN_CONFLICT
, file
.isInConflict());
322 boolean existsByPath
= fileExists(file
.getRemotePath());
323 if (existsByPath
|| fileExists(file
.getFileId())) {
324 // updating an existing file
325 operations
.add(ContentProviderOperation
.newUpdate(ProviderTableMeta
.CONTENT_URI
).
327 withSelection(ProviderTableMeta
._ID
+ "=?",
328 new String
[]{String
.valueOf(file
.getFileId())})
333 operations
.add(ContentProviderOperation
.newInsert(ProviderTableMeta
.CONTENT_URI
).
334 withValues(cv
).build());
338 // prepare operations to remove files in the given folder
339 String where
= ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=?" + " AND " +
340 ProviderTableMeta
.FILE_PATH
+ "=?";
341 String
[] whereArgs
= null
;
342 for (OCFile file
: filesToRemove
) {
343 if (file
.getParentId() == folder
.getFileId()) {
344 whereArgs
= new String
[]{mAccount
.name
, file
.getRemotePath()};
345 //Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, "" + file.getFileId());
346 if (file
.isFolder()) {
347 operations
.add(ContentProviderOperation
.newDelete(
348 ContentUris
.withAppendedId(
349 ProviderTableMeta
.CONTENT_URI_DIR
, file
.getFileId()
351 ).withSelection(where
, whereArgs
).build());
354 new File(FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, file
));
355 if (localFolder
.exists()) {
356 removeLocalFolder(localFolder
);
359 operations
.add(ContentProviderOperation
.newDelete(
360 ContentUris
.withAppendedId(
361 ProviderTableMeta
.CONTENT_URI_FILE
, file
.getFileId()
363 ).withSelection(where
, whereArgs
).build());
366 String path
= file
.getStoragePath();
367 new File(path
).delete();
368 triggerMediaScan(path
); // notify MediaScanner about removed file
374 // update metadata of folder
375 ContentValues cv
= new ContentValues();
376 cv
.put(ProviderTableMeta
.FILE_MODIFIED
, folder
.getModificationTimestamp());
378 ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
,
379 folder
.getModificationTimestampAtLastSyncForData()
381 cv
.put(ProviderTableMeta
.FILE_CREATION
, folder
.getCreationTimestamp());
382 cv
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, 0);
383 cv
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, folder
.getMimetype());
384 cv
.put(ProviderTableMeta
.FILE_NAME
, folder
.getFileName());
385 cv
.put(ProviderTableMeta
.FILE_PARENT
, folder
.getParentId());
386 cv
.put(ProviderTableMeta
.FILE_PATH
, folder
.getRemotePath());
387 cv
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, mAccount
.name
);
388 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, folder
.getLastSyncDateForProperties());
389 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
, folder
.getLastSyncDateForData());
390 cv
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, folder
.isFavorite() ?
1 : 0);
391 cv
.put(ProviderTableMeta
.FILE_ETAG
, folder
.getEtag());
392 cv
.put(ProviderTableMeta
.FILE_SHARE_BY_LINK
, folder
.isShareByLink() ?
1 : 0);
393 cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, folder
.getPublicLink());
394 cv
.put(ProviderTableMeta
.FILE_PERMISSIONS
, folder
.getPermissions());
395 cv
.put(ProviderTableMeta
.FILE_REMOTE_ID
, folder
.getRemoteId());
397 operations
.add(ContentProviderOperation
.newUpdate(ProviderTableMeta
.CONTENT_URI
).
399 withSelection(ProviderTableMeta
._ID
+ "=?",
400 new String
[]{String
.valueOf(folder
.getFileId())})
403 // apply operations in batch
404 ContentProviderResult
[] results
= null
;
405 Log_OC
.d(TAG
, "Sending " + operations
.size() + " operations to FileContentProvider");
407 if (getContentResolver() != null
) {
408 results
= getContentResolver().applyBatch(MainApp
.getAuthority(), operations
);
411 results
= getContentProviderClient().applyBatch(operations
);
414 } catch (OperationApplicationException e
) {
415 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
417 } catch (RemoteException e
) {
418 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
421 // update new id in file objects for insertions
422 if (results
!= null
) {
424 Iterator
<OCFile
> filesIt
= updatedFiles
.iterator();
426 for (int i
= 0; i
< results
.length
; i
++) {
427 if (filesIt
.hasNext()) {
428 file
= filesIt
.next();
432 if (results
[i
].uri
!= null
) {
433 newId
= Long
.parseLong(results
[i
].uri
.getPathSegments().get(1));
434 //updatedFiles.get(i).setFileId(newId);
436 file
.setFileId(newId
);
442 //updateFolderSize(folder.getFileId());
451 // private void updateFolderSize(long id) {
452 // if (id > FileDataStorageManager.ROOT_PARENT_ID) {
453 // Log_OC.d(TAG, "Updating size of " + id);
454 // if (getContentResolver() != null) {
455 // getContentResolver().update(ProviderTableMeta.CONTENT_URI_DIR,
456 // new ContentValues(),
457 // won't be used, but cannot be null; crashes in KLP
458 // ProviderTableMeta._ID + "=?",
459 // new String[] { String.valueOf(id) });
462 // getContentProviderClient().update(ProviderTableMeta.CONTENT_URI_DIR,
463 // new ContentValues(),
464 // won't be used, but cannot be null; crashes in KLP
465 // ProviderTableMeta._ID + "=?",
466 // new String[] { String.valueOf(id) });
468 // } catch (RemoteException e) {
470 // TAG, "Exception in update of folder size through compatibility patch " + e.getMessage());
474 // Log_OC.e(TAG, "not updating size for folder " + id);
479 public boolean removeFile(OCFile file
, boolean removeDBData
, boolean removeLocalCopy
) {
480 boolean success
= true
;
482 if (file
.isFolder()) {
483 success
= removeFolder(file
, removeDBData
, removeLocalCopy
);
487 //Uri file_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, ""+file.getFileId());
488 Uri file_uri
= ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_FILE
, file
.getFileId());
489 String where
= ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=?" + " AND " + ProviderTableMeta
.FILE_PATH
+ "=?";
490 String
[] whereArgs
= new String
[]{mAccount
.name
, file
.getRemotePath()};
492 if (getContentProviderClient() != null
) {
494 deleted
= getContentProviderClient().delete(file_uri
, where
, whereArgs
);
495 } catch (RemoteException e
) {
499 deleted
= getContentResolver().delete(file_uri
, where
, whereArgs
);
501 success
&= (deleted
> 0);
503 String localPath
= file
.getStoragePath();
504 if (removeLocalCopy
&& file
.isDown() && localPath
!= null
&& success
) {
505 success
= new File(localPath
).delete();
507 deleteFileInMediaScan(localPath
);
509 if (!removeDBData
&& success
) {
510 // maybe unnecessary, but should be checked TODO remove if unnecessary
511 file
.setStoragePath(null
);
513 saveConflict(file
, false
);
522 public boolean removeFolder(OCFile folder
, boolean removeDBData
, boolean removeLocalContent
) {
523 boolean success
= true
;
524 if (folder
!= null
&& folder
.isFolder()) {
525 if (removeDBData
&& folder
.getFileId() != -1) {
526 success
= removeFolderInDb(folder
);
528 if (removeLocalContent
&& success
) {
529 success
= removeLocalFolder(folder
);
535 private boolean removeFolderInDb(OCFile folder
) {
536 Uri folder_uri
= Uri
.withAppendedPath(ProviderTableMeta
.CONTENT_URI_DIR
, "" +
537 folder
.getFileId()); // URI for recursive deletion
538 String where
= ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=?" + " AND " +
539 ProviderTableMeta
.FILE_PATH
+ "=?";
540 String
[] whereArgs
= new String
[]{mAccount
.name
, folder
.getRemotePath()};
542 if (getContentProviderClient() != null
) {
544 deleted
= getContentProviderClient().delete(folder_uri
, where
, whereArgs
);
545 } catch (RemoteException e
) {
549 deleted
= getContentResolver().delete(folder_uri
, where
, whereArgs
);
554 private boolean removeLocalFolder(OCFile folder
) {
555 boolean success
= true
;
556 String localFolderPath
= FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, folder
);
557 File localFolder
= new File(localFolderPath
);
558 if (localFolder
.exists()) {
559 // stage 1: remove the local files already registered in the files database
560 // TODO Enable when "On Device" is recovered ?
561 Vector
<OCFile
> files
= getFolderContent(folder
.getFileId()/*, false*/);
563 for (OCFile file
: files
) {
564 if (file
.isFolder()) {
565 success
&= removeLocalFolder(file
);
568 File localFile
= new File(file
.getStoragePath());
569 success
&= localFile
.delete();
571 // notify MediaScanner about removed file
572 deleteFileInMediaScan(file
.getStoragePath());
573 file
.setStoragePath(null
);
581 // stage 2: remove the folder itself and any local file inside out of sync;
582 // for instance, after clearing the app cache or reinstalling
583 success
&= removeLocalFolder(localFolder
);
588 private boolean removeLocalFolder(File localFolder
) {
589 boolean success
= true
;
590 File
[] localFiles
= localFolder
.listFiles();
591 if (localFiles
!= null
) {
592 for (File localFile
: localFiles
) {
593 if (localFile
.isDirectory()) {
594 success
&= removeLocalFolder(localFile
);
596 String path
= localFile
.getAbsolutePath();
597 success
&= localFile
.delete();
601 success
&= localFolder
.delete();
607 * Updates database and file system for a file or folder that was moved to a different location.
609 * TODO explore better (faster) implementations
610 * TODO throw exceptions up !
612 public void moveLocalFile(OCFile file
, String targetPath
, String targetParentPath
) {
614 if (file
!= null
&& file
.fileExists() && !OCFile
.ROOT_PATH
.equals(file
.getFileName())) {
616 OCFile targetParent
= getFileByPath(targetParentPath
);
617 if (targetParent
== null
) {
618 throw new IllegalStateException("Parent folder of the target path does not exist!!");
621 /// 1. get all the descendants of the moved element in a single QUERY
623 if (getContentProviderClient() != null
) {
625 c
= getContentProviderClient().query(
626 ProviderTableMeta
.CONTENT_URI
,
628 ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=? AND " +
629 ProviderTableMeta
.FILE_PATH
+ " LIKE ? ",
632 file
.getRemotePath() + "%"
634 ProviderTableMeta
.FILE_PATH
+ " ASC "
636 } catch (RemoteException e
) {
637 Log_OC
.e(TAG
, e
.getMessage());
641 c
= getContentResolver().query(
642 ProviderTableMeta
.CONTENT_URI
,
644 ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=? AND " +
645 ProviderTableMeta
.FILE_PATH
+ " LIKE ? ",
648 file
.getRemotePath() + "%"
650 ProviderTableMeta
.FILE_PATH
+ " ASC "
654 /// 2. prepare a batch of update operations to change all the descendants
655 ArrayList
<ContentProviderOperation
> operations
=
656 new ArrayList
<ContentProviderOperation
>(c
.getCount());
657 String defaultSavePath
= FileStorageUtils
.getSavePath(mAccount
.name
);
658 List
<String
> originalPathsToTriggerMediaScan
= new ArrayList
<String
>();
659 List
<String
> newPathsToTriggerMediaScan
= new ArrayList
<String
>();
660 if (c
.moveToFirst()) {
661 int lengthOfOldPath
= file
.getRemotePath().length();
662 int lengthOfOldStoragePath
= defaultSavePath
.length() + lengthOfOldPath
;
664 ContentValues cv
= new ContentValues(); // keep construction in the loop
665 OCFile child
= createFileInstance(c
);
667 ProviderTableMeta
.FILE_PATH
,
668 targetPath
+ child
.getRemotePath().substring(lengthOfOldPath
)
670 if (child
.getStoragePath() != null
&&
671 child
.getStoragePath().startsWith(defaultSavePath
)) {
672 // update link to downloaded content - but local move is not done here!
673 String targetLocalPath
= defaultSavePath
+ targetPath
+
674 child
.getStoragePath().substring(lengthOfOldStoragePath
);
676 cv
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, targetLocalPath
);
678 originalPathsToTriggerMediaScan
.add(child
.getStoragePath());
679 newPathsToTriggerMediaScan
.add(targetLocalPath
);
682 if (child
.getRemotePath().equals(file
.getRemotePath())) {
684 ProviderTableMeta
.FILE_PARENT
,
685 targetParent
.getFileId()
689 ContentProviderOperation
.newUpdate(ProviderTableMeta
.CONTENT_URI
).
692 ProviderTableMeta
._ID
+ "=?",
693 new String
[]{String
.valueOf(child
.getFileId())}
697 } while (c
.moveToNext());
701 /// 3. apply updates in batch
703 if (getContentResolver() != null
) {
704 getContentResolver().applyBatch(MainApp
.getAuthority(), operations
);
707 getContentProviderClient().applyBatch(operations
);
710 } catch (Exception e
) {
711 Log_OC
.e(TAG
, "Fail to update " + file
.getFileId() + " and descendants in database", e
);
714 /// 4. move in local file system
715 String originalLocalPath
= FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, file
);
716 String targetLocalPath
= defaultSavePath
+ targetPath
;
717 File localFile
= new File(originalLocalPath
);
718 boolean renamed
= false
;
719 if (localFile
.exists()) {
720 File targetFile
= new File(targetLocalPath
);
721 File targetFolder
= targetFile
.getParentFile();
722 if (!targetFolder
.exists()) {
723 targetFolder
.mkdirs();
725 renamed
= localFile
.renameTo(targetFile
);
729 Iterator
<String
> it
= originalPathsToTriggerMediaScan
.iterator();
730 while (it
.hasNext()) {
731 // Notify MediaScanner about removed file
732 deleteFileInMediaScan(it
.next());
734 it
= newPathsToTriggerMediaScan
.iterator();
735 while (it
.hasNext()) {
736 // Notify MediaScanner about new file/folder
737 triggerMediaScan(it
.next());
744 public void copyLocalFile(OCFile file
, String targetPath
) {
746 if (file
!= null
&& file
.fileExists() && !OCFile
.ROOT_PATH
.equals(file
.getFileName())) {
747 String localPath
= FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, file
);
748 File localFile
= new File(localPath
);
749 boolean copied
= false
;
750 String defaultSavePath
= FileStorageUtils
.getSavePath(mAccount
.name
);
751 if (localFile
.exists()) {
752 File targetFile
= new File(defaultSavePath
+ targetPath
);
753 File targetFolder
= targetFile
.getParentFile();
754 if (!targetFolder
.exists()) {
755 targetFolder
.mkdirs();
757 copied
= copyFile(localFile
, targetFile
);
759 Log_OC
.d(TAG
, "Local file COPIED : " + copied
);
763 private boolean copyFile(File src
, File target
) {
766 InputStream
in = null
;
767 OutputStream out
= null
;
770 in = new FileInputStream(src
);
771 out
= new FileOutputStream(target
);
772 byte[] buf
= new byte[1024];
774 while ((len
= in.read(buf
)) > 0) {
775 out
.write(buf
, 0, len
);
777 } catch (IOException ex
) {
780 if (in != null
) try {
782 } catch (IOException e
) {
783 e
.printStackTrace(System
.err
);
785 if (out
!= null
) try {
787 } catch (IOException e
) {
788 e
.printStackTrace(System
.err
);
796 private Vector
<OCFile
> getFolderContent(long parentId
/*, boolean onlyOnDevice*/) {
798 Vector
<OCFile
> ret
= new Vector
<OCFile
>();
800 Uri req_uri
= Uri
.withAppendedPath(
801 ProviderTableMeta
.CONTENT_URI_DIR
,
802 String
.valueOf(parentId
));
805 if (getContentProviderClient() != null
) {
807 c
= getContentProviderClient().query(req_uri
, null
,
808 ProviderTableMeta
.FILE_PARENT
+ "=?",
809 new String
[]{String
.valueOf(parentId
)}, null
);
810 } catch (RemoteException e
) {
811 Log_OC
.e(TAG
, e
.getMessage());
815 c
= getContentResolver().query(req_uri
, null
,
816 ProviderTableMeta
.FILE_PARENT
+ "=?",
817 new String
[]{String
.valueOf(parentId
)}, null
);
820 if (c
.moveToFirst()) {
822 OCFile child
= createFileInstance(c
);
823 // TODO Enable when "On Device" is recovered ?
824 // if (child.isFolder() || !onlyOnDevice || onlyOnDevice && child.isDown()){
827 } while (c
.moveToNext());
832 Collections
.sort(ret
);
838 private OCFile
createRootDir() {
839 OCFile file
= new OCFile(OCFile
.ROOT_PATH
);
840 file
.setMimetype("DIR");
841 file
.setParentId(FileDataStorageManager
.ROOT_PARENT_ID
);
846 private boolean fileExists(String cmp_key
, String value
) {
848 if (getContentResolver() != null
) {
849 c
= getContentResolver()
850 .query(ProviderTableMeta
.CONTENT_URI
,
853 + ProviderTableMeta
.FILE_ACCOUNT_OWNER
855 new String
[]{value
, mAccount
.name
}, null
);
858 c
= getContentProviderClient().query(
859 ProviderTableMeta
.CONTENT_URI
,
862 + ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=?",
863 new String
[]{value
, mAccount
.name
}, null
);
864 } catch (RemoteException e
) {
866 "Couldn't determine file existance, assuming non existance: "
871 boolean retval
= c
.moveToFirst();
876 private Cursor
getCursorForValue(String key
, String value
) {
878 if (getContentResolver() != null
) {
879 c
= getContentResolver()
880 .query(ProviderTableMeta
.CONTENT_URI
,
883 + ProviderTableMeta
.FILE_ACCOUNT_OWNER
885 new String
[]{value
, mAccount
.name
}, null
);
888 c
= getContentProviderClient().query(
889 ProviderTableMeta
.CONTENT_URI
,
891 key
+ "=? AND " + ProviderTableMeta
.FILE_ACCOUNT_OWNER
892 + "=?", new String
[]{value
, mAccount
.name
},
894 } catch (RemoteException e
) {
895 Log_OC
.e(TAG
, "Could not get file details: " + e
.getMessage());
903 private OCFile
createFileInstance(Cursor c
) {
906 file
= new OCFile(c
.getString(c
907 .getColumnIndex(ProviderTableMeta
.FILE_PATH
)));
908 file
.setFileId(c
.getLong(c
.getColumnIndex(ProviderTableMeta
._ID
)));
909 file
.setParentId(c
.getLong(c
910 .getColumnIndex(ProviderTableMeta
.FILE_PARENT
)));
911 file
.setMimetype(c
.getString(c
912 .getColumnIndex(ProviderTableMeta
.FILE_CONTENT_TYPE
)));
913 if (!file
.isFolder()) {
914 file
.setStoragePath(c
.getString(c
915 .getColumnIndex(ProviderTableMeta
.FILE_STORAGE_PATH
)));
916 if (file
.getStoragePath() == null
) {
917 // try to find existing file and bind it with current account;
918 // with the current update of SynchronizeFolderOperation, this won't be
919 // necessary anymore after a full synchronization of the account
920 File f
= new File(FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, file
));
922 file
.setStoragePath(f
.getAbsolutePath());
923 file
.setLastSyncDateForData(f
.lastModified());
927 file
.setFileLength(c
.getLong(c
928 .getColumnIndex(ProviderTableMeta
.FILE_CONTENT_LENGTH
)));
929 file
.setCreationTimestamp(c
.getLong(c
930 .getColumnIndex(ProviderTableMeta
.FILE_CREATION
)));
931 file
.setModificationTimestamp(c
.getLong(c
932 .getColumnIndex(ProviderTableMeta
.FILE_MODIFIED
)));
933 file
.setModificationTimestampAtLastSyncForData(c
.getLong(c
934 .getColumnIndex(ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
)));
935 file
.setLastSyncDateForProperties(c
.getLong(c
936 .getColumnIndex(ProviderTableMeta
.FILE_LAST_SYNC_DATE
)));
937 file
.setLastSyncDateForData(c
.getLong(c
.
938 getColumnIndex(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
)));
939 file
.setFavorite(c
.getInt(
940 c
.getColumnIndex(ProviderTableMeta
.FILE_KEEP_IN_SYNC
)) == 1 ? true
: false
);
941 file
.setEtag(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_ETAG
)));
942 file
.setShareByLink(c
.getInt(
943 c
.getColumnIndex(ProviderTableMeta
.FILE_SHARE_BY_LINK
)) == 1 ? true
: false
);
944 file
.setPublicLink(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_PUBLIC_LINK
)));
945 file
.setPermissions(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_PERMISSIONS
)));
946 file
.setRemoteId(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_REMOTE_ID
)));
947 file
.setNeedsUpdateThumbnail(c
.getInt(
948 c
.getColumnIndex(ProviderTableMeta
.FILE_UPDATE_THUMBNAIL
)) == 1 ? true
: false
);
949 file
.setDownloading(c
.getInt(
950 c
.getColumnIndex(ProviderTableMeta
.FILE_IS_DOWNLOADING
)) == 1 ? true
: false
);
951 file
.setInConflict(c
.getInt(
952 c
.getColumnIndex(ProviderTableMeta
.FILE_IN_CONFLICT
)) == 1 ? true
: false
);
959 * Returns if the file/folder is shared by link or not
961 * @param path Path of the file/folder
964 public boolean isShareByLink(String path
) {
965 Cursor c
= getCursorForValue(ProviderTableMeta
.FILE_STORAGE_PATH
, path
);
967 if (c
.moveToFirst()) {
968 file
= createFileInstance(c
);
971 return file
.isShareByLink();
975 * Returns the public link of the file/folder
977 * @param path Path of the file/folder
980 public String
getPublicLink(String path
) {
981 Cursor c
= getCursorForValue(ProviderTableMeta
.FILE_STORAGE_PATH
, path
);
983 if (c
.moveToFirst()) {
984 file
= createFileInstance(c
);
987 return file
.getPublicLink();
991 // Methods for Shares
992 public boolean saveShare(OCShare share
) {
993 boolean overriden
= false
;
994 ContentValues cv
= new ContentValues();
995 cv
.put(ProviderTableMeta
.OCSHARES_FILE_SOURCE
, share
.getFileSource());
996 cv
.put(ProviderTableMeta
.OCSHARES_ITEM_SOURCE
, share
.getItemSource());
997 cv
.put(ProviderTableMeta
.OCSHARES_SHARE_TYPE
, share
.getShareType().getValue());
998 cv
.put(ProviderTableMeta
.OCSHARES_SHARE_WITH
, share
.getShareWith());
999 cv
.put(ProviderTableMeta
.OCSHARES_PATH
, share
.getPath());
1000 cv
.put(ProviderTableMeta
.OCSHARES_PERMISSIONS
, share
.getPermissions());
1001 cv
.put(ProviderTableMeta
.OCSHARES_SHARED_DATE
, share
.getSharedDate());
1002 cv
.put(ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
, share
.getExpirationDate());
1003 cv
.put(ProviderTableMeta
.OCSHARES_TOKEN
, share
.getToken());
1005 ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
,
1006 share
.getSharedWithDisplayName()
1008 cv
.put(ProviderTableMeta
.OCSHARES_IS_DIRECTORY
, share
.isFolder() ?
1 : 0);
1009 cv
.put(ProviderTableMeta
.OCSHARES_USER_ID
, share
.getUserId());
1010 cv
.put(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
, share
.getIdRemoteShared());
1011 cv
.put(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
, mAccount
.name
);
1013 if (shareExists(share
.getIdRemoteShared())) { // for renamed files; no more delete and create
1015 if (getContentResolver() != null
) {
1016 getContentResolver().update(ProviderTableMeta
.CONTENT_URI_SHARE
, cv
,
1017 ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
+ "=?",
1018 new String
[]{String
.valueOf(share
.getIdRemoteShared())});
1021 getContentProviderClient().update(ProviderTableMeta
.CONTENT_URI_SHARE
,
1022 cv
, ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
+ "=?",
1023 new String
[]{String
.valueOf(share
.getIdRemoteShared())});
1024 } catch (RemoteException e
) {
1026 "Fail to insert insert file to database "
1031 Uri result_uri
= null
;
1032 if (getContentResolver() != null
) {
1033 result_uri
= getContentResolver().insert(
1034 ProviderTableMeta
.CONTENT_URI_SHARE
, cv
);
1037 result_uri
= getContentProviderClient().insert(
1038 ProviderTableMeta
.CONTENT_URI_SHARE
, cv
);
1039 } catch (RemoteException e
) {
1041 "Fail to insert insert file to database "
1045 if (result_uri
!= null
) {
1046 long new_id
= Long
.parseLong(result_uri
.getPathSegments()
1048 share
.setId(new_id
);
1056 public OCShare
getFirstShareByPathAndType(String path
, ShareType type
) {
1058 if (getContentResolver() != null
) {
1059 c
= getContentResolver().query(
1060 ProviderTableMeta
.CONTENT_URI_SHARE
,
1062 ProviderTableMeta
.OCSHARES_PATH
+ "=? AND "
1063 + ProviderTableMeta
.OCSHARES_SHARE_TYPE
+ "=? AND "
1064 + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
+ "=?",
1065 new String
[]{path
, Integer
.toString(type
.getValue()), mAccount
.name
},
1069 c
= getContentProviderClient().query(
1070 ProviderTableMeta
.CONTENT_URI_SHARE
,
1072 ProviderTableMeta
.OCSHARES_PATH
+ "=? AND "
1073 + ProviderTableMeta
.OCSHARES_SHARE_TYPE
+ "=? AND "
1074 + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
+ "=?",
1075 new String
[]{path
, Integer
.toString(type
.getValue()), mAccount
.name
},
1078 } catch (RemoteException e
) {
1079 Log_OC
.e(TAG
, "Could not get file details: " + e
.getMessage());
1083 OCShare share
= null
;
1084 if (c
.moveToFirst()) {
1085 share
= createShareInstance(c
);
1091 private OCShare
createShareInstance(Cursor c
) {
1092 OCShare share
= null
;
1094 share
= new OCShare(c
.getString(c
1095 .getColumnIndex(ProviderTableMeta
.OCSHARES_PATH
)));
1096 share
.setId(c
.getLong(c
.getColumnIndex(ProviderTableMeta
._ID
)));
1097 share
.setFileSource(c
.getLong(c
1098 .getColumnIndex(ProviderTableMeta
.OCSHARES_ITEM_SOURCE
)));
1099 share
.setShareType(ShareType
.fromValue(c
.getInt(c
1100 .getColumnIndex(ProviderTableMeta
.OCSHARES_SHARE_TYPE
))));
1101 share
.setPermissions(c
.getInt(c
1102 .getColumnIndex(ProviderTableMeta
.OCSHARES_PERMISSIONS
)));
1103 share
.setSharedDate(c
.getLong(c
1104 .getColumnIndex(ProviderTableMeta
.OCSHARES_SHARED_DATE
)));
1105 share
.setExpirationDate(c
.getLong(c
1106 .getColumnIndex(ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
)));
1107 share
.setToken(c
.getString(c
1108 .getColumnIndex(ProviderTableMeta
.OCSHARES_TOKEN
)));
1109 share
.setSharedWithDisplayName(c
.getString(c
1110 .getColumnIndex(ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
)));
1111 share
.setIsFolder(c
.getInt(
1112 c
.getColumnIndex(ProviderTableMeta
.OCSHARES_IS_DIRECTORY
)) == 1);
1113 share
.setUserId(c
.getLong(c
.getColumnIndex(ProviderTableMeta
.OCSHARES_USER_ID
)));
1114 share
.setIdRemoteShared(c
.getLong(c
.getColumnIndex(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
)));
1119 private boolean shareExists(String cmp_key
, String value
) {
1121 if (getContentResolver() != null
) {
1122 c
= getContentResolver()
1123 .query(ProviderTableMeta
.CONTENT_URI_SHARE
,
1126 + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
1128 new String
[]{value
, mAccount
.name
}, null
);
1131 c
= getContentProviderClient().query(
1132 ProviderTableMeta
.CONTENT_URI_SHARE
,
1135 + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
+ "=?",
1136 new String
[]{value
, mAccount
.name
}, null
);
1137 } catch (RemoteException e
) {
1139 "Couldn't determine file existance, assuming non existance: "
1144 boolean retval
= c
.moveToFirst();
1149 private boolean shareExists(long remoteId
) {
1150 return shareExists(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
, String
.valueOf(remoteId
));
1153 private void cleanSharedFiles() {
1154 ContentValues cv
= new ContentValues();
1155 cv
.put(ProviderTableMeta
.FILE_SHARE_BY_LINK
, false
);
1156 cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, "");
1157 String where
= ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=?";
1158 String
[] whereArgs
= new String
[]{mAccount
.name
};
1160 if (getContentResolver() != null
) {
1161 getContentResolver().update(ProviderTableMeta
.CONTENT_URI
, cv
, where
, whereArgs
);
1165 getContentProviderClient().update(ProviderTableMeta
.CONTENT_URI
, cv
, where
, whereArgs
);
1166 } catch (RemoteException e
) {
1167 Log_OC
.e(TAG
, "Exception in cleanSharedFiles" + e
.getMessage());
1172 private void cleanSharedFilesInFolder(OCFile folder
) {
1173 ContentValues cv
= new ContentValues();
1174 cv
.put(ProviderTableMeta
.FILE_SHARE_BY_LINK
, false
);
1175 cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, "");
1176 String where
= ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=? AND " +
1177 ProviderTableMeta
.FILE_PARENT
+ "=?";
1178 String
[] whereArgs
= new String
[] { mAccount
.name
, String
.valueOf(folder
.getFileId()) };
1180 if (getContentResolver() != null
) {
1181 getContentResolver().update(ProviderTableMeta
.CONTENT_URI
, cv
, where
, whereArgs
);
1185 getContentProviderClient().update(ProviderTableMeta
.CONTENT_URI
, cv
, where
, whereArgs
);
1186 } catch (RemoteException e
) {
1187 Log_OC
.e(TAG
, "Exception in cleanSharedFilesInFolder " + e
.getMessage());
1192 private void cleanShares() {
1193 String where
= ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
+ "=?";
1194 String
[] whereArgs
= new String
[]{mAccount
.name
};
1196 if (getContentResolver() != null
) {
1197 getContentResolver().delete(ProviderTableMeta
.CONTENT_URI_SHARE
, where
, whereArgs
);
1201 getContentProviderClient().delete(ProviderTableMeta
.CONTENT_URI_SHARE
, where
, whereArgs
);
1202 } catch (RemoteException e
) {
1203 Log_OC
.e(TAG
, "Exception in cleanShares" + e
.getMessage());
1208 public void saveShares(Collection
<OCShare
> shares
) {
1210 if (shares
!= null
) {
1211 ArrayList
<ContentProviderOperation
> operations
=
1212 new ArrayList
<ContentProviderOperation
>(shares
.size());
1214 // prepare operations to insert or update files to save in the given folder
1215 for (OCShare share
: shares
) {
1216 ContentValues cv
= new ContentValues();
1217 cv
.put(ProviderTableMeta
.OCSHARES_FILE_SOURCE
, share
.getFileSource());
1218 cv
.put(ProviderTableMeta
.OCSHARES_ITEM_SOURCE
, share
.getItemSource());
1219 cv
.put(ProviderTableMeta
.OCSHARES_SHARE_TYPE
, share
.getShareType().getValue());
1220 cv
.put(ProviderTableMeta
.OCSHARES_SHARE_WITH
, share
.getShareWith());
1221 cv
.put(ProviderTableMeta
.OCSHARES_PATH
, share
.getPath());
1222 cv
.put(ProviderTableMeta
.OCSHARES_PERMISSIONS
, share
.getPermissions());
1223 cv
.put(ProviderTableMeta
.OCSHARES_SHARED_DATE
, share
.getSharedDate());
1224 cv
.put(ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
, share
.getExpirationDate());
1225 cv
.put(ProviderTableMeta
.OCSHARES_TOKEN
, share
.getToken());
1227 ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
,
1228 share
.getSharedWithDisplayName()
1230 cv
.put(ProviderTableMeta
.OCSHARES_IS_DIRECTORY
, share
.isFolder() ?
1 : 0);
1231 cv
.put(ProviderTableMeta
.OCSHARES_USER_ID
, share
.getUserId());
1232 cv
.put(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
, share
.getIdRemoteShared());
1233 cv
.put(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
, mAccount
.name
);
1235 if (shareExists(share
.getIdRemoteShared())) {
1236 // updating an existing file
1238 ContentProviderOperation
.newUpdate(ProviderTableMeta
.CONTENT_URI_SHARE
).
1240 withSelection(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
+ "=?",
1241 new String
[]{String
.valueOf(share
.getIdRemoteShared())})
1244 // adding a new file
1246 ContentProviderOperation
.newInsert(ProviderTableMeta
.CONTENT_URI_SHARE
).
1253 // apply operations in batch
1254 if (operations
.size() > 0) {
1255 @SuppressWarnings("unused")
1256 ContentProviderResult
[] results
= null
;
1257 Log_OC
.d(TAG
, "Sending " + operations
.size() +
1258 " operations to FileContentProvider");
1260 if (getContentResolver() != null
) {
1261 results
= getContentResolver().applyBatch(MainApp
.getAuthority(), operations
);
1263 results
= getContentProviderClient().applyBatch(operations
);
1266 } catch (OperationApplicationException e
) {
1267 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
1269 } catch (RemoteException e
) {
1270 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
1277 public void updateSharedFiles(Collection
<OCFile
> sharedFiles
) {
1280 if (sharedFiles
!= null
) {
1281 ArrayList
<ContentProviderOperation
> operations
=
1282 new ArrayList
<ContentProviderOperation
>(sharedFiles
.size());
1284 // prepare operations to insert or update files to save in the given folder
1285 for (OCFile file
: sharedFiles
) {
1286 ContentValues cv
= new ContentValues();
1287 cv
.put(ProviderTableMeta
.FILE_MODIFIED
, file
.getModificationTimestamp());
1289 ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
,
1290 file
.getModificationTimestampAtLastSyncForData()
1292 cv
.put(ProviderTableMeta
.FILE_CREATION
, file
.getCreationTimestamp());
1293 cv
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, file
.getFileLength());
1294 cv
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, file
.getMimetype());
1295 cv
.put(ProviderTableMeta
.FILE_NAME
, file
.getFileName());
1296 cv
.put(ProviderTableMeta
.FILE_PARENT
, file
.getParentId());
1297 cv
.put(ProviderTableMeta
.FILE_PATH
, file
.getRemotePath());
1298 if (!file
.isFolder()) {
1299 cv
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, file
.getStoragePath());
1301 cv
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, mAccount
.name
);
1302 cv
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, file
.getLastSyncDateForProperties());
1304 ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
,
1305 file
.getLastSyncDateForData()
1307 cv
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, file
.isFavorite() ?
1 : 0);
1308 cv
.put(ProviderTableMeta
.FILE_ETAG
, file
.getEtag());
1309 cv
.put(ProviderTableMeta
.FILE_SHARE_BY_LINK
, file
.isShareByLink() ?
1 : 0);
1310 cv
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, file
.getPublicLink());
1311 cv
.put(ProviderTableMeta
.FILE_PERMISSIONS
, file
.getPermissions());
1312 cv
.put(ProviderTableMeta
.FILE_REMOTE_ID
, file
.getRemoteId());
1314 ProviderTableMeta
.FILE_UPDATE_THUMBNAIL
,
1315 file
.needsUpdateThumbnail() ?
1 : 0
1318 ProviderTableMeta
.FILE_IS_DOWNLOADING
,
1319 file
.isDownloading() ?
1 : 0
1322 ProviderTableMeta
.FILE_IN_CONFLICT
,
1323 file
.isInConflict() ?
1 : 0
1326 boolean existsByPath
= fileExists(file
.getRemotePath());
1327 if (existsByPath
|| fileExists(file
.getFileId())) {
1328 // updating an existing file
1330 ContentProviderOperation
.newUpdate(ProviderTableMeta
.CONTENT_URI
).
1332 withSelection(ProviderTableMeta
._ID
+ "=?",
1333 new String
[]{String
.valueOf(file
.getFileId())})
1337 // adding a new file
1339 ContentProviderOperation
.newInsert(ProviderTableMeta
.CONTENT_URI
).
1346 // apply operations in batch
1347 if (operations
.size() > 0) {
1348 @SuppressWarnings("unused")
1349 ContentProviderResult
[] results
= null
;
1350 Log_OC
.d(TAG
, "Sending " + operations
.size() +
1351 " operations to FileContentProvider");
1353 if (getContentResolver() != null
) {
1354 results
= getContentResolver().applyBatch(MainApp
.getAuthority(), operations
);
1356 results
= getContentProviderClient().applyBatch(operations
);
1359 } catch (OperationApplicationException e
) {
1360 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
1362 } catch (RemoteException e
) {
1363 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
1370 public void removeShare(OCShare share
) {
1371 Uri share_uri
= ProviderTableMeta
.CONTENT_URI_SHARE
;
1372 String where
= ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
+ "=?" + " AND " +
1373 ProviderTableMeta
.FILE_PATH
+ "=?";
1374 String
[] whereArgs
= new String
[]{mAccount
.name
, share
.getPath()};
1375 if (getContentProviderClient() != null
) {
1377 getContentProviderClient().delete(share_uri
, where
, whereArgs
);
1378 } catch (RemoteException e
) {
1379 e
.printStackTrace();
1382 getContentResolver().delete(share_uri
, where
, whereArgs
);
1386 public void saveSharesDB(ArrayList
<OCShare
> shares
) {
1389 ArrayList
<OCFile
> sharedFiles
= new ArrayList
<OCFile
>();
1391 for (OCShare share
: shares
) {
1393 String path
= share
.getPath();
1394 if (share
.isFolder()) {
1395 path
= path
+ FileUtils
.PATH_SEPARATOR
;
1398 // Update OCFile with data from share: ShareByLink and publicLink
1399 OCFile file
= getFileByPath(path
);
1401 if (share
.getShareType().equals(ShareType
.PUBLIC_LINK
)) {
1402 file
.setShareByLink(true
);
1403 sharedFiles
.add(file
);
1408 updateSharedFiles(sharedFiles
);
1412 public void saveSharesInFolder(ArrayList
<OCShare
> shares
, OCFile folder
) {
1413 cleanSharedFilesInFolder(folder
);
1414 ArrayList
<ContentProviderOperation
> operations
= new ArrayList
<ContentProviderOperation
>();
1415 operations
= prepareRemoveSharesInFolder(folder
, operations
);
1417 if (shares
!= null
) {
1418 // prepare operations to insert or update files to save in the given folder
1419 for (OCShare share
: shares
) {
1420 ContentValues cv
= new ContentValues();
1421 cv
.put(ProviderTableMeta
.OCSHARES_FILE_SOURCE
, share
.getFileSource());
1422 cv
.put(ProviderTableMeta
.OCSHARES_ITEM_SOURCE
, share
.getItemSource());
1423 cv
.put(ProviderTableMeta
.OCSHARES_SHARE_TYPE
, share
.getShareType().getValue());
1424 cv
.put(ProviderTableMeta
.OCSHARES_SHARE_WITH
, share
.getShareWith());
1425 cv
.put(ProviderTableMeta
.OCSHARES_PATH
, share
.getPath());
1426 cv
.put(ProviderTableMeta
.OCSHARES_PERMISSIONS
, share
.getPermissions());
1427 cv
.put(ProviderTableMeta
.OCSHARES_SHARED_DATE
, share
.getSharedDate());
1428 cv
.put(ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
, share
.getExpirationDate());
1429 cv
.put(ProviderTableMeta
.OCSHARES_TOKEN
, share
.getToken());
1431 ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
,
1432 share
.getSharedWithDisplayName()
1434 cv
.put(ProviderTableMeta
.OCSHARES_IS_DIRECTORY
, share
.isFolder() ?
1 : 0);
1435 cv
.put(ProviderTableMeta
.OCSHARES_USER_ID
, share
.getUserId());
1436 cv
.put(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
, share
.getIdRemoteShared());
1437 cv
.put(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
, mAccount
.name
);
1440 if (shareExists(share.getIdRemoteShared())) {
1441 // updating an existing share resource
1443 ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI_SHARE).
1445 withSelection( ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
1446 new String[] { String.valueOf(share.getIdRemoteShared()) })
1451 // adding a new share resource
1453 ContentProviderOperation
.newInsert(ProviderTableMeta
.CONTENT_URI_SHARE
).
1461 // apply operations in batch
1462 if (operations
.size() > 0) {
1463 @SuppressWarnings("unused")
1464 ContentProviderResult
[] results
= null
;
1465 Log_OC
.d(TAG
, "Sending " + operations
.size() + " operations to FileContentProvider");
1467 if (getContentResolver() != null
) {
1468 results
= getContentResolver().applyBatch(MainApp
.getAuthority(), operations
);
1471 results
= getContentProviderClient().applyBatch(operations
);
1474 } catch (OperationApplicationException e
) {
1475 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
1477 } catch (RemoteException e
) {
1478 Log_OC
.e(TAG
, "Exception in batch of operations " + e
.getMessage());
1485 private ArrayList
<ContentProviderOperation
> prepareRemoveSharesInFolder(
1486 OCFile folder
, ArrayList
<ContentProviderOperation
> preparedOperations
) {
1487 if (folder
!= null
) {
1488 String where
= ProviderTableMeta
.OCSHARES_PATH
+ "=?" + " AND "
1489 + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
+ "=?";
1490 String
[] whereArgs
= new String
[]{ "", mAccount
.name
};
1492 // TODO Enable when "On Device" is recovered ?
1493 Vector
<OCFile
> files
= getFolderContent(folder
/*, false*/);
1495 for (OCFile file
: files
) {
1496 whereArgs
[0] = file
.getRemotePath();
1497 preparedOperations
.add(
1498 ContentProviderOperation
.newDelete(ProviderTableMeta
.CONTENT_URI_SHARE
).
1499 withSelection(where
, whereArgs
).
1504 return preparedOperations
;
1507 if (operations.size() > 0) {
1509 if (getContentResolver() != null) {
1510 getContentResolver().applyBatch(MainApp.getAuthority(), operations);
1513 getContentProviderClient().applyBatch(operations);
1516 } catch (OperationApplicationException e) {
1517 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1519 } catch (RemoteException e) {
1520 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1526 if (getContentResolver() != null) {
1528 getContentResolver().delete(ProviderTableMeta.CONTENT_URI_SHARE,
1533 getContentProviderClient().delete( ProviderTableMeta.CONTENT_URI_SHARE,
1537 } catch (RemoteException e) {
1538 Log_OC.e(TAG, "Exception deleting shares in a folder " + e.getMessage());
1545 public void triggerMediaScan(String path
) {
1546 Intent intent
= new Intent(Intent
.ACTION_MEDIA_SCANNER_SCAN_FILE
);
1547 intent
.setData(Uri
.fromFile(new File(path
)));
1548 MainApp
.getAppContext().sendBroadcast(intent
);
1551 public void deleteFileInMediaScan(String path
) {
1553 String mimetypeString
= FileStorageUtils
.getMimeTypeFromName(path
);
1554 ContentResolver contentResolver
= getContentResolver();
1556 if (contentResolver
!= null
) {
1557 if (mimetypeString
.startsWith("image/")) {
1559 contentResolver
.delete(MediaStore
.Images
.Media
.EXTERNAL_CONTENT_URI
,
1560 MediaStore
.Images
.Media
.DATA
+ "=?", new String
[]{path
});
1561 } else if (mimetypeString
.startsWith("audio/")) {
1563 contentResolver
.delete(MediaStore
.Audio
.Media
.EXTERNAL_CONTENT_URI
,
1564 MediaStore
.Audio
.Media
.DATA
+ "=?", new String
[]{path
});
1565 } else if (mimetypeString
.startsWith("video/")) {
1567 contentResolver
.delete(MediaStore
.Video
.Media
.EXTERNAL_CONTENT_URI
,
1568 MediaStore
.Video
.Media
.DATA
+ "=?", new String
[]{path
});
1571 ContentProviderClient contentProviderClient
= getContentProviderClient();
1573 if (mimetypeString
.startsWith("image/")) {
1575 contentProviderClient
.delete(MediaStore
.Images
.Media
.EXTERNAL_CONTENT_URI
,
1576 MediaStore
.Images
.Media
.DATA
+ "=?", new String
[]{path
});
1577 } else if (mimetypeString
.startsWith("audio/")) {
1579 contentProviderClient
.delete(MediaStore
.Audio
.Media
.EXTERNAL_CONTENT_URI
,
1580 MediaStore
.Audio
.Media
.DATA
+ "=?", new String
[]{path
});
1581 } else if (mimetypeString
.startsWith("video/")) {
1583 contentProviderClient
.delete(MediaStore
.Video
.Media
.EXTERNAL_CONTENT_URI
,
1584 MediaStore
.Video
.Media
.DATA
+ "=?", new String
[]{path
});
1586 } catch (RemoteException e
) {
1587 Log_OC
.e(TAG
, "Exception deleting media file in MediaStore " + e
.getMessage());
1593 public void saveConflict(OCFile file
, boolean inConflict
) {
1594 if (!file
.isDown()) {
1597 ContentValues cv
= new ContentValues();
1598 cv
.put(ProviderTableMeta
.FILE_IN_CONFLICT
, inConflict ?
1 : 0);
1600 if (getContentResolver() != null
) {
1601 updated
= getContentResolver().update(
1602 ProviderTableMeta
.CONTENT_URI_FILE
,
1604 ProviderTableMeta
._ID
+ "=?",
1605 new String
[] { String
.valueOf(file
.getFileId())}
1609 updated
= getContentProviderClient().update(
1610 ProviderTableMeta
.CONTENT_URI_FILE
,
1612 ProviderTableMeta
._ID
+ "=?",
1613 new String
[]{String
.valueOf(file
.getFileId())}
1615 } catch (RemoteException e
) {
1616 Log_OC
.e(TAG
, "Failed saving conflict in database " + e
.getMessage());
1620 Log_OC
.d(TAG
, "Number of files updated with CONFLICT: " + updated
);
1624 /// set conflict in all ancestor folders
1626 long parentId
= file
.getParentId();
1627 Set
<String
> ancestorIds
= new HashSet
<String
>();
1628 while (parentId
!= FileDataStorageManager
.ROOT_PARENT_ID
) {
1629 ancestorIds
.add(Long
.toString(parentId
));
1630 parentId
= getFileById(parentId
).getParentId();
1633 if (ancestorIds
.size() > 0) {
1634 StringBuffer whereBuffer
= new StringBuffer();
1635 whereBuffer
.append(ProviderTableMeta
._ID
).append(" IN (");
1636 for (int i
= 0; i
< ancestorIds
.size() - 1; i
++) {
1637 whereBuffer
.append("?,");
1639 whereBuffer
.append("?");
1640 whereBuffer
.append(")");
1642 if (getContentResolver() != null
) {
1643 updated
= getContentResolver().update(
1644 ProviderTableMeta
.CONTENT_URI_FILE
,
1646 whereBuffer
.toString(),
1647 ancestorIds
.toArray(new String
[]{})
1651 updated
= getContentProviderClient().update(
1652 ProviderTableMeta
.CONTENT_URI_FILE
,
1654 whereBuffer
.toString(),
1655 ancestorIds
.toArray(new String
[]{})
1657 } catch (RemoteException e
) {
1658 Log_OC
.e(TAG
, "Failed saving conflict in database " + e
.getMessage());
1661 } // else file is ROOT folder, no parent to set in conflict
1664 /// update conflict in ancestor folders
1665 // (not directly unset; maybe there are more conflicts below them)
1666 String parentPath
= file
.getRemotePath();
1667 if (parentPath
.endsWith(OCFile
.PATH_SEPARATOR
)) {
1668 parentPath
= parentPath
.substring(0, parentPath
.length() - 1);
1670 parentPath
= parentPath
.substring(0, parentPath
.lastIndexOf(OCFile
.PATH_SEPARATOR
) + 1);
1672 Log_OC
.d(TAG
, "checking parents to remove conflict; STARTING with " + parentPath
);
1673 while (parentPath
.length() > 0) {
1676 ProviderTableMeta
.FILE_IN_CONFLICT
+ " = 1 AND " +
1677 ProviderTableMeta
.FILE_CONTENT_TYPE
+ " != 'DIR' AND " +
1678 ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ " = ? AND " +
1679 ProviderTableMeta
.FILE_PATH
+ " LIKE ?";
1680 Cursor descendentsInConflict
= getContentResolver().query(
1681 ProviderTableMeta
.CONTENT_URI_FILE
,
1682 new String
[]{ProviderTableMeta
._ID
},
1684 new String
[]{mAccount
.name
, parentPath
+ "%"},
1687 if (descendentsInConflict
== null
|| descendentsInConflict
.getCount() == 0) {
1688 Log_OC
.d(TAG
, "NO MORE conflicts in " + parentPath
);
1689 if (getContentResolver() != null
) {
1690 updated
= getContentResolver().update(
1691 ProviderTableMeta
.CONTENT_URI_FILE
,
1693 ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=? AND " + ProviderTableMeta
.FILE_PATH
+ "=?",
1694 new String
[]{mAccount
.name
, parentPath
}
1698 updated
= getContentProviderClient().update(
1699 ProviderTableMeta
.CONTENT_URI_FILE
,
1701 ProviderTableMeta
.FILE_ACCOUNT_OWNER
+ "=? AND " + ProviderTableMeta
.FILE_PATH
+ "=?"
1702 , new String
[]{mAccount
.name
, parentPath
}
1704 } catch (RemoteException e
) {
1705 Log_OC
.e(TAG
, "Failed saving conflict in database " + e
.getMessage());
1710 Log_OC
.d(TAG
, "STILL " + descendentsInConflict
.getCount() + " in " + parentPath
);
1713 if (descendentsInConflict
!= null
) {
1714 descendentsInConflict
.close();
1717 parentPath
= parentPath
.substring(0, parentPath
.length() - 1); // trim last /
1718 parentPath
= parentPath
.substring(0, parentPath
.lastIndexOf(OCFile
.PATH_SEPARATOR
) + 1);
1719 Log_OC
.d(TAG
, "checking parents to remove conflict; NEXT " + parentPath
);