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