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