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