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