Merge branch 'material_buttons' of https://github.com/owncloud/android into material_fab
[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_WITH_SHAREE,
100 ProviderTableMeta.FILE_SHARED_WITH_SHAREE);
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 Uri insertedShareUri = null;
330 long rowId = db.insert(ProviderTableMeta.OCSHARES_TABLE_NAME, null, values);
331 if (rowId >0) {
332 insertedShareUri =
333 ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_SHARE, rowId);
334 } else {
335 throw new SQLException("ERROR " + uri);
336
337 }
338 updateFilesTableAccordingToShareInsertion(db, values);
339 return insertedShareUri;
340
341
342 default:
343 throw new IllegalArgumentException("Unknown uri id: " + uri);
344 }
345
346 }
347
348 private void updateFilesTableAccordingToShareInsertion(
349 SQLiteDatabase db, ContentValues newShare
350 ) {
351 ContentValues fileValues = new ContentValues();
352 int newShareType = newShare.getAsInteger(ProviderTableMeta.OCSHARES_SHARE_TYPE);
353 if (newShareType == ShareType.PUBLIC_LINK.getValue()) {
354 fileValues.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, 1);
355 } else if (newShareType == ShareType.USER.getValue() || newShareType == ShareType.GROUP.getValue()) {
356 fileValues.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, 1);
357 }
358
359 String where = ProviderTableMeta.FILE_PATH + "=? AND " +
360 ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
361 String[] whereArgs = new String[] {
362 newShare.getAsString(ProviderTableMeta.OCSHARES_PATH),
363 newShare.getAsString(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER)
364 };
365 db.update(ProviderTableMeta.FILE_TABLE_NAME, fileValues, where, whereArgs);
366 }
367
368
369 @Override
370 public boolean onCreate() {
371 mDbHelper = new DataBaseHelper(getContext());
372
373 String authority = getContext().getResources().getString(R.string.authority);
374 mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
375 mUriMatcher.addURI(authority, null, ROOT_DIRECTORY);
376 mUriMatcher.addURI(authority, "file/", SINGLE_FILE);
377 mUriMatcher.addURI(authority, "file/#", SINGLE_FILE);
378 mUriMatcher.addURI(authority, "dir/", DIRECTORY);
379 mUriMatcher.addURI(authority, "dir/#", DIRECTORY);
380 mUriMatcher.addURI(authority, "shares/", SHARES);
381 mUriMatcher.addURI(authority, "shares/#", SHARES);
382
383 return true;
384 }
385
386
387 @Override
388 public Cursor query(
389 Uri uri,
390 String[] projection,
391 String selection,
392 String[] selectionArgs,
393 String sortOrder
394 ) {
395
396 Cursor result = null;
397 SQLiteDatabase db = mDbHelper.getReadableDatabase();
398 db.beginTransaction();
399 try {
400 result = query(db, uri, projection, selection, selectionArgs, sortOrder);
401 db.setTransactionSuccessful();
402 } finally {
403 db.endTransaction();
404 }
405 return result;
406 }
407
408 private Cursor query(
409 SQLiteDatabase db,
410 Uri uri,
411 String[] projection,
412 String selection,
413 String[] selectionArgs,
414 String sortOrder
415 ) {
416
417 SQLiteQueryBuilder sqlQuery = new SQLiteQueryBuilder();
418
419 sqlQuery.setTables(ProviderTableMeta.FILE_TABLE_NAME);
420 sqlQuery.setProjectionMap(mFileProjectionMap);
421
422 switch (mUriMatcher.match(uri)) {
423 case ROOT_DIRECTORY:
424 break;
425 case DIRECTORY:
426 String folderId = uri.getPathSegments().get(1);
427 sqlQuery.appendWhere(ProviderTableMeta.FILE_PARENT + "="
428 + folderId);
429 break;
430 case SINGLE_FILE:
431 if (uri.getPathSegments().size() > 1) {
432 sqlQuery.appendWhere(ProviderTableMeta._ID + "="
433 + uri.getPathSegments().get(1));
434 }
435 break;
436 case SHARES:
437 sqlQuery.setTables(ProviderTableMeta.OCSHARES_TABLE_NAME);
438 sqlQuery.setProjectionMap(mOCSharesProjectionMap);
439 if (uri.getPathSegments().size() > 1) {
440 sqlQuery.appendWhere(ProviderTableMeta._ID + "="
441 + uri.getPathSegments().get(1));
442 }
443 break;
444 default:
445 throw new IllegalArgumentException("Unknown uri id: " + uri);
446 }
447
448 String order;
449 if (TextUtils.isEmpty(sortOrder)) {
450 if (mUriMatcher.match(uri) == SHARES) {
451 order = ProviderTableMeta.OCSHARES_DEFAULT_SORT_ORDER;
452 } else {
453
454 order = ProviderTableMeta.FILE_DEFAULT_SORT_ORDER;
455 }
456 } else {
457 order = sortOrder;
458 }
459
460 // DB case_sensitive
461 db.execSQL("PRAGMA case_sensitive_like = true");
462 Cursor c = sqlQuery.query(db, projection, selection, selectionArgs, null, null, order);
463 c.setNotificationUri(getContext().getContentResolver(), uri);
464 return c;
465 }
466
467 @Override
468 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
469
470 int count = 0;
471 SQLiteDatabase db = mDbHelper.getWritableDatabase();
472 db.beginTransaction();
473 try {
474 count = update(db, uri, values, selection, selectionArgs);
475 db.setTransactionSuccessful();
476 } finally {
477 db.endTransaction();
478 }
479 getContext().getContentResolver().notifyChange(uri, null);
480 return count;
481 }
482
483
484
485 private int update(
486 SQLiteDatabase db,
487 Uri uri,
488 ContentValues values,
489 String selection,
490 String[] selectionArgs
491 ) {
492 switch (mUriMatcher.match(uri)) {
493 case DIRECTORY:
494 return 0; //updateFolderSize(db, selectionArgs[0]);
495 case SHARES:
496 return db.update(
497 ProviderTableMeta.OCSHARES_TABLE_NAME, values, selection, selectionArgs
498 );
499 default:
500 return db.update(
501 ProviderTableMeta.FILE_TABLE_NAME, values, selection, selectionArgs
502 );
503 }
504 }
505
506 @Override
507 public ContentProviderResult[] applyBatch (ArrayList<ContentProviderOperation> operations)
508 throws OperationApplicationException {
509 Log_OC.d("FileContentProvider", "applying batch in provider " + this +
510 " (temporary: " + isTemporary() + ")" );
511 ContentProviderResult[] results = new ContentProviderResult[operations.size()];
512 int i=0;
513
514 SQLiteDatabase db = mDbHelper.getWritableDatabase();
515 db.beginTransaction(); // it's supposed that transactions can be nested
516 try {
517 for (ContentProviderOperation operation : operations) {
518 results[i] = operation.apply(this, results, i);
519 i++;
520 }
521 db.setTransactionSuccessful();
522 } finally {
523 db.endTransaction();
524 }
525 Log_OC.d("FileContentProvider", "applied batch in provider " + this);
526 return results;
527 }
528
529
530 class DataBaseHelper extends SQLiteOpenHelper {
531
532 public DataBaseHelper(Context context) {
533 super(context, ProviderMeta.DB_NAME, null, ProviderMeta.DB_VERSION);
534
535 }
536
537 @Override
538 public void onCreate(SQLiteDatabase db) {
539 // files table
540 Log_OC.i("SQL", "Entering in onCreate");
541 db.execSQL("CREATE TABLE " + ProviderTableMeta.FILE_TABLE_NAME + "("
542 + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
543 + ProviderTableMeta.FILE_NAME + " TEXT, "
544 + ProviderTableMeta.FILE_PATH + " TEXT, "
545 + ProviderTableMeta.FILE_PARENT + " INTEGER, "
546 + ProviderTableMeta.FILE_CREATION + " INTEGER, "
547 + ProviderTableMeta.FILE_MODIFIED + " INTEGER, "
548 + ProviderTableMeta.FILE_CONTENT_TYPE + " TEXT, "
549 + ProviderTableMeta.FILE_CONTENT_LENGTH + " INTEGER, "
550 + ProviderTableMeta.FILE_STORAGE_PATH + " TEXT, "
551 + ProviderTableMeta.FILE_ACCOUNT_OWNER + " TEXT, "
552 + ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, "
553 + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, "
554 + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER, "
555 + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER, "
556 + ProviderTableMeta.FILE_ETAG + " TEXT, "
557 + ProviderTableMeta.FILE_SHARED_VIA_LINK + " INTEGER, "
558 + ProviderTableMeta.FILE_PUBLIC_LINK + " TEXT, "
559 + ProviderTableMeta.FILE_PERMISSIONS + " TEXT null,"
560 + ProviderTableMeta.FILE_REMOTE_ID + " TEXT null,"
561 + ProviderTableMeta.FILE_UPDATE_THUMBNAIL + " INTEGER," //boolean
562 + ProviderTableMeta.FILE_IS_DOWNLOADING + " INTEGER," //boolean
563 + ProviderTableMeta.FILE_ETAG_IN_CONFLICT + " TEXT,"
564 + ProviderTableMeta.FILE_SHARED_WITH_SHAREE + " INTEGER);"
565 );
566
567 // Create table ocshares
568 db.execSQL("CREATE TABLE " + ProviderTableMeta.OCSHARES_TABLE_NAME + "("
569 + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
570 + ProviderTableMeta.OCSHARES_FILE_SOURCE + " INTEGER, "
571 + ProviderTableMeta.OCSHARES_ITEM_SOURCE + " INTEGER, "
572 + ProviderTableMeta.OCSHARES_SHARE_TYPE + " INTEGER, "
573 + ProviderTableMeta.OCSHARES_SHARE_WITH + " TEXT, "
574 + ProviderTableMeta.OCSHARES_PATH + " TEXT, "
575 + ProviderTableMeta.OCSHARES_PERMISSIONS+ " INTEGER, "
576 + ProviderTableMeta.OCSHARES_SHARED_DATE + " INTEGER, "
577 + ProviderTableMeta.OCSHARES_EXPIRATION_DATE + " INTEGER, "
578 + ProviderTableMeta.OCSHARES_TOKEN + " TEXT, "
579 + ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME + " TEXT, "
580 + ProviderTableMeta.OCSHARES_IS_DIRECTORY + " INTEGER, " // boolean
581 + ProviderTableMeta.OCSHARES_USER_ID + " INTEGER, "
582 + ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + " INTEGER,"
583 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + " TEXT );" );
584 }
585
586 @Override
587 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
588 Log_OC.i("SQL", "Entering in onUpgrade");
589 boolean upgraded = false;
590 if (oldVersion == 1 && newVersion >= 2) {
591 Log_OC.i("SQL", "Entering in the #1 ADD in onUpgrade");
592 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
593 " ADD COLUMN " + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER " +
594 " DEFAULT 0");
595 upgraded = true;
596 }
597 if (oldVersion < 3 && newVersion >= 3) {
598 Log_OC.i("SQL", "Entering in the #2 ADD in onUpgrade");
599 db.beginTransaction();
600 try {
601 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
602 " ADD COLUMN " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA +
603 " INTEGER " + " DEFAULT 0");
604
605 // assume there are not local changes pending to upload
606 db.execSQL("UPDATE " + ProviderTableMeta.FILE_TABLE_NAME +
607 " SET " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " = "
608 + System.currentTimeMillis() +
609 " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");
610
611 upgraded = true;
612 db.setTransactionSuccessful();
613 } finally {
614 db.endTransaction();
615 }
616 }
617 if (oldVersion < 4 && newVersion >= 4) {
618 Log_OC.i("SQL", "Entering in the #3 ADD in onUpgrade");
619 db.beginTransaction();
620 try {
621 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
622 " ADD COLUMN " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA +
623 " INTEGER " + " DEFAULT 0");
624
625 db.execSQL("UPDATE " + ProviderTableMeta.FILE_TABLE_NAME +
626 " SET " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " = " +
627 ProviderTableMeta.FILE_MODIFIED +
628 " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");
629
630 upgraded = true;
631 db.setTransactionSuccessful();
632 } finally {
633 db.endTransaction();
634 }
635 }
636 if (!upgraded)
637 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
638 ", newVersion == " + newVersion);
639
640 if (oldVersion < 5 && newVersion >= 5) {
641 Log_OC.i("SQL", "Entering in the #4 ADD in onUpgrade");
642 db.beginTransaction();
643 try {
644 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
645 " ADD COLUMN " + ProviderTableMeta.FILE_ETAG + " TEXT " +
646 " DEFAULT NULL");
647
648 upgraded = true;
649 db.setTransactionSuccessful();
650 } finally {
651 db.endTransaction();
652 }
653 }
654 if (!upgraded)
655 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
656 ", newVersion == " + newVersion);
657
658 if (oldVersion < 6 && newVersion >= 6) {
659 Log_OC.i("SQL", "Entering in the #5 ADD in onUpgrade");
660 db.beginTransaction();
661 try {
662 db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
663 " ADD COLUMN " + ProviderTableMeta.FILE_SHARED_VIA_LINK + " INTEGER " +
664 " DEFAULT 0");
665
666 db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
667 " ADD COLUMN " + ProviderTableMeta.FILE_PUBLIC_LINK + " TEXT " +
668 " DEFAULT NULL");
669
670 // Create table ocshares
671 db.execSQL("CREATE TABLE " + ProviderTableMeta.OCSHARES_TABLE_NAME + "("
672 + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
673 + ProviderTableMeta.OCSHARES_FILE_SOURCE + " INTEGER, "
674 + ProviderTableMeta.OCSHARES_ITEM_SOURCE + " INTEGER, "
675 + ProviderTableMeta.OCSHARES_SHARE_TYPE + " INTEGER, "
676 + ProviderTableMeta.OCSHARES_SHARE_WITH + " TEXT, "
677 + ProviderTableMeta.OCSHARES_PATH + " TEXT, "
678 + ProviderTableMeta.OCSHARES_PERMISSIONS + " INTEGER, "
679 + ProviderTableMeta.OCSHARES_SHARED_DATE + " INTEGER, "
680 + ProviderTableMeta.OCSHARES_EXPIRATION_DATE + " INTEGER, "
681 + ProviderTableMeta.OCSHARES_TOKEN + " TEXT, "
682 + ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME + " TEXT, "
683 + ProviderTableMeta.OCSHARES_IS_DIRECTORY + " INTEGER, " // boolean
684 + ProviderTableMeta.OCSHARES_USER_ID + " INTEGER, "
685 + ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + " INTEGER,"
686 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + " TEXT );");
687
688 upgraded = true;
689 db.setTransactionSuccessful();
690 } finally {
691 db.endTransaction();
692 }
693 }
694 if (!upgraded)
695 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
696 ", newVersion == " + newVersion);
697
698 if (oldVersion < 7 && newVersion >= 7) {
699 Log_OC.i("SQL", "Entering in the #7 ADD in onUpgrade");
700 db.beginTransaction();
701 try {
702 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
703 " ADD COLUMN " + ProviderTableMeta.FILE_PERMISSIONS + " TEXT " +
704 " DEFAULT NULL");
705
706 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
707 " ADD COLUMN " + ProviderTableMeta.FILE_REMOTE_ID + " TEXT " +
708 " DEFAULT NULL");
709
710 upgraded = true;
711 db.setTransactionSuccessful();
712 } finally {
713 db.endTransaction();
714 }
715 }
716 if (!upgraded)
717 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
718 ", newVersion == " + newVersion);
719
720 if (oldVersion < 8 && newVersion >= 8) {
721 Log_OC.i("SQL", "Entering in the #8 ADD in onUpgrade");
722 db.beginTransaction();
723 try {
724 db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
725 " ADD COLUMN " + ProviderTableMeta.FILE_UPDATE_THUMBNAIL + " INTEGER " +
726 " DEFAULT 0");
727
728 upgraded = true;
729 db.setTransactionSuccessful();
730 } finally {
731 db.endTransaction();
732 }
733 }
734 if (!upgraded)
735 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
736 ", newVersion == " + newVersion);
737
738 if (oldVersion < 9 && newVersion >= 9) {
739 Log_OC.i("SQL", "Entering in the #9 ADD in onUpgrade");
740 db.beginTransaction();
741 try {
742 db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
743 " ADD COLUMN " + ProviderTableMeta.FILE_IS_DOWNLOADING + " INTEGER " +
744 " DEFAULT 0");
745
746 upgraded = true;
747 db.setTransactionSuccessful();
748 } finally {
749 db.endTransaction();
750 }
751 }
752 if (!upgraded)
753 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
754 ", newVersion == " + newVersion);
755
756 if (oldVersion < 10 && newVersion >= 10) {
757 Log_OC.i("SQL", "Entering in the #10 ADD in onUpgrade");
758 updateAccountName(db);
759 upgraded = true;
760 }
761 if (!upgraded)
762 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
763 ", newVersion == " + newVersion);
764
765 if (oldVersion < 11 && newVersion >= 11) {
766 Log_OC.i("SQL", "Entering in the #11 ADD in onUpgrade");
767 db.beginTransaction();
768 try {
769 db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
770 " ADD COLUMN " + ProviderTableMeta.FILE_ETAG_IN_CONFLICT + " TEXT " +
771 " DEFAULT NULL");
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 < 12 && newVersion >= 12) {
784 Log_OC.i("SQL", "Entering in the #12 ADD in onUpgrade");
785 db.beginTransaction();
786 try {
787 db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
788 " ADD COLUMN " + ProviderTableMeta.FILE_SHARED_WITH_SHAREE + " INTEGER " +
789 " DEFAULT 0");
790 upgraded = true;
791 db.setTransactionSuccessful();
792 } finally {
793 db.endTransaction();
794 }
795 }
796 if (!upgraded)
797 Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
798 ", newVersion == " + newVersion);
799
800 }
801 }
802
803
804 /**
805 * Version 10 of database does not modify its scheme. It coincides with the upgrade of the ownCloud account names
806 * structure to include in it the path to the server instance. Updating the account names and path to local files
807 * in the files table is a must to keep the existing account working and the database clean.
808 *
809 * See {@link com.owncloud.android.authentication.AccountUtils#updateAccountVersion(android.content.Context)}
810 *
811 * @param db Database where table of files is included.
812 */
813 private void updateAccountName(SQLiteDatabase db){
814 Log_OC.d("SQL", "THREAD: "+ Thread.currentThread().getName());
815 AccountManager ama = AccountManager.get(getContext());
816 try {
817 // get accounts from AccountManager ; we can't be sure if accounts in it are updated or not although
818 // we know the update was previously done in {link @FileActivity#onCreate} because the changes through
819 // AccountManager are not synchronous
820 Account[] accounts = AccountManager.get(getContext()).getAccountsByType(
821 MainApp.getAccountType());
822 String serverUrl, username, oldAccountName, newAccountName;
823 for (Account account : accounts) {
824 // build both old and new account name
825 serverUrl = ama.getUserData(account, AccountUtils.Constants.KEY_OC_BASE_URL);
826 username = account.name.substring(0, account.name.lastIndexOf('@'));
827 oldAccountName = AccountUtils.buildAccountNameOld(Uri.parse(serverUrl), username);
828 newAccountName = AccountUtils.buildAccountName(Uri.parse(serverUrl), username);
829
830 // update values in database
831 db.beginTransaction();
832 try {
833 ContentValues cv = new ContentValues();
834 cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, newAccountName);
835 int num = db.update(ProviderTableMeta.FILE_TABLE_NAME,
836 cv,
837 ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
838 new String[]{oldAccountName});
839
840 Log_OC.d("SQL", "Updated account in database: old name == " + oldAccountName +
841 ", new name == " + newAccountName + " (" + num + " rows updated )");
842
843 // update path for downloaded files
844 updateDownloadedFiles(db, newAccountName, oldAccountName);
845
846 db.setTransactionSuccessful();
847
848 } catch (SQLException e) {
849 Log_OC.e(TAG, "SQL Exception upgrading account names or paths in database", e);
850 } finally {
851 db.endTransaction();
852 }
853 }
854 } catch (Exception e) {
855 Log_OC.e(TAG, "Exception upgrading account names or paths in database", e);
856 }
857 }
858
859
860 /**
861 * Rename the local ownCloud folder of one account to match the a rename of the account itself. Updates the
862 * table of files in database so that the paths to the local files keep being the same.
863 *
864 * @param db Database where table of files is included.
865 * @param newAccountName New name for the target OC account.
866 * @param oldAccountName Old name of the target OC account.
867 */
868 private void updateDownloadedFiles(SQLiteDatabase db, String newAccountName,
869 String oldAccountName) {
870
871 String whereClause = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
872 ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL";
873
874 Cursor c = db.query(ProviderTableMeta.FILE_TABLE_NAME,
875 null,
876 whereClause,
877 new String[] { newAccountName },
878 null, null, null);
879
880 try {
881 if (c.moveToFirst()) {
882 // create storage path
883 String oldAccountPath = FileStorageUtils.getSavePath(oldAccountName);
884 String newAccountPath = FileStorageUtils.getSavePath(newAccountName);
885
886 // move files
887 File oldAccountFolder = new File(oldAccountPath);
888 File newAccountFolder = new File(newAccountPath);
889 oldAccountFolder.renameTo(newAccountFolder);
890
891 // update database
892 do {
893 // Update database
894 String oldPath = c.getString(
895 c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH));
896 OCFile file = new OCFile(
897 c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH)));
898 String newPath = FileStorageUtils.getDefaultSavePathFor(newAccountName, file);
899
900 ContentValues cv = new ContentValues();
901 cv.put(ProviderTableMeta.FILE_STORAGE_PATH, newPath);
902 db.update(ProviderTableMeta.FILE_TABLE_NAME,
903 cv,
904 ProviderTableMeta.FILE_STORAGE_PATH + "=?",
905 new String[]{oldPath});
906
907 Log_OC.v("SQL", "Updated path of downloaded file: old file name == " + oldPath +
908 ", new file name == " + newPath);
909
910 } while (c.moveToNext());
911 }
912 } finally {
913 c.close();
914 }
915
916 }
917
918 }