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