Merge remote-tracking branch 'remotes/upstream/master' into resizedImagesMaster
[pub/Android/ownCloud.git] / src / com / owncloud / android / datamodel / FileDataStorageManager.java
1 /**
2 * ownCloud Android client application
3 *
4 * Copyright (C) 2012 Bartek Przybylski
5 * Copyright (C) 2015 ownCloud Inc.
6 *
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.
10 *
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.
15 *
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/>.
18 *
19 */
20
21 package com.owncloud.android.datamodel;
22
23 import java.io.File;
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;
30 import java.util.Set;
31 import java.util.Vector;
32
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;
46
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;
54
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;
60
61 public class FileDataStorageManager {
62
63 public static final int ROOT_PARENT_ID = 0;
64
65 private ContentResolver mContentResolver;
66 private ContentProviderClient mContentProviderClient;
67 private Account mAccount;
68
69 private static String TAG = FileDataStorageManager.class.getSimpleName();
70
71
72 public FileDataStorageManager(Account account, ContentResolver cr) {
73 mContentProviderClient = null;
74 mContentResolver = cr;
75 mAccount = account;
76 }
77
78 public FileDataStorageManager(Account account, ContentProviderClient cp) {
79 mContentProviderClient = cp;
80 mContentResolver = null;
81 mAccount = account;
82 }
83
84
85 public void setAccount(Account account) {
86 mAccount = account;
87 }
88
89 public Account getAccount() {
90 return mAccount;
91 }
92
93 public ContentResolver getContentResolver() {
94 return mContentResolver;
95 }
96
97 public ContentProviderClient getContentProviderClient() {
98 return mContentProviderClient;
99 }
100
101
102 public OCFile getFileByPath(String path) {
103 Cursor c = getCursorForValue(ProviderTableMeta.FILE_PATH, path);
104 OCFile file = null;
105 if (c.moveToFirst()) {
106 file = createFileInstance(c);
107 }
108 c.close();
109 if (file == null && OCFile.ROOT_PATH.equals(path)) {
110 return createRootDir(); // root should always exist
111 }
112 return file;
113 }
114
115
116 public OCFile getFileById(long id) {
117 Cursor c = getCursorForValue(ProviderTableMeta._ID, String.valueOf(id));
118 OCFile file = null;
119 if (c.moveToFirst()) {
120 file = createFileInstance(c);
121 }
122 c.close();
123 return file;
124 }
125
126 public OCFile getFileByLocalPath(String path) {
127 Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path);
128 OCFile file = null;
129 if (c.moveToFirst()) {
130 file = createFileInstance(c);
131 }
132 c.close();
133 return file;
134 }
135
136 public boolean fileExists(long id) {
137 return fileExists(ProviderTableMeta._ID, String.valueOf(id));
138 }
139
140 public boolean fileExists(String path) {
141 return fileExists(ProviderTableMeta.FILE_PATH, path);
142 }
143
144
145 public Vector<OCFile> getFolderContent(OCFile f/*, boolean onlyOnDevice*/) {
146 if (f != null && f.isFolder() && f.getFileId() != -1) {
147 // TODO Enable when "On Device" is recovered ?
148 return getFolderContent(f.getFileId()/*, onlyOnDevice*/);
149
150 } else {
151 return new Vector<OCFile>();
152 }
153 }
154
155
156 public Vector<OCFile> getFolderImages(OCFile folder/*, boolean onlyOnDevice*/) {
157 Vector<OCFile> ret = new Vector<OCFile>();
158 if (folder != null) {
159 // TODO better implementation, filtering in the access to database instead of here
160 // TODO Enable when "On Device" is recovered ?
161 Vector<OCFile> tmp = getFolderContent(folder/*, onlyOnDevice*/);
162 OCFile current = null;
163 for (int i=0; i<tmp.size(); i++) {
164 current = tmp.get(i);
165 if (current.isImage()) {
166 ret.add(current);
167 }
168 }
169 }
170 return ret;
171 }
172
173 public boolean saveFile(OCFile file) {
174 boolean overriden = false;
175 ContentValues cv = new ContentValues();
176 cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
177 cv.put(
178 ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,
179 file.getModificationTimestampAtLastSyncForData()
180 );
181 cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
182 cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
183 cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
184 cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
185 cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
186 cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
187 if (!file.isFolder())
188 cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
189 cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
190 cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
191 cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
192 cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isFavorite() ? 1 : 0);
193 cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
194 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
195 cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
196 cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
197 cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
198 cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
199 cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail());
200 cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading());
201 cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict());
202
203 boolean sameRemotePath = fileExists(file.getRemotePath());
204 if (sameRemotePath ||
205 fileExists(file.getFileId())) { // for renamed files; no more delete and create
206
207 OCFile oldFile;
208 if (sameRemotePath) {
209 oldFile = getFileByPath(file.getRemotePath());
210 file.setFileId(oldFile.getFileId());
211 } else {
212 oldFile = getFileById(file.getFileId());
213 }
214
215 overriden = true;
216 if (getContentResolver() != null) {
217 getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv,
218 ProviderTableMeta._ID + "=?",
219 new String[]{String.valueOf(file.getFileId())});
220 } else {
221 try {
222 getContentProviderClient().update(ProviderTableMeta.CONTENT_URI,
223 cv, ProviderTableMeta._ID + "=?",
224 new String[]{String.valueOf(file.getFileId())});
225 } catch (RemoteException e) {
226 Log_OC.e(TAG,
227 "Fail to insert insert file to database "
228 + e.getMessage());
229 }
230 }
231 } else {
232 Uri result_uri = null;
233 if (getContentResolver() != null) {
234 result_uri = getContentResolver().insert(
235 ProviderTableMeta.CONTENT_URI_FILE, cv);
236 } else {
237 try {
238 result_uri = getContentProviderClient().insert(
239 ProviderTableMeta.CONTENT_URI_FILE, cv);
240 } catch (RemoteException e) {
241 Log_OC.e(TAG,
242 "Fail to insert insert file to database "
243 + e.getMessage());
244 }
245 }
246 if (result_uri != null) {
247 long new_id = Long.parseLong(result_uri.getPathSegments()
248 .get(1));
249 file.setFileId(new_id);
250 }
251 }
252
253 return overriden;
254 }
255
256
257 /**
258 * Inserts or updates the list of files contained in a given folder.
259 * <p/>
260 * CALLER IS THE RESPONSIBLE FOR GRANTING RIGHT UPDATE OF INFORMATION, NOT THIS METHOD.
261 * HERE ONLY DATA CONSISTENCY SHOULD BE GRANTED
262 *
263 * @param folder
264 * @param updatedFiles
265 * @param filesToRemove
266 */
267 public void saveFolder(
268 OCFile folder, Collection<OCFile> updatedFiles, Collection<OCFile> filesToRemove
269 ) {
270
271 Log_OC.d(TAG, "Saving folder " + folder.getRemotePath() + " with " + updatedFiles.size()
272 + " children and " + filesToRemove.size() + " files to remove");
273
274 ArrayList<ContentProviderOperation> operations =
275 new ArrayList<ContentProviderOperation>(updatedFiles.size());
276
277 // prepare operations to insert or update files to save in the given folder
278 for (OCFile file : updatedFiles) {
279 ContentValues cv = new ContentValues();
280 cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
281 cv.put(
282 ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,
283 file.getModificationTimestampAtLastSyncForData()
284 );
285 cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
286 cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
287 cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
288 cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
289 //cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
290 cv.put(ProviderTableMeta.FILE_PARENT, folder.getFileId());
291 cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
292 if (!file.isFolder()) {
293 cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
294 }
295 cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
296 cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
297 cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
298 cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isFavorite() ? 1 : 0);
299 cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
300 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
301 cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
302 cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
303 cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
304 cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
305 cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail());
306 cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading());
307 cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict());
308
309 boolean existsByPath = fileExists(file.getRemotePath());
310 if (existsByPath || fileExists(file.getFileId())) {
311 // updating an existing file
312 operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
313 withValues(cv).
314 withSelection(ProviderTableMeta._ID + "=?",
315 new String[]{String.valueOf(file.getFileId())})
316 .build());
317
318 } else {
319 // adding a new file
320 operations.add(ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).
321 withValues(cv).build());
322 }
323 }
324
325 // prepare operations to remove files in the given folder
326 String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?" + " AND " +
327 ProviderTableMeta.FILE_PATH + "=?";
328 String [] whereArgs = null;
329 for (OCFile file : filesToRemove) {
330 if (file.getParentId() == folder.getFileId()) {
331 whereArgs = new String[]{mAccount.name, file.getRemotePath()};
332 if (file.isFolder()) {
333 operations.add(ContentProviderOperation.newDelete(
334 ContentUris.withAppendedId(
335 ProviderTableMeta.CONTENT_URI_DIR, file.getFileId()
336 )
337 ).withSelection(where, whereArgs).build());
338
339 File localFolder =
340 new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
341 if (localFolder.exists()) {
342 removeLocalFolder(localFolder);
343 }
344 } else {
345 operations.add(ContentProviderOperation.newDelete(
346 ContentUris.withAppendedId(
347 ProviderTableMeta.CONTENT_URI_FILE, file.getFileId()
348 )
349 ).withSelection(where, whereArgs).build());
350
351 if (file.isDown()) {
352 String path = file.getStoragePath();
353 new File(path).delete();
354 triggerMediaScan(path); // notify MediaScanner about removed file
355 }
356 }
357 }
358 }
359
360 // update metadata of folder
361 ContentValues cv = new ContentValues();
362 cv.put(ProviderTableMeta.FILE_MODIFIED, folder.getModificationTimestamp());
363 cv.put(
364 ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,
365 folder.getModificationTimestampAtLastSyncForData()
366 );
367 cv.put(ProviderTableMeta.FILE_CREATION, folder.getCreationTimestamp());
368 cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, 0);
369 cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, folder.getMimetype());
370 cv.put(ProviderTableMeta.FILE_NAME, folder.getFileName());
371 cv.put(ProviderTableMeta.FILE_PARENT, folder.getParentId());
372 cv.put(ProviderTableMeta.FILE_PATH, folder.getRemotePath());
373 cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
374 cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, folder.getLastSyncDateForProperties());
375 cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, folder.getLastSyncDateForData());
376 cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, folder.isFavorite() ? 1 : 0);
377 cv.put(ProviderTableMeta.FILE_ETAG, folder.getEtag());
378 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, folder.isSharedViaLink() ? 1 : 0);
379 cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, folder.isSharedWithSharee() ? 1 : 0);
380 cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, folder.getPublicLink());
381 cv.put(ProviderTableMeta.FILE_PERMISSIONS, folder.getPermissions());
382 cv.put(ProviderTableMeta.FILE_REMOTE_ID, folder.getRemoteId());
383
384 operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
385 withValues(cv).
386 withSelection(ProviderTableMeta._ID + "=?",
387 new String[]{String.valueOf(folder.getFileId())})
388 .build());
389
390 // apply operations in batch
391 ContentProviderResult[] results = null;
392 Log_OC.d(TAG, "Sending " + operations.size() + " operations to FileContentProvider");
393 try {
394 if (getContentResolver() != null) {
395 results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
396
397 } else {
398 results = getContentProviderClient().applyBatch(operations);
399 }
400
401 } catch (OperationApplicationException e) {
402 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
403
404 } catch (RemoteException e) {
405 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
406 }
407
408 // update new id in file objects for insertions
409 if (results != null) {
410 long newId;
411 Iterator<OCFile> filesIt = updatedFiles.iterator();
412 OCFile file = null;
413 for (int i = 0; i < results.length; i++) {
414 if (filesIt.hasNext()) {
415 file = filesIt.next();
416 } else {
417 file = null;
418 }
419 if (results[i].uri != null) {
420 newId = Long.parseLong(results[i].uri.getPathSegments().get(1));
421 //updatedFiles.get(i).setFileId(newId);
422 if (file != null) {
423 file.setFileId(newId);
424 }
425 }
426 }
427 }
428
429 }
430
431
432 public boolean removeFile(OCFile file, boolean removeDBData, boolean removeLocalCopy) {
433 boolean success = true;
434 if (file != null) {
435 if (file.isFolder()) {
436 success = removeFolder(file, removeDBData, removeLocalCopy);
437
438 } else {
439 if (removeDBData) {
440 //Uri file_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE,
441 // ""+file.getFileId());
442 Uri file_uri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE,
443 file.getFileId());
444 String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?" + " AND " +
445 ProviderTableMeta.FILE_PATH + "=?";
446 String[] whereArgs = new String[]{mAccount.name, file.getRemotePath()};
447 int deleted = 0;
448 if (getContentProviderClient() != null) {
449 try {
450 deleted = getContentProviderClient().delete(file_uri, where, whereArgs);
451 } catch (RemoteException e) {
452 e.printStackTrace();
453 }
454 } else {
455 deleted = getContentResolver().delete(file_uri, where, whereArgs);
456 }
457 success &= (deleted > 0);
458 }
459 String localPath = file.getStoragePath();
460 if (removeLocalCopy && file.isDown() && localPath != null && success) {
461 success = new File(localPath).delete();
462 if (success) {
463 deleteFileInMediaScan(localPath);
464 }
465 if (!removeDBData && success) {
466 // maybe unnecessary, but should be checked TODO remove if unnecessary
467 file.setStoragePath(null);
468 saveFile(file);
469 saveConflict(file, null);
470 }
471 }
472 }
473 }
474 return success;
475 }
476
477
478 public boolean removeFolder(OCFile folder, boolean removeDBData, boolean removeLocalContent) {
479 boolean success = true;
480 if (folder != null && folder.isFolder()) {
481 if (removeDBData && folder.getFileId() != -1) {
482 success = removeFolderInDb(folder);
483 }
484 if (removeLocalContent && success) {
485 success = removeLocalFolder(folder);
486 }
487 }
488 return success;
489 }
490
491 private boolean removeFolderInDb(OCFile folder) {
492 Uri folder_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, "" +
493 folder.getFileId()); // URI for recursive deletion
494 String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?" + " AND " +
495 ProviderTableMeta.FILE_PATH + "=?";
496 String [] whereArgs = new String[]{mAccount.name, folder.getRemotePath()};
497 int deleted = 0;
498 if (getContentProviderClient() != null) {
499 try {
500 deleted = getContentProviderClient().delete(folder_uri, where, whereArgs);
501 } catch (RemoteException e) {
502 e.printStackTrace();
503 }
504 } else {
505 deleted = getContentResolver().delete(folder_uri, where, whereArgs);
506 }
507 return deleted > 0;
508 }
509
510 private boolean removeLocalFolder(OCFile folder) {
511 boolean success = true;
512 String localFolderPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, folder);
513 File localFolder = new File(localFolderPath);
514 if (localFolder.exists()) {
515 // stage 1: remove the local files already registered in the files database
516 // TODO Enable when "On Device" is recovered ?
517 Vector<OCFile> files = getFolderContent(folder.getFileId()/*, false*/);
518 if (files != null) {
519 for (OCFile file : files) {
520 if (file.isFolder()) {
521 success &= removeLocalFolder(file);
522 } else {
523 if (file.isDown()) {
524 File localFile = new File(file.getStoragePath());
525 success &= localFile.delete();
526 if (success) {
527 // notify MediaScanner about removed file
528 deleteFileInMediaScan(file.getStoragePath());
529 file.setStoragePath(null);
530 saveFile(file);
531 }
532 }
533 }
534 }
535 }
536
537 // stage 2: remove the folder itself and any local file inside out of sync;
538 // for instance, after clearing the app cache or reinstalling
539 success &= removeLocalFolder(localFolder);
540 }
541 return success;
542 }
543
544 private boolean removeLocalFolder(File localFolder) {
545 boolean success = true;
546 File[] localFiles = localFolder.listFiles();
547 if (localFiles != null) {
548 for (File localFile : localFiles) {
549 if (localFile.isDirectory()) {
550 success &= removeLocalFolder(localFile);
551 } else {
552 String path = localFile.getAbsolutePath();
553 success &= localFile.delete();
554 }
555 }
556 }
557 success &= localFolder.delete();
558 return success;
559 }
560
561
562 /**
563 * Updates database and file system for a file or folder that was moved to a different location.
564 *
565 * TODO explore better (faster) implementations
566 * TODO throw exceptions up !
567 */
568 public void moveLocalFile(OCFile file, String targetPath, String targetParentPath) {
569
570 if (file != null && file.fileExists() && !OCFile.ROOT_PATH.equals(file.getFileName())) {
571
572 OCFile targetParent = getFileByPath(targetParentPath);
573 if (targetParent == null) {
574 throw new IllegalStateException(
575 "Parent folder of the target path does not exist!!");
576 }
577
578 /// 1. get all the descendants of the moved element in a single QUERY
579 Cursor c = null;
580 if (getContentProviderClient() != null) {
581 try {
582 c = getContentProviderClient().query(
583 ProviderTableMeta.CONTENT_URI,
584 null,
585 ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
586 ProviderTableMeta.FILE_PATH + " LIKE ? ",
587 new String[]{
588 mAccount.name,
589 file.getRemotePath() + "%"
590 },
591 ProviderTableMeta.FILE_PATH + " ASC "
592 );
593 } catch (RemoteException e) {
594 Log_OC.e(TAG, e.getMessage());
595 }
596
597 } else {
598 c = getContentResolver().query(
599 ProviderTableMeta.CONTENT_URI,
600 null,
601 ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
602 ProviderTableMeta.FILE_PATH + " LIKE ? ",
603 new String[]{
604 mAccount.name,
605 file.getRemotePath() + "%"
606 },
607 ProviderTableMeta.FILE_PATH + " ASC "
608 );
609 }
610
611 /// 2. prepare a batch of update operations to change all the descendants
612 ArrayList<ContentProviderOperation> operations =
613 new ArrayList<ContentProviderOperation>(c.getCount());
614 String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name);
615 List<String> originalPathsToTriggerMediaScan = new ArrayList<String>();
616 List<String> newPathsToTriggerMediaScan = new ArrayList<String>();
617 if (c.moveToFirst()) {
618 int lengthOfOldPath = file.getRemotePath().length();
619 int lengthOfOldStoragePath = defaultSavePath.length() + lengthOfOldPath;
620 do {
621 ContentValues cv = new ContentValues(); // keep construction in the loop
622 OCFile child = createFileInstance(c);
623 cv.put(
624 ProviderTableMeta.FILE_PATH,
625 targetPath + child.getRemotePath().substring(lengthOfOldPath)
626 );
627 if (child.getStoragePath() != null &&
628 child.getStoragePath().startsWith(defaultSavePath)) {
629 // update link to downloaded content - but local move is not done here!
630 String targetLocalPath = defaultSavePath + targetPath +
631 child.getStoragePath().substring(lengthOfOldStoragePath);
632
633 cv.put(ProviderTableMeta.FILE_STORAGE_PATH, targetLocalPath);
634
635 originalPathsToTriggerMediaScan.add(child.getStoragePath());
636 newPathsToTriggerMediaScan.add(targetLocalPath);
637
638 }
639 if (child.getRemotePath().equals(file.getRemotePath())) {
640 cv.put(
641 ProviderTableMeta.FILE_PARENT,
642 targetParent.getFileId()
643 );
644 }
645 operations.add(
646 ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
647 withValues(cv).
648 withSelection(
649 ProviderTableMeta._ID + "=?",
650 new String[]{String.valueOf(child.getFileId())}
651 )
652 .build());
653
654 } while (c.moveToNext());
655 }
656 c.close();
657
658 /// 3. apply updates in batch
659 try {
660 if (getContentResolver() != null) {
661 getContentResolver().applyBatch(MainApp.getAuthority(), operations);
662
663 } else {
664 getContentProviderClient().applyBatch(operations);
665 }
666
667 } catch (Exception e) {
668 Log_OC.e(TAG, "Fail to update " + file.getFileId() + " and descendants in database",
669 e);
670 }
671
672 /// 4. move in local file system
673 String originalLocalPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, file);
674 String targetLocalPath = defaultSavePath + targetPath;
675 File localFile = new File(originalLocalPath);
676 boolean renamed = false;
677 if (localFile.exists()) {
678 File targetFile = new File(targetLocalPath);
679 File targetFolder = targetFile.getParentFile();
680 if (!targetFolder.exists()) {
681 targetFolder.mkdirs();
682 }
683 renamed = localFile.renameTo(targetFile);
684 }
685
686 if (renamed) {
687 Iterator<String> it = originalPathsToTriggerMediaScan.iterator();
688 while (it.hasNext()) {
689 // Notify MediaScanner about removed file
690 deleteFileInMediaScan(it.next());
691 }
692 it = newPathsToTriggerMediaScan.iterator();
693 while (it.hasNext()) {
694 // Notify MediaScanner about new file/folder
695 triggerMediaScan(it.next());
696 }
697 }
698 }
699
700 }
701
702 public void copyLocalFile(OCFile file, String targetPath) {
703
704 if (file != null && file.fileExists() && !OCFile.ROOT_PATH.equals(file.getFileName())) {
705 String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, file);
706 File localFile = new File(localPath);
707 boolean copied = false;
708 String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name);
709 if (localFile.exists()) {
710 File targetFile = new File(defaultSavePath + targetPath);
711 File targetFolder = targetFile.getParentFile();
712 if (!targetFolder.exists()) {
713 targetFolder.mkdirs();
714 }
715 copied = copyFile(localFile, targetFile);
716 }
717 Log_OC.d(TAG, "Local file COPIED : " + copied);
718 }
719 }
720
721 private boolean copyFile(File src, File target) {
722 boolean ret = true;
723
724 InputStream in = null;
725 OutputStream out = null;
726
727 try {
728 in = new FileInputStream(src);
729 out = new FileOutputStream(target);
730 byte[] buf = new byte[1024];
731 int len;
732 while ((len = in.read(buf)) > 0) {
733 out.write(buf, 0, len);
734 }
735 } catch (IOException ex) {
736 ret = false;
737 } finally {
738 if (in != null) try {
739 in.close();
740 } catch (IOException e) {
741 e.printStackTrace(System.err);
742 }
743 if (out != null) try {
744 out.close();
745 } catch (IOException e) {
746 e.printStackTrace(System.err);
747 }
748 }
749
750 return ret;
751 }
752
753
754 private Vector<OCFile> getFolderContent(long parentId/*, boolean onlyOnDevice*/) {
755
756 Vector<OCFile> ret = new Vector<OCFile>();
757
758 Uri req_uri = Uri.withAppendedPath(
759 ProviderTableMeta.CONTENT_URI_DIR,
760 String.valueOf(parentId));
761 Cursor c = null;
762
763 if (getContentProviderClient() != null) {
764 try {
765 c = getContentProviderClient().query(req_uri, null,
766 ProviderTableMeta.FILE_PARENT + "=?",
767 new String[]{String.valueOf(parentId)}, null);
768 } catch (RemoteException e) {
769 Log_OC.e(TAG, e.getMessage());
770 return ret;
771 }
772 } else {
773 c = getContentResolver().query(req_uri, null,
774 ProviderTableMeta.FILE_PARENT + "=?",
775 new String[]{String.valueOf(parentId)}, null);
776 }
777
778 if (c.moveToFirst()) {
779 do {
780 OCFile child = createFileInstance(c);
781 // TODO Enable when "On Device" is recovered ?
782 // if (child.isFolder() || !onlyOnDevice || onlyOnDevice && child.isDown()){
783 ret.add(child);
784 // }
785 } while (c.moveToNext());
786 }
787
788 c.close();
789
790 Collections.sort(ret);
791
792 return ret;
793 }
794
795
796 private OCFile createRootDir() {
797 OCFile file = new OCFile(OCFile.ROOT_PATH);
798 file.setMimetype("DIR");
799 file.setParentId(FileDataStorageManager.ROOT_PARENT_ID);
800 saveFile(file);
801 return file;
802 }
803
804 private boolean fileExists(String cmp_key, String value) {
805 Cursor c;
806 if (getContentResolver() != null) {
807 c = getContentResolver()
808 .query(ProviderTableMeta.CONTENT_URI,
809 null,
810 cmp_key + "=? AND "
811 + ProviderTableMeta.FILE_ACCOUNT_OWNER
812 + "=?",
813 new String[]{value, mAccount.name}, null);
814 } else {
815 try {
816 c = getContentProviderClient().query(
817 ProviderTableMeta.CONTENT_URI,
818 null,
819 cmp_key + "=? AND "
820 + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
821 new String[]{value, mAccount.name}, null);
822 } catch (RemoteException e) {
823 Log_OC.e(TAG,
824 "Couldn't determine file existance, assuming non existance: "
825 + e.getMessage());
826 return false;
827 }
828 }
829 boolean retval = c.moveToFirst();
830 c.close();
831 return retval;
832 }
833
834 private Cursor getCursorForValue(String key, String value) {
835 Cursor c = null;
836 if (getContentResolver() != null) {
837 c = getContentResolver()
838 .query(ProviderTableMeta.CONTENT_URI,
839 null,
840 key + "=? AND "
841 + ProviderTableMeta.FILE_ACCOUNT_OWNER
842 + "=?",
843 new String[]{value, mAccount.name}, null);
844 } else {
845 try {
846 c = getContentProviderClient().query(
847 ProviderTableMeta.CONTENT_URI,
848 null,
849 key + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER
850 + "=?", new String[]{value, mAccount.name},
851 null);
852 } catch (RemoteException e) {
853 Log_OC.e(TAG, "Could not get file details: " + e.getMessage());
854 c = null;
855 }
856 }
857 return c;
858 }
859
860
861 private OCFile createFileInstance(Cursor c) {
862 OCFile file = null;
863 if (c != null) {
864 file = new OCFile(c.getString(c
865 .getColumnIndex(ProviderTableMeta.FILE_PATH)));
866 file.setFileId(c.getLong(c.getColumnIndex(ProviderTableMeta._ID)));
867 file.setParentId(c.getLong(c
868 .getColumnIndex(ProviderTableMeta.FILE_PARENT)));
869 file.setMimetype(c.getString(c
870 .getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)));
871 if (!file.isFolder()) {
872 file.setStoragePath(c.getString(c
873 .getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)));
874 if (file.getStoragePath() == null) {
875 // try to find existing file and bind it with current account;
876 // with the current update of SynchronizeFolderOperation, this won't be
877 // necessary anymore after a full synchronization of the account
878 File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
879 if (f.exists()) {
880 file.setStoragePath(f.getAbsolutePath());
881 file.setLastSyncDateForData(f.lastModified());
882 }
883 }
884 }
885 file.setFileLength(c.getLong(c
886 .getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH)));
887 file.setCreationTimestamp(c.getLong(c
888 .getColumnIndex(ProviderTableMeta.FILE_CREATION)));
889 file.setModificationTimestamp(c.getLong(c
890 .getColumnIndex(ProviderTableMeta.FILE_MODIFIED)));
891 file.setModificationTimestampAtLastSyncForData(c.getLong(c
892 .getColumnIndex(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA)));
893 file.setLastSyncDateForProperties(c.getLong(c
894 .getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE)));
895 file.setLastSyncDateForData(c.getLong(c.
896 getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA)));
897 file.setFavorite(c.getInt(
898 c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false);
899 file.setEtag(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG)));
900 file.setShareViaLink(c.getInt(
901 c.getColumnIndex(ProviderTableMeta.FILE_SHARED_VIA_LINK)) == 1 ? true : false);
902 file.setShareWithSharee(c.getInt(
903 c.getColumnIndex(ProviderTableMeta.FILE_SHARED_WITH_SHAREE)) == 1 ? true : false);
904 file.setPublicLink(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PUBLIC_LINK)));
905 file.setPermissions(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PERMISSIONS)));
906 file.setRemoteId(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_REMOTE_ID)));
907 file.setNeedsUpdateThumbnail(c.getInt(
908 c.getColumnIndex(ProviderTableMeta.FILE_UPDATE_THUMBNAIL)) == 1 ? true : false);
909 file.setDownloading(c.getInt(
910 c.getColumnIndex(ProviderTableMeta.FILE_IS_DOWNLOADING)) == 1 ? true : false);
911 file.setEtagInConflict(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG_IN_CONFLICT)));
912
913 }
914 return file;
915 }
916
917 // Methods for Shares
918 public boolean saveShare(OCShare share) {
919 boolean overriden = false;
920 ContentValues cv = new ContentValues();
921 cv.put(ProviderTableMeta.OCSHARES_FILE_SOURCE, share.getFileSource());
922 cv.put(ProviderTableMeta.OCSHARES_ITEM_SOURCE, share.getItemSource());
923 cv.put(ProviderTableMeta.OCSHARES_SHARE_TYPE, share.getShareType().getValue());
924 cv.put(ProviderTableMeta.OCSHARES_SHARE_WITH, share.getShareWith());
925 cv.put(ProviderTableMeta.OCSHARES_PATH, share.getPath());
926 cv.put(ProviderTableMeta.OCSHARES_PERMISSIONS, share.getPermissions());
927 cv.put(ProviderTableMeta.OCSHARES_SHARED_DATE, share.getSharedDate());
928 cv.put(ProviderTableMeta.OCSHARES_EXPIRATION_DATE, share.getExpirationDate());
929 cv.put(ProviderTableMeta.OCSHARES_TOKEN, share.getToken());
930 cv.put(
931 ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME,
932 share.getSharedWithDisplayName()
933 );
934 cv.put(ProviderTableMeta.OCSHARES_IS_DIRECTORY, share.isFolder() ? 1 : 0);
935 cv.put(ProviderTableMeta.OCSHARES_USER_ID, share.getUserId());
936 cv.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, share.getIdRemoteShared());
937 cv.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, mAccount.name);
938
939 if (shareExists(share.getIdRemoteShared())) {// for renamed files; no more delete and create
940 overriden = true;
941 if (getContentResolver() != null) {
942 getContentResolver().update(ProviderTableMeta.CONTENT_URI_SHARE, cv,
943 ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
944 new String[]{String.valueOf(share.getIdRemoteShared())});
945 } else {
946 try {
947 getContentProviderClient().update(ProviderTableMeta.CONTENT_URI_SHARE,
948 cv, ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
949 new String[]{String.valueOf(share.getIdRemoteShared())});
950 } catch (RemoteException e) {
951 Log_OC.e(TAG,
952 "Fail to insert insert file to database "
953 + e.getMessage());
954 }
955 }
956 } else {
957 Uri result_uri = null;
958 if (getContentResolver() != null) {
959 result_uri = getContentResolver().insert(
960 ProviderTableMeta.CONTENT_URI_SHARE, cv);
961 } else {
962 try {
963 result_uri = getContentProviderClient().insert(
964 ProviderTableMeta.CONTENT_URI_SHARE, cv);
965 } catch (RemoteException e) {
966 Log_OC.e(TAG,
967 "Fail to insert insert file to database "
968 + e.getMessage());
969 }
970 }
971 if (result_uri != null) {
972 long new_id = Long.parseLong(result_uri.getPathSegments()
973 .get(1));
974 share.setId(new_id);
975 }
976 }
977
978 return overriden;
979 }
980
981
982 public OCShare getFirstShareByPathAndType(String path, ShareType type, String shareWith) {
983 Cursor c = null;
984
985 String selection = ProviderTableMeta.OCSHARES_PATH + "=? AND "
986 + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? AND "
987 + ProviderTableMeta.OCSHARES_SHARE_WITH + "=? AND "
988 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?" ;
989
990 String [] selectionArgs = new String[]{path, Integer.toString(type.getValue()),
991 shareWith, mAccount.name};
992
993 if (getContentResolver() != null) {
994 c = getContentResolver().query(
995 ProviderTableMeta.CONTENT_URI_SHARE,
996 null,
997 selection, selectionArgs,
998 null);
999 } else {
1000 try {
1001 c = getContentProviderClient().query(
1002 ProviderTableMeta.CONTENT_URI_SHARE,
1003 null,
1004 selection, selectionArgs,
1005 null);
1006
1007 } catch (RemoteException e) {
1008 Log_OC.e(TAG, "Could not get file details: " + e.getMessage());
1009 c = null;
1010 }
1011 }
1012 OCShare share = null;
1013 if (c.moveToFirst()) {
1014 share = createShareInstance(c);
1015 }
1016 c.close();
1017 return share;
1018 }
1019
1020 private OCShare createShareInstance(Cursor c) {
1021 OCShare share = null;
1022 if (c != null) {
1023 share = new OCShare(c.getString(c
1024 .getColumnIndex(ProviderTableMeta.OCSHARES_PATH)));
1025 share.setId(c.getLong(c.getColumnIndex(ProviderTableMeta._ID)));
1026 share.setFileSource(c.getLong(c
1027 .getColumnIndex(ProviderTableMeta.OCSHARES_ITEM_SOURCE)));
1028 share.setShareType(ShareType.fromValue(c.getInt(c
1029 .getColumnIndex(ProviderTableMeta.OCSHARES_SHARE_TYPE))));
1030 share.setShareWith(c.getString(c
1031 .getColumnIndex(ProviderTableMeta.OCSHARES_SHARE_WITH)));
1032 share.setPermissions(c.getInt(c
1033 .getColumnIndex(ProviderTableMeta.OCSHARES_PERMISSIONS)));
1034 share.setSharedDate(c.getLong(c
1035 .getColumnIndex(ProviderTableMeta.OCSHARES_SHARED_DATE)));
1036 share.setExpirationDate(c.getLong(c
1037 .getColumnIndex(ProviderTableMeta.OCSHARES_EXPIRATION_DATE)));
1038 share.setToken(c.getString(c
1039 .getColumnIndex(ProviderTableMeta.OCSHARES_TOKEN)));
1040 share.setSharedWithDisplayName(c.getString(c
1041 .getColumnIndex(ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME)));
1042 share.setIsFolder(c.getInt(
1043 c.getColumnIndex(ProviderTableMeta.OCSHARES_IS_DIRECTORY)) == 1);
1044 share.setUserId(c.getLong(c.getColumnIndex(ProviderTableMeta.OCSHARES_USER_ID)));
1045 share.setIdRemoteShared(c.getLong(
1046 c.getColumnIndex(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED)));
1047 }
1048 return share;
1049 }
1050
1051 private boolean shareExists(String cmp_key, String value) {
1052 Cursor c;
1053 if (getContentResolver() != null) {
1054 c = getContentResolver()
1055 .query(ProviderTableMeta.CONTENT_URI_SHARE,
1056 null,
1057 cmp_key + "=? AND "
1058 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER
1059 + "=?",
1060 new String[]{value, mAccount.name}, null);
1061 } else {
1062 try {
1063 c = getContentProviderClient().query(
1064 ProviderTableMeta.CONTENT_URI_SHARE,
1065 null,
1066 cmp_key + "=? AND "
1067 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
1068 new String[]{value, mAccount.name}, null);
1069 } catch (RemoteException e) {
1070 Log_OC.e(TAG,
1071 "Couldn't determine file existance, assuming non existance: "
1072 + e.getMessage());
1073 return false;
1074 }
1075 }
1076 boolean retval = c.moveToFirst();
1077 c.close();
1078 return retval;
1079 }
1080
1081 private boolean shareExists(long remoteId) {
1082 return shareExists(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, String.valueOf(remoteId));
1083 }
1084
1085 private void resetShareFlagsInAllFiles() {
1086 ContentValues cv = new ContentValues();
1087 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, false);
1088 cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, false);
1089 cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
1090 String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
1091 String[] whereArgs = new String[]{mAccount.name};
1092
1093 if (getContentResolver() != null) {
1094 getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
1095
1096 } else {
1097 try {
1098 getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where,
1099 whereArgs);
1100 } catch (RemoteException e) {
1101 Log_OC.e(TAG, "Exception in resetShareFlagsInAllFiles" + e.getMessage());
1102 }
1103 }
1104 }
1105
1106 private void resetShareFlagsInFolder(OCFile folder) {
1107 ContentValues cv = new ContentValues();
1108 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, false);
1109 cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, false);
1110 cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
1111 String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
1112 ProviderTableMeta.FILE_PARENT + "=?";
1113 String [] whereArgs = new String[] { mAccount.name , String.valueOf(folder.getFileId()) };
1114
1115 if (getContentResolver() != null) {
1116 getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
1117
1118 } else {
1119 try {
1120 getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where,
1121 whereArgs);
1122 } catch (RemoteException e) {
1123 Log_OC.e(TAG, "Exception in resetShareFlagsInFolder " + e.getMessage());
1124 }
1125 }
1126 }
1127
1128 private void resetShareFlagInAFile(String filePath){
1129 ContentValues cv = new ContentValues();
1130 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, false);
1131 cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, false);
1132 cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
1133 String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
1134 ProviderTableMeta.FILE_PATH+ "=?";
1135 String [] whereArgs = new String[] { mAccount.name , filePath };
1136
1137 if (getContentResolver() != null) {
1138 getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
1139
1140 } else {
1141 try {
1142 getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where,
1143 whereArgs);
1144 } catch (RemoteException e) {
1145 Log_OC.e(TAG, "Exception in resetShareFlagsInFolder " + e.getMessage());
1146 }
1147 }
1148 }
1149
1150 private void cleanShares() {
1151 String where = ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
1152 String[] whereArgs = new String[]{mAccount.name};
1153
1154 if (getContentResolver() != null) {
1155 getContentResolver().delete(ProviderTableMeta.CONTENT_URI_SHARE, where, whereArgs);
1156
1157 } else {
1158 try {
1159 getContentProviderClient().delete(ProviderTableMeta.CONTENT_URI_SHARE, where,
1160 whereArgs);
1161 } catch (RemoteException e) {
1162 Log_OC.e(TAG, "Exception in cleanShares" + e.getMessage());
1163 }
1164 }
1165 }
1166
1167 public void saveShares(Collection<OCShare> shares) {
1168 cleanShares();
1169 if (shares != null) {
1170 ArrayList<ContentProviderOperation> operations =
1171 new ArrayList<ContentProviderOperation>(shares.size());
1172
1173 // prepare operations to insert or update files to save in the given folder
1174 for (OCShare share : shares) {
1175 ContentValues cv = new ContentValues();
1176 cv.put(ProviderTableMeta.OCSHARES_FILE_SOURCE, share.getFileSource());
1177 cv.put(ProviderTableMeta.OCSHARES_ITEM_SOURCE, share.getItemSource());
1178 cv.put(ProviderTableMeta.OCSHARES_SHARE_TYPE, share.getShareType().getValue());
1179 cv.put(ProviderTableMeta.OCSHARES_SHARE_WITH, share.getShareWith());
1180 cv.put(ProviderTableMeta.OCSHARES_PATH, share.getPath());
1181 cv.put(ProviderTableMeta.OCSHARES_PERMISSIONS, share.getPermissions());
1182 cv.put(ProviderTableMeta.OCSHARES_SHARED_DATE, share.getSharedDate());
1183 cv.put(ProviderTableMeta.OCSHARES_EXPIRATION_DATE, share.getExpirationDate());
1184 cv.put(ProviderTableMeta.OCSHARES_TOKEN, share.getToken());
1185 cv.put(
1186 ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME,
1187 share.getSharedWithDisplayName()
1188 );
1189 cv.put(ProviderTableMeta.OCSHARES_IS_DIRECTORY, share.isFolder() ? 1 : 0);
1190 cv.put(ProviderTableMeta.OCSHARES_USER_ID, share.getUserId());
1191 cv.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, share.getIdRemoteShared());
1192 cv.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, mAccount.name);
1193
1194 if (shareExists(share.getIdRemoteShared())) {
1195 // updating an existing file
1196 operations.add(
1197 ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI_SHARE).
1198 withValues(cv).
1199 withSelection(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
1200 new String[]{String.valueOf(share.getIdRemoteShared())})
1201 .build());
1202 } else {
1203 // adding a new file
1204 operations.add(
1205 ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI_SHARE).
1206 withValues(cv).
1207 build()
1208 );
1209 }
1210 }
1211
1212 // apply operations in batch
1213 if (operations.size() > 0) {
1214 @SuppressWarnings("unused")
1215 ContentProviderResult[] results = null;
1216 Log_OC.d(TAG, "Sending " + operations.size() +
1217 " operations to FileContentProvider");
1218 try {
1219 if (getContentResolver() != null) {
1220 results = getContentResolver().applyBatch(MainApp.getAuthority(),
1221 operations);
1222 } else {
1223 results = getContentProviderClient().applyBatch(operations);
1224 }
1225
1226 } catch (OperationApplicationException e) {
1227 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1228
1229 } catch (RemoteException e) {
1230 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1231 }
1232 }
1233 }
1234
1235 }
1236
1237 public void updateSharedFiles(Collection<OCFile> sharedFiles) {
1238 resetShareFlagsInAllFiles();
1239
1240 if (sharedFiles != null) {
1241 ArrayList<ContentProviderOperation> operations =
1242 new ArrayList<ContentProviderOperation>(sharedFiles.size());
1243
1244 // prepare operations to insert or update files to save in the given folder
1245 for (OCFile file : sharedFiles) {
1246 ContentValues cv = new ContentValues();
1247 cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
1248 cv.put(
1249 ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,
1250 file.getModificationTimestampAtLastSyncForData()
1251 );
1252 cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
1253 cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
1254 cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
1255 cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
1256 cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
1257 cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
1258 if (!file.isFolder()) {
1259 cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
1260 }
1261 cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
1262 cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
1263 cv.put(
1264 ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA,
1265 file.getLastSyncDateForData()
1266 );
1267 cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isFavorite() ? 1 : 0);
1268 cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
1269 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
1270 cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
1271 cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
1272 cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
1273 cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
1274 cv.put(
1275 ProviderTableMeta.FILE_UPDATE_THUMBNAIL,
1276 file.needsUpdateThumbnail() ? 1 : 0
1277 );
1278 cv.put(
1279 ProviderTableMeta.FILE_IS_DOWNLOADING,
1280 file.isDownloading() ? 1 : 0
1281 );
1282 cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict());
1283
1284 boolean existsByPath = fileExists(file.getRemotePath());
1285 if (existsByPath || fileExists(file.getFileId())) {
1286 // updating an existing file
1287 operations.add(
1288 ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
1289 withValues(cv).
1290 withSelection(ProviderTableMeta._ID + "=?",
1291 new String[]{String.valueOf(file.getFileId())})
1292 .build());
1293
1294 } else {
1295 // adding a new file
1296 operations.add(
1297 ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).
1298 withValues(cv).
1299 build()
1300 );
1301 }
1302 }
1303
1304 // apply operations in batch
1305 if (operations.size() > 0) {
1306 @SuppressWarnings("unused")
1307 ContentProviderResult[] results = null;
1308 Log_OC.d(TAG, "Sending " + operations.size() +
1309 " operations to FileContentProvider");
1310 try {
1311 if (getContentResolver() != null) {
1312 results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
1313 } else {
1314 results = getContentProviderClient().applyBatch(operations);
1315 }
1316
1317 } catch (OperationApplicationException e) {
1318 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1319
1320 } catch (RemoteException e) {
1321 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1322 }
1323 }
1324 }
1325
1326 }
1327
1328 public void removeShare(OCShare share) {
1329 Uri share_uri = ProviderTableMeta.CONTENT_URI_SHARE;
1330 String where = ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?" + " AND " +
1331 ProviderTableMeta._ID + "=?";
1332 String [] whereArgs = new String[]{mAccount.name, Long.toString(share.getId())};
1333 if (getContentProviderClient() != null) {
1334 try {
1335 getContentProviderClient().delete(share_uri, where, whereArgs);
1336 } catch (RemoteException e) {
1337 e.printStackTrace();
1338 }
1339 } else {
1340 getContentResolver().delete(share_uri, where, whereArgs);
1341 }
1342 }
1343
1344 public void saveSharesDB(ArrayList<OCShare> shares) {
1345 ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
1346
1347 // Reset flags & Remove shares for this files
1348 String filePath = "";
1349 for (OCShare share: shares) {
1350 if (filePath != share.getPath()){
1351 filePath = share.getPath();
1352 resetShareFlagInAFile(filePath);
1353 operations = prepareRemoveSharesInFile(filePath, operations);
1354 }
1355 }
1356
1357 // Add operations to insert shares
1358 operations = prepareInsertShares(shares, operations);
1359
1360 // apply operations in batch
1361 if (operations.size() > 0) {
1362 Log_OC.d(TAG, "Sending " + operations.size() + " operations to FileContentProvider");
1363 try {
1364 if (getContentResolver() != null) {
1365 getContentResolver().applyBatch(MainApp.getAuthority(), operations);
1366
1367 } else {
1368 getContentProviderClient().applyBatch(operations);
1369 }
1370
1371 } catch (OperationApplicationException e) {
1372 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1373
1374 } catch (RemoteException e) {
1375 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1376 }
1377 }
1378
1379 // // TODO: review if it is needed
1380 // // Update shared files
1381 // ArrayList<OCFile> sharedFiles = new ArrayList<OCFile>();
1382 //
1383 // for (OCShare share : shares) {
1384 // // Get the path
1385 // String path = share.getPath();
1386 // if (share.isFolder()) {
1387 // path = path + FileUtils.PATH_SEPARATOR;
1388 // }
1389 //
1390 // // Update OCFile with data from share: ShareByLink, publicLink and
1391 // OCFile file = getFileByPath(path);
1392 // if (file != null) {
1393 // if (share.getShareType().equals(ShareType.PUBLIC_LINK)) {
1394 // file.setShareViaLink(true);
1395 // sharedFiles.add(file);
1396 // }
1397 // }
1398 // }
1399 //
1400 // // TODO: Review
1401 // updateSharedFiles(sharedFiles);
1402 }
1403
1404
1405 public void saveSharesInFolder(ArrayList<OCShare> shares, OCFile folder) {
1406 resetShareFlagsInFolder(folder);
1407 ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
1408 operations = prepareRemoveSharesInFolder(folder, operations);
1409
1410 if (shares != null) {
1411 // prepare operations to insert or update files to save in the given folder
1412 operations = prepareInsertShares(shares, operations);
1413 }
1414
1415 // apply operations in batch
1416 if (operations.size() > 0) {
1417 Log_OC.d(TAG, "Sending " + operations.size() + " operations to FileContentProvider");
1418 try {
1419 if (getContentResolver() != null) {
1420 getContentResolver().applyBatch(MainApp.getAuthority(), operations);
1421
1422 } else {
1423 getContentProviderClient().applyBatch(operations);
1424 }
1425
1426 } catch (OperationApplicationException e) {
1427 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1428
1429 } catch (RemoteException e) {
1430
1431 }
1432 }
1433
1434 }
1435
1436 /**
1437 * Prepare operations to insert or update files to save in the given folder
1438 * @param shares List of shares to insert
1439 * @param operations List of operations
1440 * @return
1441 */
1442 private ArrayList<ContentProviderOperation> prepareInsertShares(
1443 ArrayList<OCShare> shares, ArrayList<ContentProviderOperation> operations) {
1444
1445 if (shares != null) {
1446 // prepare operations to insert or update files to save in the given folder
1447 for (OCShare share : shares) {
1448 ContentValues cv = new ContentValues();
1449 cv.put(ProviderTableMeta.OCSHARES_FILE_SOURCE, share.getFileSource());
1450 cv.put(ProviderTableMeta.OCSHARES_ITEM_SOURCE, share.getItemSource());
1451 cv.put(ProviderTableMeta.OCSHARES_SHARE_TYPE, share.getShareType().getValue());
1452 cv.put(ProviderTableMeta.OCSHARES_SHARE_WITH, share.getShareWith());
1453 cv.put(ProviderTableMeta.OCSHARES_PATH, share.getPath());
1454 cv.put(ProviderTableMeta.OCSHARES_PERMISSIONS, share.getPermissions());
1455 cv.put(ProviderTableMeta.OCSHARES_SHARED_DATE, share.getSharedDate());
1456 cv.put(ProviderTableMeta.OCSHARES_EXPIRATION_DATE, share.getExpirationDate());
1457 cv.put(ProviderTableMeta.OCSHARES_TOKEN, share.getToken());
1458 cv.put(
1459 ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME,
1460 share.getSharedWithDisplayName()
1461 );
1462 cv.put(ProviderTableMeta.OCSHARES_IS_DIRECTORY, share.isFolder() ? 1 : 0);
1463 cv.put(ProviderTableMeta.OCSHARES_USER_ID, share.getUserId());
1464 cv.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, share.getIdRemoteShared());
1465 cv.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, mAccount.name);
1466
1467 // adding a new share resource
1468 operations.add(
1469 ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI_SHARE).
1470 withValues(cv).
1471 build()
1472 );
1473 }
1474 }
1475 return operations;
1476 }
1477
1478 private ArrayList<ContentProviderOperation> prepareRemoveSharesInFolder(
1479 OCFile folder, ArrayList<ContentProviderOperation> preparedOperations) {
1480 if (folder != null) {
1481 String where = ProviderTableMeta.OCSHARES_PATH + "=?" + " AND "
1482 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
1483 String [] whereArgs = new String[]{ "", mAccount.name };
1484
1485 // TODO Enable when "On Device" is recovered ?
1486 Vector<OCFile> files = getFolderContent(folder /*, false*/);
1487
1488 for (OCFile file : files) {
1489 whereArgs[0] = file.getRemotePath();
1490 preparedOperations.add(
1491 ContentProviderOperation.newDelete(ProviderTableMeta.CONTENT_URI_SHARE).
1492 withSelection(where, whereArgs).
1493 build()
1494 );
1495 }
1496 }
1497 return preparedOperations;
1498
1499 }
1500
1501 private ArrayList<ContentProviderOperation> prepareRemoveSharesInFile(
1502 String filePath, ArrayList<ContentProviderOperation> preparedOperations) {
1503
1504 String where = ProviderTableMeta.OCSHARES_PATH + "=?" + " AND "
1505 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
1506 String[] whereArgs = new String[]{filePath, mAccount.name};
1507
1508 preparedOperations.add(
1509 ContentProviderOperation.newDelete(ProviderTableMeta.CONTENT_URI_SHARE).
1510 withSelection(where, whereArgs).
1511 build()
1512 );
1513
1514 return preparedOperations;
1515
1516 }
1517
1518 public ArrayList<OCShare> getSharesWithForAFile(String filePath, String accountName){
1519 // Condition
1520 String where = ProviderTableMeta.OCSHARES_PATH + "=?" + " AND "
1521 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?"+ "AND"
1522 + " (" + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? OR "
1523 + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? ) ";
1524 String [] whereArgs = new String[]{ filePath, accountName ,
1525 Integer.toString(ShareType.USER.getValue()),
1526 Integer.toString(ShareType.GROUP.getValue()) };
1527
1528 Cursor c = null;
1529 if (getContentResolver() != null) {
1530 c = getContentResolver().query(
1531 ProviderTableMeta.CONTENT_URI_SHARE,
1532 null, where, whereArgs, null);
1533 } else {
1534 try {
1535 c = getContentProviderClient().query(
1536 ProviderTableMeta.CONTENT_URI_SHARE,
1537 null, where, whereArgs, null);
1538
1539 } catch (RemoteException e) {
1540 Log_OC.e(TAG, "Could not get list of shares with: " + e.getMessage());
1541 c = null;
1542 }
1543 }
1544 ArrayList<OCShare> shares = new ArrayList<OCShare>();
1545 OCShare share = null;
1546 if (c.moveToFirst()) {
1547 do {
1548 share = createShareInstance(c);
1549 shares.add(share);
1550 // }
1551 } while (c.moveToNext());
1552 }
1553 c.close();
1554
1555 return shares;
1556 }
1557
1558 public void triggerMediaScan(String path) {
1559 Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
1560 intent.setData(Uri.fromFile(new File(path)));
1561 MainApp.getAppContext().sendBroadcast(intent);
1562 }
1563
1564 public void deleteFileInMediaScan(String path) {
1565
1566 String mimetypeString = FileStorageUtils.getMimeTypeFromName(path);
1567 ContentResolver contentResolver = getContentResolver();
1568
1569 if (contentResolver != null) {
1570 if (mimetypeString.startsWith("image/")) {
1571 // Images
1572 contentResolver.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
1573 MediaStore.Images.Media.DATA + "=?", new String[]{path});
1574 } else if (mimetypeString.startsWith("audio/")) {
1575 // Audio
1576 contentResolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
1577 MediaStore.Audio.Media.DATA + "=?", new String[]{path});
1578 } else if (mimetypeString.startsWith("video/")) {
1579 // Video
1580 contentResolver.delete(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
1581 MediaStore.Video.Media.DATA + "=?", new String[]{path});
1582 }
1583 } else {
1584 ContentProviderClient contentProviderClient = getContentProviderClient();
1585 try {
1586 if (mimetypeString.startsWith("image/")) {
1587 // Images
1588 contentProviderClient.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
1589 MediaStore.Images.Media.DATA + "=?", new String[]{path});
1590 } else if (mimetypeString.startsWith("audio/")) {
1591 // Audio
1592 contentProviderClient.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
1593 MediaStore.Audio.Media.DATA + "=?", new String[]{path});
1594 } else if (mimetypeString.startsWith("video/")) {
1595 // Video
1596 contentProviderClient.delete(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
1597 MediaStore.Video.Media.DATA + "=?", new String[]{path});
1598 }
1599 } catch (RemoteException e) {
1600 Log_OC.e(TAG, "Exception deleting media file in MediaStore " + e.getMessage());
1601 }
1602 }
1603
1604 }
1605
1606 public void saveConflict(OCFile file, String etagInConflict) {
1607 if (!file.isDown()) {
1608 etagInConflict = null;
1609 }
1610 ContentValues cv = new ContentValues();
1611 cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, etagInConflict);
1612 int updated = 0;
1613 if (getContentResolver() != null) {
1614 updated = getContentResolver().update(
1615 ProviderTableMeta.CONTENT_URI_FILE,
1616 cv,
1617 ProviderTableMeta._ID + "=?",
1618 new String[] { String.valueOf(file.getFileId())}
1619 );
1620 } else {
1621 try {
1622 updated = getContentProviderClient().update(
1623 ProviderTableMeta.CONTENT_URI_FILE,
1624 cv,
1625 ProviderTableMeta._ID + "=?",
1626 new String[]{String.valueOf(file.getFileId())}
1627 );
1628 } catch (RemoteException e) {
1629 Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage());
1630 }
1631 }
1632
1633 Log_OC.d(TAG, "Number of files updated with CONFLICT: " + updated);
1634
1635 if (updated > 0) {
1636 if (etagInConflict != null) {
1637 /// set conflict in all ancestor folders
1638
1639 long parentId = file.getParentId();
1640 Set<String> ancestorIds = new HashSet<String>();
1641 while (parentId != FileDataStorageManager.ROOT_PARENT_ID) {
1642 ancestorIds.add(Long.toString(parentId));
1643 parentId = getFileById(parentId).getParentId();
1644 }
1645
1646 if (ancestorIds.size() > 0) {
1647 StringBuffer whereBuffer = new StringBuffer();
1648 whereBuffer.append(ProviderTableMeta._ID).append(" IN (");
1649 for (int i = 0; i < ancestorIds.size() - 1; i++) {
1650 whereBuffer.append("?,");
1651 }
1652 whereBuffer.append("?");
1653 whereBuffer.append(")");
1654
1655 if (getContentResolver() != null) {
1656 updated = getContentResolver().update(
1657 ProviderTableMeta.CONTENT_URI_FILE,
1658 cv,
1659 whereBuffer.toString(),
1660 ancestorIds.toArray(new String[]{})
1661 );
1662 } else {
1663 try {
1664 updated = getContentProviderClient().update(
1665 ProviderTableMeta.CONTENT_URI_FILE,
1666 cv,
1667 whereBuffer.toString(),
1668 ancestorIds.toArray(new String[]{})
1669 );
1670 } catch (RemoteException e) {
1671 Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage());
1672 }
1673 }
1674 } // else file is ROOT folder, no parent to set in conflict
1675
1676 } else {
1677 /// update conflict in ancestor folders
1678 // (not directly unset; maybe there are more conflicts below them)
1679 String parentPath = file.getRemotePath();
1680 if (parentPath.endsWith(OCFile.PATH_SEPARATOR)) {
1681 parentPath = parentPath.substring(0, parentPath.length() - 1);
1682 }
1683 parentPath = parentPath.substring(0, parentPath.lastIndexOf(OCFile.PATH_SEPARATOR) + 1);
1684
1685 Log_OC.d(TAG, "checking parents to remove conflict; STARTING with " + parentPath);
1686 while (parentPath.length() > 0) {
1687
1688 String where =
1689 ProviderTableMeta.FILE_ETAG_IN_CONFLICT + " IS NOT NULL AND " +
1690 ProviderTableMeta.FILE_CONTENT_TYPE + " != 'DIR' AND " +
1691 ProviderTableMeta.FILE_ACCOUNT_OWNER + " = ? AND " +
1692 ProviderTableMeta.FILE_PATH + " LIKE ?";
1693 Cursor descendentsInConflict = getContentResolver().query(
1694 ProviderTableMeta.CONTENT_URI_FILE,
1695 new String[]{ProviderTableMeta._ID},
1696 where,
1697 new String[]{mAccount.name, parentPath + "%"},
1698 null
1699 );
1700 if (descendentsInConflict == null || descendentsInConflict.getCount() == 0) {
1701 Log_OC.d(TAG, "NO MORE conflicts in " + parentPath);
1702 if (getContentResolver() != null) {
1703 updated = getContentResolver().update(
1704 ProviderTableMeta.CONTENT_URI_FILE,
1705 cv,
1706 ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
1707 ProviderTableMeta.FILE_PATH + "=?",
1708 new String[]{mAccount.name, parentPath}
1709 );
1710 } else {
1711 try {
1712 updated = getContentProviderClient().update(
1713 ProviderTableMeta.CONTENT_URI_FILE,
1714 cv,
1715 ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
1716 ProviderTableMeta.FILE_PATH + "=?"
1717 , new String[]{mAccount.name, parentPath}
1718 );
1719 } catch (RemoteException e) {
1720 Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage());
1721 }
1722 }
1723
1724 } else {
1725 Log_OC.d(TAG, "STILL " + descendentsInConflict.getCount() + " in " + parentPath);
1726 }
1727
1728 if (descendentsInConflict != null) {
1729 descendentsInConflict.close();
1730 }
1731
1732 parentPath = parentPath.substring(0, parentPath.length() - 1); // trim last /
1733 parentPath = parentPath.substring(0, parentPath.lastIndexOf(OCFile.PATH_SEPARATOR) + 1);
1734 Log_OC.d(TAG, "checking parents to remove conflict; NEXT " + parentPath);
1735 }
1736 }
1737 }
1738
1739 }
1740 }