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