Add new string for share_groups_indicator
[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) {
978 Cursor c = null;
979 if (getContentResolver() != null) {
980 c = getContentResolver().query(
981 ProviderTableMeta.CONTENT_URI_SHARE,
982 null,
983 ProviderTableMeta.OCSHARES_PATH + "=? AND "
984 + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? AND "
985 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
986 new String[]{path, Integer.toString(type.getValue()), mAccount.name},
987 null);
988 } else {
989 try {
990 c = getContentProviderClient().query(
991 ProviderTableMeta.CONTENT_URI_SHARE,
992 null,
993 ProviderTableMeta.OCSHARES_PATH + "=? AND "
994 + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? AND "
995 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
996 new String[]{path, Integer.toString(type.getValue()), mAccount.name},
997 null);
998
999 } catch (RemoteException e) {
1000 Log_OC.e(TAG, "Could not get file details: " + e.getMessage());
1001 c = null;
1002 }
1003 }
1004 OCShare share = null;
1005 if (c.moveToFirst()) {
1006 share = createShareInstance(c);
1007 }
1008 c.close();
1009 return share;
1010 }
1011
1012 private OCShare createShareInstance(Cursor c) {
1013 OCShare share = null;
1014 if (c != null) {
1015 share = new OCShare(c.getString(c
1016 .getColumnIndex(ProviderTableMeta.OCSHARES_PATH)));
1017 share.setId(c.getLong(c.getColumnIndex(ProviderTableMeta._ID)));
1018 share.setFileSource(c.getLong(c
1019 .getColumnIndex(ProviderTableMeta.OCSHARES_ITEM_SOURCE)));
1020 share.setShareType(ShareType.fromValue(c.getInt(c
1021 .getColumnIndex(ProviderTableMeta.OCSHARES_SHARE_TYPE))));
1022 share.setPermissions(c.getInt(c
1023 .getColumnIndex(ProviderTableMeta.OCSHARES_PERMISSIONS)));
1024 share.setSharedDate(c.getLong(c
1025 .getColumnIndex(ProviderTableMeta.OCSHARES_SHARED_DATE)));
1026 share.setExpirationDate(c.getLong(c
1027 .getColumnIndex(ProviderTableMeta.OCSHARES_EXPIRATION_DATE)));
1028 share.setToken(c.getString(c
1029 .getColumnIndex(ProviderTableMeta.OCSHARES_TOKEN)));
1030 share.setSharedWithDisplayName(c.getString(c
1031 .getColumnIndex(ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME)));
1032 share.setIsFolder(c.getInt(
1033 c.getColumnIndex(ProviderTableMeta.OCSHARES_IS_DIRECTORY)) == 1);
1034 share.setUserId(c.getLong(c.getColumnIndex(ProviderTableMeta.OCSHARES_USER_ID)));
1035 share.setIdRemoteShared(c.getLong(c.getColumnIndex(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED)));
1036 }
1037 return share;
1038 }
1039
1040 private boolean shareExists(String cmp_key, String value) {
1041 Cursor c;
1042 if (getContentResolver() != null) {
1043 c = getContentResolver()
1044 .query(ProviderTableMeta.CONTENT_URI_SHARE,
1045 null,
1046 cmp_key + "=? AND "
1047 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER
1048 + "=?",
1049 new String[]{value, mAccount.name}, null);
1050 } else {
1051 try {
1052 c = getContentProviderClient().query(
1053 ProviderTableMeta.CONTENT_URI_SHARE,
1054 null,
1055 cmp_key + "=? AND "
1056 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
1057 new String[]{value, mAccount.name}, null);
1058 } catch (RemoteException e) {
1059 Log_OC.e(TAG,
1060 "Couldn't determine file existance, assuming non existance: "
1061 + e.getMessage());
1062 return false;
1063 }
1064 }
1065 boolean retval = c.moveToFirst();
1066 c.close();
1067 return retval;
1068 }
1069
1070 private boolean shareExists(long remoteId) {
1071 return shareExists(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, String.valueOf(remoteId));
1072 }
1073
1074 private void resetShareFlagsInAllFiles() {
1075 ContentValues cv = new ContentValues();
1076 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, false);
1077 cv.put(ProviderTableMeta.FILE_SHARED_VIA_USERS, false);
1078 cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
1079 String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
1080 String[] whereArgs = new String[]{mAccount.name};
1081
1082 if (getContentResolver() != null) {
1083 getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
1084
1085 } else {
1086 try {
1087 getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
1088 } catch (RemoteException e) {
1089 Log_OC.e(TAG, "Exception in resetShareFlagsInAllFiles" + e.getMessage());
1090 }
1091 }
1092 }
1093
1094 private void resetShareFlagsInFolder(OCFile folder) {
1095 ContentValues cv = new ContentValues();
1096 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, false);
1097 cv.put(ProviderTableMeta.FILE_SHARED_VIA_USERS, false);
1098 cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
1099 String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
1100 ProviderTableMeta.FILE_PARENT + "=?";
1101 String [] whereArgs = new String[] { mAccount.name , String.valueOf(folder.getFileId()) };
1102
1103 if (getContentResolver() != null) {
1104 getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
1105
1106 } else {
1107 try {
1108 getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
1109 } catch (RemoteException e) {
1110 Log_OC.e(TAG, "Exception in resetShareFlagsInFolder " + e.getMessage());
1111 }
1112 }
1113 }
1114
1115 private void cleanShares() {
1116 String where = ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
1117 String[] whereArgs = new String[]{mAccount.name};
1118
1119 if (getContentResolver() != null) {
1120 getContentResolver().delete(ProviderTableMeta.CONTENT_URI_SHARE, where, whereArgs);
1121
1122 } else {
1123 try {
1124 getContentProviderClient().delete(ProviderTableMeta.CONTENT_URI_SHARE, where, whereArgs);
1125 } catch (RemoteException e) {
1126 Log_OC.e(TAG, "Exception in cleanShares" + e.getMessage());
1127 }
1128 }
1129 }
1130
1131 public void saveShares(Collection<OCShare> shares) {
1132 cleanShares();
1133 if (shares != null) {
1134 ArrayList<ContentProviderOperation> operations =
1135 new ArrayList<ContentProviderOperation>(shares.size());
1136
1137 // prepare operations to insert or update files to save in the given folder
1138 for (OCShare share : shares) {
1139 ContentValues cv = new ContentValues();
1140 cv.put(ProviderTableMeta.OCSHARES_FILE_SOURCE, share.getFileSource());
1141 cv.put(ProviderTableMeta.OCSHARES_ITEM_SOURCE, share.getItemSource());
1142 cv.put(ProviderTableMeta.OCSHARES_SHARE_TYPE, share.getShareType().getValue());
1143 cv.put(ProviderTableMeta.OCSHARES_SHARE_WITH, share.getShareWith());
1144 cv.put(ProviderTableMeta.OCSHARES_PATH, share.getPath());
1145 cv.put(ProviderTableMeta.OCSHARES_PERMISSIONS, share.getPermissions());
1146 cv.put(ProviderTableMeta.OCSHARES_SHARED_DATE, share.getSharedDate());
1147 cv.put(ProviderTableMeta.OCSHARES_EXPIRATION_DATE, share.getExpirationDate());
1148 cv.put(ProviderTableMeta.OCSHARES_TOKEN, share.getToken());
1149 cv.put(
1150 ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME,
1151 share.getSharedWithDisplayName()
1152 );
1153 cv.put(ProviderTableMeta.OCSHARES_IS_DIRECTORY, share.isFolder() ? 1 : 0);
1154 cv.put(ProviderTableMeta.OCSHARES_USER_ID, share.getUserId());
1155 cv.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, share.getIdRemoteShared());
1156 cv.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, mAccount.name);
1157
1158 if (shareExists(share.getIdRemoteShared())) {
1159 // updating an existing file
1160 operations.add(
1161 ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI_SHARE).
1162 withValues(cv).
1163 withSelection(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
1164 new String[]{String.valueOf(share.getIdRemoteShared())})
1165 .build());
1166 } else {
1167 // adding a new file
1168 operations.add(
1169 ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI_SHARE).
1170 withValues(cv).
1171 build()
1172 );
1173 }
1174 }
1175
1176 // apply operations in batch
1177 if (operations.size() > 0) {
1178 @SuppressWarnings("unused")
1179 ContentProviderResult[] results = null;
1180 Log_OC.d(TAG, "Sending " + operations.size() +
1181 " operations to FileContentProvider");
1182 try {
1183 if (getContentResolver() != null) {
1184 results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
1185 } else {
1186 results = getContentProviderClient().applyBatch(operations);
1187 }
1188
1189 } catch (OperationApplicationException e) {
1190 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1191
1192 } catch (RemoteException e) {
1193 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1194 }
1195 }
1196 }
1197
1198 }
1199
1200 public void updateSharedFiles(Collection<OCFile> sharedFiles) {
1201 resetShareFlagsInAllFiles();
1202
1203 if (sharedFiles != null) {
1204 ArrayList<ContentProviderOperation> operations =
1205 new ArrayList<ContentProviderOperation>(sharedFiles.size());
1206
1207 // prepare operations to insert or update files to save in the given folder
1208 for (OCFile file : sharedFiles) {
1209 ContentValues cv = new ContentValues();
1210 cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
1211 cv.put(
1212 ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,
1213 file.getModificationTimestampAtLastSyncForData()
1214 );
1215 cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
1216 cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
1217 cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
1218 cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
1219 cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
1220 cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
1221 if (!file.isFolder()) {
1222 cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
1223 }
1224 cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
1225 cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
1226 cv.put(
1227 ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA,
1228 file.getLastSyncDateForData()
1229 );
1230 cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isFavorite() ? 1 : 0);
1231 cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
1232 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
1233 cv.put(ProviderTableMeta.FILE_SHARED_VIA_USERS, file.isSharedViaUsers() ? 1 : 0);
1234 cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
1235 cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
1236 cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
1237 cv.put(
1238 ProviderTableMeta.FILE_UPDATE_THUMBNAIL,
1239 file.needsUpdateThumbnail() ? 1 : 0
1240 );
1241 cv.put(
1242 ProviderTableMeta.FILE_IS_DOWNLOADING,
1243 file.isDownloading() ? 1 : 0
1244 );
1245 cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict());
1246
1247 boolean existsByPath = fileExists(file.getRemotePath());
1248 if (existsByPath || fileExists(file.getFileId())) {
1249 // updating an existing file
1250 operations.add(
1251 ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
1252 withValues(cv).
1253 withSelection(ProviderTableMeta._ID + "=?",
1254 new String[]{String.valueOf(file.getFileId())})
1255 .build());
1256
1257 } else {
1258 // adding a new file
1259 operations.add(
1260 ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).
1261 withValues(cv).
1262 build()
1263 );
1264 }
1265 }
1266
1267 // apply operations in batch
1268 if (operations.size() > 0) {
1269 @SuppressWarnings("unused")
1270 ContentProviderResult[] results = null;
1271 Log_OC.d(TAG, "Sending " + operations.size() +
1272 " operations to FileContentProvider");
1273 try {
1274 if (getContentResolver() != null) {
1275 results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
1276 } else {
1277 results = getContentProviderClient().applyBatch(operations);
1278 }
1279
1280 } catch (OperationApplicationException e) {
1281 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1282
1283 } catch (RemoteException e) {
1284 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1285 }
1286 }
1287 }
1288
1289 }
1290
1291 public void removeShare(OCShare share) {
1292 Uri share_uri = ProviderTableMeta.CONTENT_URI_SHARE;
1293 String where = ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?" + " AND " +
1294 ProviderTableMeta.FILE_PATH + "=?";
1295 String [] whereArgs = new String[]{mAccount.name, share.getPath()};
1296 if (getContentProviderClient() != null) {
1297 try {
1298 getContentProviderClient().delete(share_uri, where, whereArgs);
1299 } catch (RemoteException e) {
1300 e.printStackTrace();
1301 }
1302 } else {
1303 getContentResolver().delete(share_uri, where, whereArgs);
1304 }
1305 }
1306
1307 public void saveSharesDB(ArrayList<OCShare> shares) {
1308 saveShares(shares);
1309
1310 ArrayList<OCFile> sharedFiles = new ArrayList<OCFile>();
1311
1312 for (OCShare share : shares) {
1313 // Get the path
1314 String path = share.getPath();
1315 if (share.isFolder()) {
1316 path = path + FileUtils.PATH_SEPARATOR;
1317 }
1318
1319 // Update OCFile with data from share: ShareByLink and publicLink
1320 OCFile file = getFileByPath(path);
1321 if (file != null) {
1322 if (share.getShareType().equals(ShareType.PUBLIC_LINK)) {
1323 file.setShareViaLink(true);
1324 sharedFiles.add(file);
1325 }
1326 }
1327 }
1328
1329 updateSharedFiles(sharedFiles);
1330 }
1331
1332
1333 public void saveSharesInFolder(ArrayList<OCShare> shares, OCFile folder) {
1334 resetShareFlagsInFolder(folder);
1335 ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
1336 operations = prepareRemoveSharesInFolder(folder, operations);
1337
1338 if (shares != null) {
1339 // prepare operations to insert or update files to save in the given folder
1340 for (OCShare share : shares) {
1341 ContentValues cv = new ContentValues();
1342 cv.put(ProviderTableMeta.OCSHARES_FILE_SOURCE, share.getFileSource());
1343 cv.put(ProviderTableMeta.OCSHARES_ITEM_SOURCE, share.getItemSource());
1344 cv.put(ProviderTableMeta.OCSHARES_SHARE_TYPE, share.getShareType().getValue());
1345 cv.put(ProviderTableMeta.OCSHARES_SHARE_WITH, share.getShareWith());
1346 cv.put(ProviderTableMeta.OCSHARES_PATH, share.getPath());
1347 cv.put(ProviderTableMeta.OCSHARES_PERMISSIONS, share.getPermissions());
1348 cv.put(ProviderTableMeta.OCSHARES_SHARED_DATE, share.getSharedDate());
1349 cv.put(ProviderTableMeta.OCSHARES_EXPIRATION_DATE, share.getExpirationDate());
1350 cv.put(ProviderTableMeta.OCSHARES_TOKEN, share.getToken());
1351 cv.put(
1352 ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME,
1353 share.getSharedWithDisplayName()
1354 );
1355 cv.put(ProviderTableMeta.OCSHARES_IS_DIRECTORY, share.isFolder() ? 1 : 0);
1356 cv.put(ProviderTableMeta.OCSHARES_USER_ID, share.getUserId());
1357 cv.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, share.getIdRemoteShared());
1358 cv.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, mAccount.name);
1359
1360 // adding a new share resource
1361 operations.add(
1362 ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI_SHARE).
1363 withValues(cv).
1364 build()
1365 );
1366 }
1367 }
1368
1369 // apply operations in batch
1370 if (operations.size() > 0) {
1371 Log_OC.d(TAG, "Sending " + operations.size() + " operations to FileContentProvider");
1372 try {
1373 if (getContentResolver() != null) {
1374 getContentResolver().applyBatch(MainApp.getAuthority(), operations);
1375
1376 } else {
1377 getContentProviderClient().applyBatch(operations);
1378 }
1379
1380 } catch (OperationApplicationException e) {
1381 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1382
1383 } catch (RemoteException e) {
1384 Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
1385 }
1386 }
1387
1388 }
1389
1390 private ArrayList<ContentProviderOperation> prepareRemoveSharesInFolder(
1391 OCFile folder, ArrayList<ContentProviderOperation> preparedOperations) {
1392 if (folder != null) {
1393 String where = ProviderTableMeta.OCSHARES_PATH + "=?" + " AND "
1394 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
1395 String [] whereArgs = new String[]{ "", mAccount.name };
1396
1397 // TODO Enable when "On Device" is recovered ?
1398 Vector<OCFile> files = getFolderContent(folder /*, false*/);
1399
1400 for (OCFile file : files) {
1401 whereArgs[0] = file.getRemotePath();
1402 preparedOperations.add(
1403 ContentProviderOperation.newDelete(ProviderTableMeta.CONTENT_URI_SHARE).
1404 withSelection(where, whereArgs).
1405 build()
1406 );
1407 }
1408 }
1409 return preparedOperations;
1410 }
1411
1412 public ArrayList<OCShare> getSharesWithForAFile(String filePath, String accountName){
1413 // Condition
1414 String where = ProviderTableMeta.OCSHARES_PATH + "=?" + " AND "
1415 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?"+ "AND"
1416 + " (" + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? OR "
1417 + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? ) ";
1418 String [] whereArgs = new String[]{ filePath, accountName ,
1419 Integer.toString(ShareType.USER.getValue()),
1420 Integer.toString(ShareType.GROUP.getValue()) };
1421
1422 Cursor c = null;
1423 if (getContentResolver() != null) {
1424 c = getContentResolver().query(
1425 ProviderTableMeta.CONTENT_URI_SHARE,
1426 null, where, whereArgs, null);
1427 } else {
1428 try {
1429 c = getContentProviderClient().query(
1430 ProviderTableMeta.CONTENT_URI_SHARE,
1431 null, where, whereArgs, null);
1432
1433 } catch (RemoteException e) {
1434 Log_OC.e(TAG, "Could not get list of shares with: " + e.getMessage());
1435 c = null;
1436 }
1437 }
1438 ArrayList<OCShare> shares = new ArrayList<>();
1439 OCShare share = null;
1440 if (c.moveToFirst()) {
1441 do {
1442 share = createShareInstance(c);
1443 shares.add(share);
1444 // }
1445 } while (c.moveToNext());
1446 }
1447 c.close();
1448
1449 return shares;
1450 }
1451
1452 public void triggerMediaScan(String path) {
1453 Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
1454 intent.setData(Uri.fromFile(new File(path)));
1455 MainApp.getAppContext().sendBroadcast(intent);
1456 }
1457
1458 public void deleteFileInMediaScan(String path) {
1459
1460 String mimetypeString = FileStorageUtils.getMimeTypeFromName(path);
1461 ContentResolver contentResolver = getContentResolver();
1462
1463 if (contentResolver != null) {
1464 if (mimetypeString.startsWith("image/")) {
1465 // Images
1466 contentResolver.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
1467 MediaStore.Images.Media.DATA + "=?", new String[]{path});
1468 } else if (mimetypeString.startsWith("audio/")) {
1469 // Audio
1470 contentResolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
1471 MediaStore.Audio.Media.DATA + "=?", new String[]{path});
1472 } else if (mimetypeString.startsWith("video/")) {
1473 // Video
1474 contentResolver.delete(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
1475 MediaStore.Video.Media.DATA + "=?", new String[]{path});
1476 }
1477 } else {
1478 ContentProviderClient contentProviderClient = getContentProviderClient();
1479 try {
1480 if (mimetypeString.startsWith("image/")) {
1481 // Images
1482 contentProviderClient.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
1483 MediaStore.Images.Media.DATA + "=?", new String[]{path});
1484 } else if (mimetypeString.startsWith("audio/")) {
1485 // Audio
1486 contentProviderClient.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
1487 MediaStore.Audio.Media.DATA + "=?", new String[]{path});
1488 } else if (mimetypeString.startsWith("video/")) {
1489 // Video
1490 contentProviderClient.delete(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
1491 MediaStore.Video.Media.DATA + "=?", new String[]{path});
1492 }
1493 } catch (RemoteException e) {
1494 Log_OC.e(TAG, "Exception deleting media file in MediaStore " + e.getMessage());
1495 }
1496 }
1497
1498 }
1499
1500 public void saveConflict(OCFile file, String etagInConflict) {
1501 if (!file.isDown()) {
1502 etagInConflict = null;
1503 }
1504 ContentValues cv = new ContentValues();
1505 cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, etagInConflict);
1506 int updated = 0;
1507 if (getContentResolver() != null) {
1508 updated = getContentResolver().update(
1509 ProviderTableMeta.CONTENT_URI_FILE,
1510 cv,
1511 ProviderTableMeta._ID + "=?",
1512 new String[] { String.valueOf(file.getFileId())}
1513 );
1514 } else {
1515 try {
1516 updated = getContentProviderClient().update(
1517 ProviderTableMeta.CONTENT_URI_FILE,
1518 cv,
1519 ProviderTableMeta._ID + "=?",
1520 new String[]{String.valueOf(file.getFileId())}
1521 );
1522 } catch (RemoteException e) {
1523 Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage());
1524 }
1525 }
1526
1527 Log_OC.d(TAG, "Number of files updated with CONFLICT: " + updated);
1528
1529 if (updated > 0) {
1530 if (etagInConflict != null) {
1531 /// set conflict in all ancestor folders
1532
1533 long parentId = file.getParentId();
1534 Set<String> ancestorIds = new HashSet<String>();
1535 while (parentId != FileDataStorageManager.ROOT_PARENT_ID) {
1536 ancestorIds.add(Long.toString(parentId));
1537 parentId = getFileById(parentId).getParentId();
1538 }
1539
1540 if (ancestorIds.size() > 0) {
1541 StringBuffer whereBuffer = new StringBuffer();
1542 whereBuffer.append(ProviderTableMeta._ID).append(" IN (");
1543 for (int i = 0; i < ancestorIds.size() - 1; i++) {
1544 whereBuffer.append("?,");
1545 }
1546 whereBuffer.append("?");
1547 whereBuffer.append(")");
1548
1549 if (getContentResolver() != null) {
1550 updated = getContentResolver().update(
1551 ProviderTableMeta.CONTENT_URI_FILE,
1552 cv,
1553 whereBuffer.toString(),
1554 ancestorIds.toArray(new String[]{})
1555 );
1556 } else {
1557 try {
1558 updated = getContentProviderClient().update(
1559 ProviderTableMeta.CONTENT_URI_FILE,
1560 cv,
1561 whereBuffer.toString(),
1562 ancestorIds.toArray(new String[]{})
1563 );
1564 } catch (RemoteException e) {
1565 Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage());
1566 }
1567 }
1568 } // else file is ROOT folder, no parent to set in conflict
1569
1570 } else {
1571 /// update conflict in ancestor folders
1572 // (not directly unset; maybe there are more conflicts below them)
1573 String parentPath = file.getRemotePath();
1574 if (parentPath.endsWith(OCFile.PATH_SEPARATOR)) {
1575 parentPath = parentPath.substring(0, parentPath.length() - 1);
1576 }
1577 parentPath = parentPath.substring(0, parentPath.lastIndexOf(OCFile.PATH_SEPARATOR) + 1);
1578
1579 Log_OC.d(TAG, "checking parents to remove conflict; STARTING with " + parentPath);
1580 while (parentPath.length() > 0) {
1581
1582 String where =
1583 ProviderTableMeta.FILE_ETAG_IN_CONFLICT + " IS NOT NULL AND " +
1584 ProviderTableMeta.FILE_CONTENT_TYPE + " != 'DIR' AND " +
1585 ProviderTableMeta.FILE_ACCOUNT_OWNER + " = ? AND " +
1586 ProviderTableMeta.FILE_PATH + " LIKE ?";
1587 Cursor descendentsInConflict = getContentResolver().query(
1588 ProviderTableMeta.CONTENT_URI_FILE,
1589 new String[]{ProviderTableMeta._ID},
1590 where,
1591 new String[]{mAccount.name, parentPath + "%"},
1592 null
1593 );
1594 if (descendentsInConflict == null || descendentsInConflict.getCount() == 0) {
1595 Log_OC.d(TAG, "NO MORE conflicts in " + parentPath);
1596 if (getContentResolver() != null) {
1597 updated = getContentResolver().update(
1598 ProviderTableMeta.CONTENT_URI_FILE,
1599 cv,
1600 ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
1601 ProviderTableMeta.FILE_PATH + "=?",
1602 new String[]{mAccount.name, parentPath}
1603 );
1604 } else {
1605 try {
1606 updated = getContentProviderClient().update(
1607 ProviderTableMeta.CONTENT_URI_FILE,
1608 cv,
1609 ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
1610 ProviderTableMeta.FILE_PATH + "=?"
1611 , new String[]{mAccount.name, parentPath}
1612 );
1613 } catch (RemoteException e) {
1614 Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage());
1615 }
1616 }
1617
1618 } else {
1619 Log_OC.d(TAG, "STILL " + descendentsInConflict.getCount() + " in " + parentPath);
1620 }
1621
1622 if (descendentsInConflict != null) {
1623 descendentsInConflict.close();
1624 }
1625
1626 parentPath = parentPath.substring(0, parentPath.length() - 1); // trim last /
1627 parentPath = parentPath.substring(0, parentPath.lastIndexOf(OCFile.PATH_SEPARATOR) + 1);
1628 Log_OC.d(TAG, "checking parents to remove conflict; NEXT " + parentPath);
1629 }
1630 }
1631 }
1632
1633 }
1634 }