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