Merge tag 'oc-android-1.8' into sdcard-save
[pub/Android/ownCloud.git] / src / com / owncloud / android / providers / FileContentProvider.java
1 /**
2 * ownCloud Android client application
3 *
4 * @author Bartek Przybylski
5 * @author David A. Velasco
6 * Copyright (C) 2011 Bartek Przybylski
7 * Copyright (C) 2015 ownCloud Inc.
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2,
11 * as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23 package com.owncloud.android.providers;
24
25 import java.io.File;
26 import java.security.Provider;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29
30 import com.owncloud.android.MainApp;
31 import com.owncloud.android.R;
32 import com.owncloud.android.datamodel.OCFile;
33 import com.owncloud.android.db.ProviderMeta;
34 import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
35 import com.owncloud.android.lib.common.accounts.AccountUtils;
36 import com.owncloud.android.lib.common.utils.Log_OC;
37 import com.owncloud.android.lib.resources.shares.ShareType;
38 import com.owncloud.android.utils.FileStorageUtils;
39
40 import android.accounts.Account;
41 import android.accounts.AccountManager;
42 import android.content.ContentProvider;
43 import android.content.ContentProviderOperation;
44 import android.content.ContentProviderResult;
45 import android.content.ContentUris;
46 import android.content.ContentValues;
47 import android.content.Context;
48 import android.content.OperationApplicationException;
49 import android.content.UriMatcher;
50 import android.database.Cursor;
51 import android.database.SQLException;
52 import android.database.sqlite.SQLiteDatabase;
53 import android.database.sqlite.SQLiteOpenHelper;
54 import android.database.sqlite.SQLiteQueryBuilder;
55 import android.net.Uri;
56 import android.text.TextUtils;
57
58 /**
59 * The ContentProvider for the ownCloud App.
60 */
61 public class FileContentProvider extends ContentProvider {
62
63 private DataBaseHelper mDbHelper;
64
65 // Projection for filelist table
66 private static HashMap<String, String> mFileProjectionMap;
67 static {
68 mFileProjectionMap = new HashMap<String, String>();
69 mFileProjectionMap.put(ProviderTableMeta._ID, ProviderTableMeta._ID);
70 mFileProjectionMap.put(ProviderTableMeta.FILE_PARENT,
71 ProviderTableMeta.FILE_PARENT);
72 mFileProjectionMap.put(ProviderTableMeta.FILE_PATH,
73 ProviderTableMeta.FILE_PATH);
74 mFileProjectionMap.put(ProviderTableMeta.FILE_NAME,
75 ProviderTableMeta.FILE_NAME);
76 mFileProjectionMap.put(ProviderTableMeta.FILE_CREATION,
77 ProviderTableMeta.FILE_CREATION);
78 mFileProjectionMap.put(ProviderTableMeta.FILE_MODIFIED,
79 ProviderTableMeta.FILE_MODIFIED);
80 mFileProjectionMap.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,
81 ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA);
82 mFileProjectionMap.put(ProviderTableMeta.FILE_CONTENT_LENGTH,
83 ProviderTableMeta.FILE_CONTENT_LENGTH);
84 mFileProjectionMap.put(ProviderTableMeta.FILE_CONTENT_TYPE,
85 ProviderTableMeta.FILE_CONTENT_TYPE);
86 mFileProjectionMap.put(ProviderTableMeta.FILE_STORAGE_PATH,
87 ProviderTableMeta.FILE_STORAGE_PATH);
88 mFileProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE,
89 ProviderTableMeta.FILE_LAST_SYNC_DATE);
90 mFileProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA,
91 ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA);
92 mFileProjectionMap.put(ProviderTableMeta.FILE_KEEP_IN_SYNC,
93 ProviderTableMeta.FILE_KEEP_IN_SYNC);
94 mFileProjectionMap.put(ProviderTableMeta.FILE_ACCOUNT_OWNER,
95 ProviderTableMeta.FILE_ACCOUNT_OWNER);
96 mFileProjectionMap.put(ProviderTableMeta.FILE_ETAG,
97 ProviderTableMeta.FILE_ETAG);
98 mFileProjectionMap.put(ProviderTableMeta.FILE_SHARE_BY_LINK,
99 ProviderTableMeta.FILE_SHARE_BY_LINK);
100 mFileProjectionMap.put(ProviderTableMeta.FILE_PUBLIC_LINK,
101 ProviderTableMeta.FILE_PUBLIC_LINK);
102 mFileProjectionMap.put(ProviderTableMeta.FILE_PERMISSIONS,
103 ProviderTableMeta.FILE_PERMISSIONS);
104 mFileProjectionMap.put(ProviderTableMeta.FILE_REMOTE_ID,
105 ProviderTableMeta.FILE_REMOTE_ID);
106 mFileProjectionMap.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL,
107 ProviderTableMeta.FILE_UPDATE_THUMBNAIL);
108 mFileProjectionMap.put(ProviderTableMeta.FILE_IS_DOWNLOADING,
109 ProviderTableMeta.FILE_IS_DOWNLOADING);
110 }
111
112 private static final int SINGLE_FILE = 1;
113 private static final int DIRECTORY = 2;
114 private static final int ROOT_DIRECTORY = 3;
115 private static final int SHARES = 4;
116
117 private static final String TAG = FileContentProvider.class.getSimpleName();
118
119 // Projection for ocshares table
120 private static HashMap<String, String> mOCSharesProjectionMap;
121 static {
122 mOCSharesProjectionMap = new HashMap<String, String>();
123 mOCSharesProjectionMap.put(ProviderTableMeta._ID, ProviderTableMeta._ID);
124 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_FILE_SOURCE,
125 ProviderTableMeta.OCSHARES_FILE_SOURCE);
126 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_ITEM_SOURCE,
127 ProviderTableMeta.OCSHARES_ITEM_SOURCE);
128 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_SHARE_TYPE,
129 ProviderTableMeta.OCSHARES_SHARE_TYPE);
130 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_SHARE_WITH,
131 ProviderTableMeta.OCSHARES_SHARE_WITH);
132 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_PATH,
133 ProviderTableMeta.OCSHARES_PATH);
134 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_PERMISSIONS,
135 ProviderTableMeta.OCSHARES_PERMISSIONS);
136 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_SHARED_DATE,
137 ProviderTableMeta.OCSHARES_SHARED_DATE);
138 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_EXPIRATION_DATE,
139 ProviderTableMeta.OCSHARES_EXPIRATION_DATE);
140 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_TOKEN,
141 ProviderTableMeta.OCSHARES_TOKEN);
142 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME,
143 ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME);
144 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_IS_DIRECTORY,
145 ProviderTableMeta.OCSHARES_IS_DIRECTORY);
146 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_USER_ID,
147 ProviderTableMeta.OCSHARES_USER_ID);
148 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED,
149 ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED);
150 mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER,
151 ProviderTableMeta.OCSHARES_ACCOUNT_OWNER);
152 }
153
154 private UriMatcher mUriMatcher;
155
156 @Override
157 public int delete(Uri uri, String where, String[] whereArgs) {
158 //Log_OC.d(TAG, "Deleting " + uri + " at provider " + this);
159 int count = 0;
160 SQLiteDatabase db = mDbHelper.getWritableDatabase();
161 db.beginTransaction();
162 try {
163 count = delete(db, uri, where, whereArgs);
164 db.setTransactionSuccessful();
165 } finally {
166 db.endTransaction();
167 }
168 getContext().getContentResolver().notifyChange(uri, null);
169 return count;
170 }
171
172 private int delete(SQLiteDatabase db, Uri uri, String where, String[] whereArgs) {
173 int count = 0;
174 switch (mUriMatcher.match(uri)) {
175 case SINGLE_FILE:
176 Cursor c = query(db, uri, null, where, whereArgs, null);
177 String remoteId = "";
178 if (c != null && c.moveToFirst()) {
179 remoteId = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_REMOTE_ID));
180 //ThumbnailsCacheManager.removeFileFromCache(remoteId);
181 c.close();
182 }
183 Log_OC.d(TAG, "Removing FILE " + remoteId);
184
185 count = db.delete(ProviderTableMeta.FILE_TABLE_NAME,
186 ProviderTableMeta._ID
187 + "="
188 + uri.getPathSegments().get(1)
189 + (!TextUtils.isEmpty(where) ? " AND (" + where
190 + ")" : ""), whereArgs);
191 break;
192 case DIRECTORY:
193 // deletion of folder is recursive
194 /*
195 Uri folderUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, Long.parseLong(uri.getPathSegments().get(1)));
196 Cursor folder = query(db, folderUri, null, null, null, null);
197 String folderName = "(unknown)";
198 if (folder != null && folder.moveToFirst()) {
199 folderName = folder.getString(folder.getColumnIndex(ProviderTableMeta.FILE_PATH));
200 }
201 */
202 Cursor children = query(uri, null, null, null, null);
203 if (children != null && children.moveToFirst()) {
204 long childId;
205 boolean isDir;
206 while (!children.isAfterLast()) {
207 childId = children.getLong(children.getColumnIndex(ProviderTableMeta._ID));
208 isDir = "DIR".equals(children.getString(
209 children.getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)
210 ));
211 //remotePath = children.getString(children.getColumnIndex(ProviderTableMeta.FILE_PATH));
212 if (isDir) {
213 count += delete(
214 db,
215 ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_DIR, childId),
216 null,
217 null
218 );
219 } else {
220 count += delete(
221 db,
222 ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, childId),
223 null,
224 null
225 );
226 }
227 children.moveToNext();
228 }
229 children.close();
230 } /*else {
231 Log_OC.d(TAG, "No child to remove in DIRECTORY " + folderName);
232 }
233 Log_OC.d(TAG, "Removing DIRECTORY " + folderName + " (or maybe not) ");
234 */
235 count += db.delete(ProviderTableMeta.FILE_TABLE_NAME,
236 ProviderTableMeta._ID
237 + "="
238 + uri.getPathSegments().get(1)
239 + (!TextUtils.isEmpty(where) ? " AND (" + where
240 + ")" : ""), whereArgs);
241 /* Just for log
242 if (folder != null) {
243 folder.close();
244 }*/
245 break;
246 case ROOT_DIRECTORY:
247 //Log_OC.d(TAG, "Removing ROOT!");
248 count = db.delete(ProviderTableMeta.FILE_TABLE_NAME, where, whereArgs);
249 break;
250 case SHARES:
251 count = db.delete(ProviderTableMeta.OCSHARES_TABLE_NAME, where, whereArgs);
252 break;
253 default:
254 //Log_OC.e(TAG, "Unknown uri " + uri);
255 throw new IllegalArgumentException("Unknown uri: " + uri.toString());
256 }
257 return count;
258 }
259
260 @Override
261 public String getType(Uri uri) {
262 switch (mUriMatcher.match(uri)) {
263 case ROOT_DIRECTORY:
264 return ProviderTableMeta.CONTENT_TYPE;
265 case SINGLE_FILE:
266 return ProviderTableMeta.CONTENT_TYPE_ITEM;
267 default:
268 throw new IllegalArgumentException("Unknown Uri id."
269 + uri.toString());
270 }
271 }
272
273 @Override
274 public Uri insert(Uri uri, ContentValues values) {
275 Uri newUri = null;
276 SQLiteDatabase db = mDbHelper.getWritableDatabase();
277 db.beginTransaction();
278 try {
279 newUri = insert(db, uri, values);
280 db.setTransactionSuccessful();
281 } finally {
282 db.endTransaction();
283 }
284 getContext().getContentResolver().notifyChange(newUri, null);
285 return newUri;
286 }
287
288 private Uri insert(SQLiteDatabase db, Uri uri, ContentValues values) {
289 switch (mUriMatcher.match(uri)){
290 case ROOT_DIRECTORY:
291 case SINGLE_FILE:
292 String remotePath = values.getAsString(ProviderTableMeta.FILE_PATH);
293 String accountName = values.getAsString(ProviderTableMeta.FILE_ACCOUNT_OWNER);
294 String[] projection = new String[] {
295 ProviderTableMeta._ID, ProviderTableMeta.FILE_PATH,
296 ProviderTableMeta.FILE_ACCOUNT_OWNER
297 };
298 String where = ProviderTableMeta.FILE_PATH + "=? AND " +
299 ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
300 String[] whereArgs = new String[] {remotePath, accountName};
301 Cursor doubleCheck = query(db, uri, projection, where, whereArgs, null);
302 // ugly patch; serious refactorization is needed to reduce work in
303 // FileDataStorageManager and bring it to FileContentProvider
304 if (doubleCheck == null || !doubleCheck.moveToFirst()) {
305 if (doubleCheck != null) {
306 doubleCheck.close();
307 }
308 long rowId = db.insert(ProviderTableMeta.FILE_TABLE_NAME, null, values);
309 if (rowId > 0) {
310 Uri insertedFileUri =
311 ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, rowId);
312 return insertedFileUri;
313 } else {
314 throw new SQLException("ERROR " + uri);
315 }
316 } else {
317 // file is already inserted; race condition, let's avoid a duplicated entry
318 Uri insertedFileUri = ContentUris.withAppendedId(
319 ProviderTableMeta.CONTENT_URI_FILE,
320 doubleCheck.getLong(doubleCheck.getColumnIndex(ProviderTableMeta._ID))
321 );
322 doubleCheck.close();
323
324 return insertedFileUri;
325 }
326
327 case SHARES:
328 String path = values.getAsString(ProviderTableMeta.OCSHARES_PATH);
329 String accountNameShare= values.getAsString(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER);
330 String[] projectionShare = new String[] {
331 ProviderTableMeta._ID, ProviderTableMeta.OCSHARES_PATH,
332 ProviderTableMeta.OCSHARES_ACCOUNT_OWNER
333 };
334 String whereShare = ProviderTableMeta.OCSHARES_PATH + "=? AND " +
335 ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
336 String[] whereArgsShare = new String[] {path, accountNameShare};
337 Uri insertedShareUri = null;
338 Cursor doubleCheckShare =
339 query(db, uri, projectionShare, whereShare, whereArgsShare, null);
340 // ugly patch; serious refactorization is needed to reduce work in
341 // FileDataStorageManager and bring it to FileContentProvider
342 if (doubleCheckShare == null || !doubleCheckShare.moveToFirst()) {
343 if (doubleCheckShare != null) {
344 doubleCheckShare.close();
345 }
346 long rowId = db.insert(ProviderTableMeta.OCSHARES_TABLE_NAME, null, values);
347 if (rowId >0) {
348 insertedShareUri =
349 ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_SHARE, rowId);
350 } else {
351 throw new SQLException("ERROR " + uri);
352
353 }
354 } else {
355 // file is already inserted; race condition, let's avoid a duplicated entry
356 insertedShareUri = ContentUris.withAppendedId(
357 ProviderTableMeta.CONTENT_URI_SHARE,
358 doubleCheckShare.getLong(
359 doubleCheckShare.getColumnIndex(ProviderTableMeta._ID)
360 )
361 );
362 doubleCheckShare.close();
363 }
364 updateFilesTableAccordingToShareInsertion(db, uri, values);
365 return insertedShareUri;
366
367
368 default:
369 throw new IllegalArgumentException("Unknown uri id: " + uri);
370 }
371
372 }
373
374 private void updateFilesTableAccordingToShareInsertion(
375 SQLiteDatabase db, Uri uri, ContentValues shareValues
376 ) {
377 ContentValues fileValues = new ContentValues();
378 fileValues.put(
379 ProviderTableMeta.FILE_SHARE_BY_LINK,
380 ShareType.PUBLIC_LINK.getValue() ==
381 shareValues.getAsInteger(ProviderTableMeta.OCSHARES_SHARE_TYPE) ? 1 : 0
382 );
383 String whereShare = ProviderTableMeta.FILE_PATH + "=? AND " +
384 ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
385 String[] whereArgsShare = new String[] {
386 shareValues.getAsString(ProviderTableMeta.OCSHARES_PATH),
387 shareValues.getAsString(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER)
388 };
389 db.update(ProviderTableMeta.FILE_TABLE_NAME, fileValues, whereShare, whereArgsShare);
390 }
391
392
393 @Override
394 public boolean onCreate() {
395 mDbHelper = new DataBaseHelper(getContext());
396
397 String authority = getContext().getResources().getString(R.string.authority);
398 mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
399 mUriMatcher.addURI(authority, null, ROOT_DIRECTORY);
400 mUriMatcher.addURI(authority, "file/", SINGLE_FILE);
401 mUriMatcher.addURI(authority, "file/#", SINGLE_FILE);
402 mUriMatcher.addURI(authority, "dir/", DIRECTORY);
403 mUriMatcher.addURI(authority, "dir/#", DIRECTORY);
404 mUriMatcher.addURI(authority, "shares/", SHARES);
405 mUriMatcher.addURI(authority, "shares/#", SHARES);
406
407 return true;
408 }
409
410
411 @Override
412 public Cursor query(
413 Uri uri,
414 String[] projection,
415 String selection,
416 String[] selectionArgs,
417 String sortOrder
418 ) {
419
420 Cursor result = null;
421 SQLiteDatabase db = mDbHelper.getReadableDatabase();
422 db.beginTransaction();
423 try {
424 result = query(db, uri, projection, selection, selectionArgs, sortOrder);
425 db.setTransactionSuccessful();
426 } finally {
427 db.endTransaction();
428 }
429 return result;
430 }
431
432 private Cursor query(
433 SQLiteDatabase db,
434 Uri uri,
435 String[] projection,
436 String selection,
437 String[] selectionArgs,
438 String sortOrder
439 ) {
440
441 SQLiteQueryBuilder sqlQuery = new SQLiteQueryBuilder();
442
443 sqlQuery.setTables(ProviderTableMeta.FILE_TABLE_NAME);
444 sqlQuery.setProjectionMap(mFileProjectionMap);
445
446 switch (mUriMatcher.match(uri)) {
447 case ROOT_DIRECTORY:
448 break;
449 case DIRECTORY:
450 String folderId = uri.getPathSegments().get(1);
451 sqlQuery.appendWhere(ProviderTableMeta.FILE_PARENT + "="
452 + folderId);
453 break;
454 case SINGLE_FILE:
455 if (uri.getPathSegments().size() > 1) {
456 sqlQuery.appendWhere(ProviderTableMeta._ID + "="
457 + uri.getPathSegments().get(1));
458 }
459 break;
460 case SHARES:
461 sqlQuery.setTables(ProviderTableMeta.OCSHARES_TABLE_NAME);
462 sqlQuery.setProjectionMap(mOCSharesProjectionMap);
463 if (uri.getPathSegments().size() > 1) {
464 sqlQuery.appendWhere(ProviderTableMeta._ID + "="
465 + uri.getPathSegments().get(1));
466 }
467 break;
468 default:
469 throw new IllegalArgumentException("Unknown uri id: " + uri);
470 }
471
472 String order;
473 if (TextUtils.isEmpty(sortOrder)) {
474 if (mUriMatcher.match(uri) == SHARES) {
475 order = ProviderTableMeta.OCSHARES_DEFAULT_SORT_ORDER;
476 } else {
477
478 order = ProviderTableMeta.FILE_DEFAULT_SORT_ORDER;
479 }
480 } else {
481 order = sortOrder;
482 }
483
484 // DB case_sensitive
485 db.execSQL("PRAGMA case_sensitive_like = true");
486 Cursor c = sqlQuery.query(db, projection, selection, selectionArgs, null, null, order);
487 c.setNotificationUri(getContext().getContentResolver(), uri);
488 return c;
489 }
490
491 @Override
492 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
493
494 int count = 0;
495 SQLiteDatabase db = mDbHelper.getWritableDatabase();
496 db.beginTransaction();
497 try {
498 count = update(db, uri, values, selection, selectionArgs);
499 db.setTransactionSuccessful();
500 } finally {
501 db.endTransaction();
502 }
503 getContext().getContentResolver().notifyChange(uri, null);
504 return count;
505 }
506
507
508
509 private int update(
510 SQLiteDatabase db,
511 Uri uri,
512 ContentValues values,
513 String selection,
514 String[] selectionArgs
515 ) {
516 switch (mUriMatcher.match(uri)) {
517 case DIRECTORY:
518 return 0; //updateFolderSize(db, selectionArgs[0]);
519 case SHARES:
520 return db.update(
521 ProviderTableMeta.OCSHARES_TABLE_NAME, values, selection, selectionArgs
522 );
523 default:
524 return db.update(
525 ProviderTableMeta.FILE_TABLE_NAME, values, selection, selectionArgs
526 );
527 }
528 }
529
530 /*
531 private int updateFolderSize(SQLiteDatabase db, String folderId) {
532 int count = 0;
533 String [] whereArgs = new String[] { folderId };
534
535 // read current size saved for the folder
536 long folderSize = 0;
537 long folderParentId = -1;
538 Uri selectFolderUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, folderId);
539 String[] folderProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH, ProviderTableMeta.FILE_PARENT};
540 String folderWhere = ProviderTableMeta._ID + "=?";
541 Cursor folderCursor = query(db, selectFolderUri, folderProjection, folderWhere, whereArgs, null);
542 if (folderCursor != null && folderCursor.moveToFirst()) {
543 folderSize = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH));;
544 folderParentId = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_PARENT));;
545 }
546 folderCursor.close();
547
548 // read and sum sizes of children
549 long childrenSize = 0;
550 Uri selectChildrenUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, folderId);
551 String[] childrenProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH, ProviderTableMeta.FILE_PARENT};
552 String childrenWhere = ProviderTableMeta.FILE_PARENT + "=?";
553 Cursor childrenCursor = query(db, selectChildrenUri, childrenProjection, childrenWhere, whereArgs, null);
554 if (childrenCursor != null && childrenCursor.moveToFirst()) {
555 while (!childrenCursor.isAfterLast()) {
556 childrenSize += childrenCursor.getLong(childrenCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH));
557 childrenCursor.moveToNext();
558 }
559 }
560 childrenCursor.close();
561
562 // update if needed
563 if (folderSize != childrenSize) {
564 Log_OC.d("FileContentProvider", "Updating " + folderSize + " to " + childrenSize);
565 ContentValues cv = new ContentValues();
566 cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, childrenSize);
567 count = db.update(ProviderTableMeta.FILE_TABLE_NAME, cv, folderWhere, whereArgs);
568
569 // propagate update until root
570 if (folderParentId > FileDataStorageManager.ROOT_PARENT_ID) {
571 Log_OC.d("FileContentProvider", "Propagating update to " + folderParentId);
572 updateFolderSize(db, String.valueOf(folderParentId));
573 } else {
574 Log_OC.d("FileContentProvider", "NOT propagating to " + folderParentId);
575 }
576 } else {
577 Log_OC.d("FileContentProvider", "NOT updating, sizes are " + folderSize + " and " + childrenSize);
578 }
579 return count;
580 }
581 */
582
583 @Override
584 public ContentProviderResult[] applyBatch (ArrayList<ContentProviderOperation> operations)
585 throws OperationApplicationException {
586 Log_OC.d("FileContentProvider", "applying batch in provider " + this +
587 " (temporary: " + isTemporary() + ")" );
588 ContentProviderResult[] results = new ContentProviderResult[operations.size()];
589 int i=0;
590
591 SQLiteDatabase db = mDbHelper.getWritableDatabase();
592 db.beginTransaction(); // it's supposed that transactions can be nested
593 try {
594 for (ContentProviderOperation operation : operations) {
595 results[i] = operation.apply(this, results, i);
596 i++;
597 }
598 db.setTransactionSuccessful();
599 } finally {
600 db.endTransaction();
601 }
602 Log_OC.d("FileContentProvider", "applied batch in provider " + this);
603 return results;
604 }
605
606
607 class DataBaseHelper extends SQLiteOpenHelper {
608
609 public DataBaseHelper(Context context) {
610 super(context, ProviderMeta.DB_NAME, null, ProviderMeta.DB_VERSION);
611
612 }
613
614 @Override
615 public void onCreate(SQLiteDatabase db) {
616 // files table
617 Log_OC.i("SQL", "Entering in onCreate");
618 db.execSQL("CREATE TABLE " + ProviderTableMeta.FILE_TABLE_NAME + "("
619 + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
620 + ProviderTableMeta.FILE_NAME + " TEXT, "
621 + ProviderTableMeta.FILE_PATH + " TEXT, "
622 + ProviderTableMeta.FILE_PARENT + " INTEGER, "
623 + ProviderTableMeta.FILE_CREATION + " INTEGER, "
624 + ProviderTableMeta.FILE_MODIFIED + " INTEGER, "
625 + ProviderTableMeta.FILE_CONTENT_TYPE + " TEXT, "
626 + ProviderTableMeta.FILE_CONTENT_LENGTH + " INTEGER, "
627 + ProviderTableMeta.FILE_STORAGE_PATH + " TEXT, "
628 + ProviderTableMeta.FILE_ACCOUNT_OWNER + " TEXT, "
629 + ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, "
630 + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, "
631 + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER, "
632 + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER, "
633 + ProviderTableMeta.FILE_ETAG + " TEXT, "
634 + ProviderTableMeta.FILE_SHARE_BY_LINK + " INTEGER, "
635 + ProviderTableMeta.FILE_PUBLIC_LINK + " TEXT, "
636 + ProviderTableMeta.FILE_PERMISSIONS + " TEXT null,"
637 + ProviderTableMeta.FILE_REMOTE_ID + " TEXT null,"
638 + ProviderTableMeta.FILE_UPDATE_THUMBNAIL + " INTEGER," //boolean
639 + ProviderTableMeta.FILE_IS_DOWNLOADING + " INTEGER);" //boolean
640 );
641
642 // Create table ocshares
643 db.execSQL("CREATE TABLE " + ProviderTableMeta.OCSHARES_TABLE_NAME + "("
644 + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
645 + ProviderTableMeta.OCSHARES_FILE_SOURCE + " INTEGER, "
646 + ProviderTableMeta.OCSHARES_ITEM_SOURCE + " INTEGER, "
647 + ProviderTableMeta.OCSHARES_SHARE_TYPE + " INTEGER, "
648 + ProviderTableMeta.OCSHARES_SHARE_WITH + " TEXT, "
649 + ProviderTableMeta.OCSHARES_PATH + " TEXT, "
650 + ProviderTableMeta.OCSHARES_PERMISSIONS+ " INTEGER, "
651 + ProviderTableMeta.OCSHARES_SHARED_DATE + " INTEGER, "
652 + ProviderTableMeta.OCSHARES_EXPIRATION_DATE + " INTEGER, "
653 + ProviderTableMeta.OCSHARES_TOKEN + " TEXT, "
654 + ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME + " TEXT, "
655 + ProviderTableMeta.OCSHARES_IS_DIRECTORY + " INTEGER, " // boolean
656 + ProviderTableMeta.OCSHARES_USER_ID + " INTEGER, "
657 + ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + " INTEGER,"
658 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + " TEXT );" );
659 }
660
661 @Override
662 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
663 Log_OC.i("SQL", "Entering in onUpgrade");
664 boolean upgraded = false;
665 if (oldVersion == 1 && newVersion >= 2) {
666 Log_OC.i("SQL", "Entering in the #1 ADD in onUpgrade");
667 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
668 " ADD COLUMN " + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER " +
669 " DEFAULT 0");
670 upgraded = true;
671 }
672 if (oldVersion < 3 && newVersion >= 3) {
673 Log_OC.i("SQL", "Entering in the #2 ADD in onUpgrade");
674 db.beginTransaction();
675 try {
676 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
677 " ADD COLUMN " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA +
678 " INTEGER " + " DEFAULT 0");
679
680 // assume there are not local changes pending to upload
681 db.execSQL("UPDATE " + ProviderTableMeta.FILE_TABLE_NAME +
682 " SET " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " = "
683 + System.currentTimeMillis() +
684 " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");
685
686 upgraded = true;
687 db.setTransactionSuccessful();
688 } finally {
689 db.endTransaction();
690 }
691 }
692 if (oldVersion < 4 && newVersion >= 4) {
693 Log_OC.i("SQL", "Entering in the #3 ADD in onUpgrade");
694 db.beginTransaction();
695 try {
696 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
697 " ADD COLUMN " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA +
698 " INTEGER " + " DEFAULT 0");
699
700 db.execSQL("UPDATE " + ProviderTableMeta.FILE_TABLE_NAME +
701 " SET " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " = " +
702 ProviderTableMeta.FILE_MODIFIED +
703 " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");
704
705 upgraded = true;
706 db.setTransactionSuccessful();
707 } finally {
708 db.endTransaction();
709 }
710 }
711 if (!upgraded)
712 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
713 ", newVersion == " + newVersion);
714
715 if (oldVersion < 5 && newVersion >= 5) {
716 Log_OC.i("SQL", "Entering in the #4 ADD in onUpgrade");
717 db.beginTransaction();
718 try {
719 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
720 " ADD COLUMN " + ProviderTableMeta.FILE_ETAG + " TEXT " +
721 " DEFAULT NULL");
722
723 upgraded = true;
724 db.setTransactionSuccessful();
725 } finally {
726 db.endTransaction();
727 }
728 }
729 if (!upgraded)
730 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
731 ", newVersion == " + newVersion);
732
733 if (oldVersion < 6 && newVersion >= 6) {
734 Log_OC.i("SQL", "Entering in the #5 ADD in onUpgrade");
735 db.beginTransaction();
736 try {
737 db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
738 " ADD COLUMN " + ProviderTableMeta.FILE_SHARE_BY_LINK + " INTEGER " +
739 " DEFAULT 0");
740
741 db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
742 " ADD COLUMN " + ProviderTableMeta.FILE_PUBLIC_LINK + " TEXT " +
743 " DEFAULT NULL");
744
745 // Create table ocshares
746 db.execSQL("CREATE TABLE " + ProviderTableMeta.OCSHARES_TABLE_NAME + "("
747 + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
748 + ProviderTableMeta.OCSHARES_FILE_SOURCE + " INTEGER, "
749 + ProviderTableMeta.OCSHARES_ITEM_SOURCE + " INTEGER, "
750 + ProviderTableMeta.OCSHARES_SHARE_TYPE + " INTEGER, "
751 + ProviderTableMeta.OCSHARES_SHARE_WITH + " TEXT, "
752 + ProviderTableMeta.OCSHARES_PATH + " TEXT, "
753 + ProviderTableMeta.OCSHARES_PERMISSIONS + " INTEGER, "
754 + ProviderTableMeta.OCSHARES_SHARED_DATE + " INTEGER, "
755 + ProviderTableMeta.OCSHARES_EXPIRATION_DATE + " INTEGER, "
756 + ProviderTableMeta.OCSHARES_TOKEN + " TEXT, "
757 + ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME + " TEXT, "
758 + ProviderTableMeta.OCSHARES_IS_DIRECTORY + " INTEGER, " // boolean
759 + ProviderTableMeta.OCSHARES_USER_ID + " INTEGER, "
760 + ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + " INTEGER,"
761 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + " TEXT );");
762
763 upgraded = true;
764 db.setTransactionSuccessful();
765 } finally {
766 db.endTransaction();
767 }
768 }
769 if (!upgraded)
770 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
771 ", newVersion == " + newVersion);
772
773 if (oldVersion < 7 && newVersion >= 7) {
774 Log_OC.i("SQL", "Entering in the #7 ADD in onUpgrade");
775 db.beginTransaction();
776 try {
777 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
778 " ADD COLUMN " + ProviderTableMeta.FILE_PERMISSIONS + " TEXT " +
779 " DEFAULT NULL");
780
781 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
782 " ADD COLUMN " + ProviderTableMeta.FILE_REMOTE_ID + " TEXT " +
783 " DEFAULT NULL");
784
785 upgraded = true;
786 db.setTransactionSuccessful();
787 } finally {
788 db.endTransaction();
789 }
790 }
791 if (!upgraded)
792 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
793 ", newVersion == " + newVersion);
794
795 if (oldVersion < 8 && newVersion >= 8) {
796 Log_OC.i("SQL", "Entering in the #8 ADD in onUpgrade");
797 db.beginTransaction();
798 try {
799 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
800 " ADD COLUMN " + ProviderTableMeta.FILE_UPDATE_THUMBNAIL + " INTEGER " +
801 " DEFAULT 0");
802
803 upgraded = true;
804 db.setTransactionSuccessful();
805 } finally {
806 db.endTransaction();
807 }
808 }
809 if (!upgraded)
810 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
811 ", newVersion == " + newVersion);
812
813 if (oldVersion < 9 && newVersion >= 9) {
814 Log_OC.i("SQL", "Entering in the #9 ADD in onUpgrade");
815 db.beginTransaction();
816 try {
817 db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
818 " ADD COLUMN " + ProviderTableMeta.FILE_IS_DOWNLOADING + " INTEGER " +
819 " DEFAULT 0");
820
821 upgraded = true;
822 db.setTransactionSuccessful();
823 } finally {
824 db.endTransaction();
825 }
826 }
827 if (!upgraded)
828 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
829 ", newVersion == " + newVersion);
830
831 if (oldVersion < 10 && newVersion >= 10) {
832 Log_OC.i("SQL", "Entering in the #10 ADD in onUpgrade");
833 updateAccountName(db);
834 upgraded = true;
835 }
836 if (!upgraded)
837 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
838 ", newVersion == " + newVersion);
839 }
840 }
841
842
843 /**
844 * Version 10 of database does not modify its scheme. It coincides with the upgrade of the ownCloud account names
845 * structure to include in it the path to the server instance. Updating the account names and path to local files
846 * in the files table is a must to keep the existing account working and the database clean.
847 *
848 * See {@link com.owncloud.android.authentication.AccountUtils#updateAccountVersion(android.content.Context)}
849 *
850 * @param db Database where table of files is included.
851 */
852 private void updateAccountName(SQLiteDatabase db){
853 Log_OC.d("SQL", "THREAD: "+ Thread.currentThread().getName());
854 AccountManager ama = AccountManager.get(getContext());
855 try {
856 // get accounts from AccountManager ; we can't be sure if accounts in it are updated or not although
857 // we know the update was previously done in {link @FileActivity#onCreate} because the changes through
858 // AccountManager are not synchronous
859 Account[] accounts = AccountManager.get(getContext()).getAccountsByType(
860 MainApp.getAccountType());
861 String serverUrl, username, oldAccountName, newAccountName;
862 for (Account account : accounts) {
863 // build both old and new account name
864 serverUrl = ama.getUserData(account, AccountUtils.Constants.KEY_OC_BASE_URL);
865 username = account.name.substring(0, account.name.lastIndexOf('@'));
866 oldAccountName = AccountUtils.buildAccountNameOld(Uri.parse(serverUrl), username);
867 newAccountName = AccountUtils.buildAccountName(Uri.parse(serverUrl), username);
868
869 // update values in database
870 db.beginTransaction();
871 try {
872 ContentValues cv = new ContentValues();
873 cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, newAccountName);
874 int num = db.update(ProviderTableMeta.FILE_TABLE_NAME,
875 cv,
876 ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
877 new String[]{oldAccountName});
878
879 Log_OC.d("SQL", "Updated account in database: old name == " + oldAccountName +
880 ", new name == " + newAccountName + " (" + num + " rows updated )");
881
882 // update path for downloaded files
883 updateDownloadedFiles(db, newAccountName, oldAccountName);
884
885 db.setTransactionSuccessful();
886
887 } catch (SQLException e) {
888 Log_OC.e(TAG, "SQL Exception upgrading account names or paths in database", e);
889 } finally {
890 db.endTransaction();
891 }
892 }
893 } catch (Exception e) {
894 Log_OC.e(TAG, "Exception upgrading account names or paths in database", e);
895 }
896 }
897
898
899 /**
900 * Rename the local ownCloud folder of one account to match the a rename of the account itself. Updates the
901 * table of files in database so that the paths to the local files keep being the same.
902 *
903 * @param db Database where table of files is included.
904 * @param newAccountName New name for the target OC account.
905 * @param oldAccountName Old name of the target OC account.
906 */
907 private void updateDownloadedFiles(SQLiteDatabase db, String newAccountName,
908 String oldAccountName) {
909
910 String whereClause = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
911 ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL";
912
913 Cursor c = db.query(ProviderTableMeta.FILE_TABLE_NAME,
914 null,
915 whereClause,
916 new String[] { newAccountName },
917 null, null, null);
918
919 try {
920 if (c.moveToFirst()) {
921 // create storage path
922 String oldAccountPath = FileStorageUtils.getSavePath(oldAccountName);
923 String newAccountPath = FileStorageUtils.getSavePath(newAccountName);
924
925 // move files
926 File oldAccountFolder = new File(oldAccountPath);
927 File newAccountFolder = new File(newAccountPath);
928 oldAccountFolder.renameTo(newAccountFolder);
929
930 // update database
931 do {
932 // Update database
933 String oldPath = c.getString(
934 c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH));
935 OCFile file = new OCFile(
936 c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH)));
937 String newPath = FileStorageUtils.getDefaultSavePathFor(newAccountName, file);
938
939 ContentValues cv = new ContentValues();
940 cv.put(ProviderTableMeta.FILE_STORAGE_PATH, newPath);
941 db.update(ProviderTableMeta.FILE_TABLE_NAME,
942 cv,
943 ProviderTableMeta.FILE_STORAGE_PATH + "=?",
944 new String[]{oldPath});
945
946 Log_OC.v("SQL", "Updated path of downloaded file: old file name == " + oldPath +
947 ", new file name == " + newPath);
948
949 } while (c.moveToNext());
950 }
951 } finally {
952 c.close();
953 }
954
955 }
956
957 }