1 /* ownCloud Android client application 
   2  *   Copyright (C) 2011  Bartek Przybylski 
   3  *   Copyright (C) 2012-2013 ownCloud Inc. 
   5  *   This program is free software: you can redistribute it and/or modify 
   6  *   it under the terms of the GNU General Public License version 2, 
   7  *   as published by the Free Software Foundation. 
   9  *   This program is distributed in the hope that it will be useful, 
  10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of 
  11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  12  *   GNU General Public License for more details. 
  14  *   You should have received a copy of the GNU General Public License 
  15  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  19 package com
.owncloud
.android
.providers
; 
  21 import java
.util
.ArrayList
; 
  22 import java
.util
.HashMap
; 
  24 import com
.owncloud
.android
.R
; 
  25 import com
.owncloud
.android
.db
.ProviderMeta
; 
  26 import com
.owncloud
.android
.db
.ProviderMeta
.ProviderTableMeta
; 
  27 import com
.owncloud
.android
.lib
.common
.utils
.Log_OC
; 
  28 import com
.owncloud
.android
.lib
.resources
.shares
.ShareType
; 
  30 import android
.content
.ContentProvider
; 
  31 import android
.content
.ContentProviderOperation
; 
  32 import android
.content
.ContentProviderResult
; 
  33 import android
.content
.ContentUris
; 
  34 import android
.content
.ContentValues
; 
  35 import android
.content
.Context
; 
  36 import android
.content
.OperationApplicationException
; 
  37 import android
.content
.UriMatcher
; 
  38 import android
.database
.Cursor
; 
  39 import android
.database
.SQLException
; 
  40 import android
.database
.sqlite
.SQLiteDatabase
; 
  41 import android
.database
.sqlite
.SQLiteOpenHelper
; 
  42 import android
.database
.sqlite
.SQLiteQueryBuilder
; 
  43 import android
.net
.Uri
; 
  44 import android
.text
.TextUtils
; 
  47  * The ContentProvider for the ownCloud App. 
  49  * @author Bartek Przybylski 
  50  * @author David A. Velasco 
  53 public class FileContentProvider 
extends ContentProvider 
{ 
  55     private DataBaseHelper mDbHelper
; 
  57     // Projection for filelist table 
  58     private static HashMap
<String
, String
> mFileProjectionMap
; 
  60         mFileProjectionMap 
= new HashMap
<String
, String
>(); 
  61         mFileProjectionMap
.put(ProviderTableMeta
._ID
, ProviderTableMeta
._ID
); 
  62         mFileProjectionMap
.put(ProviderTableMeta
.FILE_PARENT
, 
  63                 ProviderTableMeta
.FILE_PARENT
); 
  64         mFileProjectionMap
.put(ProviderTableMeta
.FILE_PATH
, 
  65                 ProviderTableMeta
.FILE_PATH
); 
  66         mFileProjectionMap
.put(ProviderTableMeta
.FILE_NAME
, 
  67                 ProviderTableMeta
.FILE_NAME
); 
  68         mFileProjectionMap
.put(ProviderTableMeta
.FILE_CREATION
, 
  69                 ProviderTableMeta
.FILE_CREATION
); 
  70         mFileProjectionMap
.put(ProviderTableMeta
.FILE_MODIFIED
, 
  71                 ProviderTableMeta
.FILE_MODIFIED
); 
  72         mFileProjectionMap
.put(ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
, 
  73                 ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
); 
  74         mFileProjectionMap
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, 
  75                 ProviderTableMeta
.FILE_CONTENT_LENGTH
); 
  76         mFileProjectionMap
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, 
  77                 ProviderTableMeta
.FILE_CONTENT_TYPE
); 
  78         mFileProjectionMap
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, 
  79                 ProviderTableMeta
.FILE_STORAGE_PATH
); 
  80         mFileProjectionMap
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, 
  81                 ProviderTableMeta
.FILE_LAST_SYNC_DATE
); 
  82         mFileProjectionMap
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
, 
  83                 ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
); 
  84         mFileProjectionMap
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, 
  85                 ProviderTableMeta
.FILE_KEEP_IN_SYNC
); 
  86         mFileProjectionMap
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, 
  87                 ProviderTableMeta
.FILE_ACCOUNT_OWNER
); 
  88         mFileProjectionMap
.put(ProviderTableMeta
.FILE_ETAG
,  
  89                 ProviderTableMeta
.FILE_ETAG
); 
  90         mFileProjectionMap
.put(ProviderTableMeta
.FILE_SHARE_BY_LINK
, 
  91                 ProviderTableMeta
.FILE_SHARE_BY_LINK
); 
  92         mFileProjectionMap
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, 
  93                 ProviderTableMeta
.FILE_PUBLIC_LINK
); 
  94         mFileProjectionMap
.put(ProviderTableMeta
.FILE_PERMISSIONS
, 
  95                 ProviderTableMeta
.FILE_PERMISSIONS
); 
  96         mFileProjectionMap
.put(ProviderTableMeta
.FILE_REMOTE_ID
, 
  97                 ProviderTableMeta
.FILE_REMOTE_ID
); 
  98         mFileProjectionMap
.put(ProviderTableMeta
.FILE_UPDATE_THUMBNAIL
, 
  99                 ProviderTableMeta
.FILE_UPDATE_THUMBNAIL
); 
 100         mFileProjectionMap
.put(ProviderTableMeta
.FILE_IS_DOWNLOADING
, 
 101                 ProviderTableMeta
.FILE_IS_DOWNLOADING
); 
 104     private static final int SINGLE_FILE 
= 1; 
 105     private static final int DIRECTORY 
= 2; 
 106     private static final int ROOT_DIRECTORY 
= 3; 
 107     private static final int SHARES 
= 4; 
 109     private static final String TAG 
= FileContentProvider
.class.getSimpleName(); 
 111     // Projection for ocshares table 
 112     private static HashMap
<String
, String
> mOCSharesProjectionMap
; 
 114         mOCSharesProjectionMap 
= new HashMap
<String
, String
>(); 
 115         mOCSharesProjectionMap
.put(ProviderTableMeta
._ID
, ProviderTableMeta
._ID
); 
 116         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_FILE_SOURCE
, 
 117                 ProviderTableMeta
.OCSHARES_FILE_SOURCE
); 
 118         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_ITEM_SOURCE
, 
 119                 ProviderTableMeta
.OCSHARES_ITEM_SOURCE
); 
 120         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_SHARE_TYPE
, 
 121                 ProviderTableMeta
.OCSHARES_SHARE_TYPE
); 
 122         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_SHARE_WITH
, 
 123                 ProviderTableMeta
.OCSHARES_SHARE_WITH
); 
 124         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_PATH
, 
 125                 ProviderTableMeta
.OCSHARES_PATH
); 
 126         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_PERMISSIONS
, 
 127                 ProviderTableMeta
.OCSHARES_PERMISSIONS
); 
 128         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_SHARED_DATE
, 
 129                 ProviderTableMeta
.OCSHARES_SHARED_DATE
); 
 130         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
, 
 131                 ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
); 
 132         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_TOKEN
, 
 133                 ProviderTableMeta
.OCSHARES_TOKEN
); 
 134         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
, 
 135                 ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
); 
 136         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_IS_DIRECTORY
, 
 137                 ProviderTableMeta
.OCSHARES_IS_DIRECTORY
); 
 138         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_USER_ID
, 
 139                 ProviderTableMeta
.OCSHARES_USER_ID
); 
 140         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
, 
 141                 ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
); 
 142         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
, 
 143                 ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
); 
 146     private UriMatcher mUriMatcher
; 
 149     public int delete(Uri uri
, String where
, String
[] whereArgs
) { 
 150         //Log_OC.d(TAG, "Deleting " + uri + " at provider " + this); 
 152         SQLiteDatabase db 
= mDbHelper
.getWritableDatabase(); 
 153         db
.beginTransaction(); 
 155             count 
= delete(db
, uri
, where
, whereArgs
); 
 156             db
.setTransactionSuccessful(); 
 160         getContext().getContentResolver().notifyChange(uri
, null
); 
 164     private int delete(SQLiteDatabase db
, Uri uri
, String where
, String
[] whereArgs
) { 
 166         switch (mUriMatcher
.match(uri
)) { 
 168             Cursor c 
= query(db
, uri
, null
, where
, whereArgs
, null
); 
 169             String remoteId 
= ""; 
 170             if (c 
!= null 
&& c
.moveToFirst()) { 
 171                 remoteId 
= c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_REMOTE_ID
)); 
 172                 //ThumbnailsCacheManager.removeFileFromCache(remoteId); 
 174             Log_OC
.d(TAG
, "Removing FILE " + remoteId
); 
 176             count 
= db
.delete(ProviderTableMeta
.FILE_TABLE_NAME
, 
 177                     ProviderTableMeta
._ID
 
 179                             + uri
.getPathSegments().get(1) 
 180                             + (!TextUtils
.isEmpty(where
) ? 
" AND (" + where
 
 181                                     + ")" : ""), whereArgs
); 
 189             // deletion of folder is recursive 
 191             Uri folderUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, Long.parseLong(uri.getPathSegments().get(1))); 
 192             Cursor folder = query(db, folderUri, null, null, null, null); 
 193             String folderName = "(unknown)"; 
 194             if (folder != null && folder.moveToFirst()) { 
 195                 folderName = folder.getString(folder.getColumnIndex(ProviderTableMeta.FILE_PATH)); 
 198             Cursor children 
= query(uri
, null
, null
, null
, null
); 
 199             if (children 
!= null 
&& children
.moveToFirst())  { 
 203                 while (!children
.isAfterLast()) { 
 204                     childId 
= children
.getLong(children
.getColumnIndex(ProviderTableMeta
._ID
)); 
 205                     isDir 
= "DIR".equals(children
.getString( 
 206                             children
.getColumnIndex(ProviderTableMeta
.FILE_CONTENT_TYPE
) 
 208                     //remotePath = children.getString(children.getColumnIndex(ProviderTableMeta.FILE_PATH)); 
 212                             ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_DIR
, childId
),  
 219                             ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_FILE
, childId
), 
 224                     children
.moveToNext(); 
 228                 Log_OC.d(TAG, "No child to remove in DIRECTORY " + folderName); 
 230             Log_OC.d(TAG, "Removing DIRECTORY " + folderName + " (or maybe not) "); 
 232             count 
+= db
.delete(ProviderTableMeta
.FILE_TABLE_NAME
, 
 233                     ProviderTableMeta
._ID
 
 235                     + uri
.getPathSegments().get(1) 
 236                     + (!TextUtils
.isEmpty(where
) ? 
" AND (" + where
 
 237                             + ")" : ""), whereArgs
); 
 239              if (folder != null) { 
 244             //Log_OC.d(TAG, "Removing ROOT!"); 
 245             count 
= db
.delete(ProviderTableMeta
.FILE_TABLE_NAME
, where
, whereArgs
); 
 248             count 
= db
.delete(ProviderTableMeta
.OCSHARES_TABLE_NAME
, where
, whereArgs
); 
 251             //Log_OC.e(TAG, "Unknown uri " + uri); 
 252             throw new IllegalArgumentException("Unknown uri: " + uri
.toString()); 
 258     public String 
getType(Uri uri
) { 
 259         switch (mUriMatcher
.match(uri
)) { 
 261             return ProviderTableMeta
.CONTENT_TYPE
; 
 263             return ProviderTableMeta
.CONTENT_TYPE_ITEM
; 
 265             throw new IllegalArgumentException("Unknown Uri id." 
 271     public Uri 
insert(Uri uri
, ContentValues values
) { 
 273         SQLiteDatabase db 
= mDbHelper
.getWritableDatabase(); 
 274         db
.beginTransaction(); 
 276             newUri 
= insert(db
, uri
, values
); 
 277             db
.setTransactionSuccessful(); 
 281         getContext().getContentResolver().notifyChange(newUri
, null
); 
 285     private Uri 
insert(SQLiteDatabase db
, Uri uri
, ContentValues values
) { 
 286         switch (mUriMatcher
.match(uri
)){ 
 289             String remotePath 
= values
.getAsString(ProviderTableMeta
.FILE_PATH
); 
 290             String accountName 
= values
.getAsString(ProviderTableMeta
.FILE_ACCOUNT_OWNER
); 
 291             String
[] projection 
= new String
[] { 
 292                     ProviderTableMeta
._ID
, ProviderTableMeta
.FILE_PATH
,  
 293                     ProviderTableMeta
.FILE_ACCOUNT_OWNER 
 
 295             String where 
= ProviderTableMeta
.FILE_PATH 
+ "=? AND " +  
 296                     ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=?"; 
 297             String
[] whereArgs 
= new String
[] {remotePath
, accountName
}; 
 298             Cursor doubleCheck 
= query(db
, uri
, projection
, where
, whereArgs
, null
); 
 299             // ugly patch; serious refactorization is needed to reduce work in  
 300             // FileDataStorageManager and bring it to FileContentProvider 
 301             if (doubleCheck 
== null 
|| !doubleCheck
.moveToFirst()) {      
 302                 long rowId 
= db
.insert(ProviderTableMeta
.FILE_TABLE_NAME
, null
, values
); 
 304                     Uri insertedFileUri 
=  
 305                             ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_FILE
, rowId
); 
 306                     return insertedFileUri
; 
 308                     throw new SQLException("ERROR " + uri
); 
 311                 // file is already inserted; race condition, let's avoid a duplicated entry 
 312                 Uri insertedFileUri 
= ContentUris
.withAppendedId( 
 313                         ProviderTableMeta
.CONTENT_URI_FILE
,  
 314                         doubleCheck
.getLong(doubleCheck
.getColumnIndex(ProviderTableMeta
._ID
)) 
 318                 return insertedFileUri
; 
 322             String path 
= values
.getAsString(ProviderTableMeta
.OCSHARES_PATH
); 
 323             String accountNameShare
= values
.getAsString(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
); 
 324             String
[] projectionShare 
= new String
[] { 
 325                     ProviderTableMeta
._ID
, ProviderTableMeta
.OCSHARES_PATH
,  
 326                     ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER 
 
 328             String whereShare 
= ProviderTableMeta
.OCSHARES_PATH 
+ "=? AND " +  
 329                     ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER 
+ "=?"; 
 330             String
[] whereArgsShare 
= new String
[] {path
, accountNameShare
}; 
 331             Uri insertedShareUri 
= null
; 
 332             Cursor doubleCheckShare 
=  
 333                     query(db
, uri
, projectionShare
, whereShare
, whereArgsShare
, null
); 
 334             // ugly patch; serious refactorization is needed to reduce work in  
 335             // FileDataStorageManager and bring it to FileContentProvider 
 336             if (doubleCheckShare 
== null 
|| !doubleCheckShare
.moveToFirst()) {      
 337                 long rowId 
= db
.insert(ProviderTableMeta
.OCSHARES_TABLE_NAME
, null
, values
); 
 340                             ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_SHARE
, rowId
); 
 342                     throw new SQLException("ERROR " + uri
); 
 346                 // file is already inserted; race condition, let's avoid a duplicated entry 
 347                 insertedShareUri 
= ContentUris
.withAppendedId( 
 348                         ProviderTableMeta
.CONTENT_URI_SHARE
,  
 349                         doubleCheckShare
.getLong( 
 350                                 doubleCheckShare
.getColumnIndex(ProviderTableMeta
._ID
) 
 353                 doubleCheckShare
.close(); 
 355             updateFilesTableAccordingToShareInsertion(db
, uri
, values
); 
 356             return insertedShareUri
; 
 360             throw new IllegalArgumentException("Unknown uri id: " + uri
); 
 365     private void updateFilesTableAccordingToShareInsertion( 
 366             SQLiteDatabase db
, Uri uri
, ContentValues shareValues
 
 368         ContentValues fileValues 
= new ContentValues(); 
 370                 ProviderTableMeta
.FILE_SHARE_BY_LINK
,  
 371                 ShareType
.PUBLIC_LINK
.getValue() ==  
 372                     shareValues
.getAsInteger(ProviderTableMeta
.OCSHARES_SHARE_TYPE
)? 
1 : 0 
 374         String whereShare 
= ProviderTableMeta
.FILE_PATH 
+ "=? AND " +  
 375                 ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=?"; 
 376         String
[] whereArgsShare 
= new String
[] { 
 377                 shareValues
.getAsString(ProviderTableMeta
.OCSHARES_PATH
),  
 378                 shareValues
.getAsString(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
) 
 380         db
.update(ProviderTableMeta
.FILE_TABLE_NAME
, fileValues
, whereShare
, whereArgsShare
); 
 385     public boolean onCreate() { 
 386         mDbHelper 
= new DataBaseHelper(getContext()); 
 388         String authority 
= getContext().getResources().getString(R
.string
.authority
); 
 389         mUriMatcher 
= new UriMatcher(UriMatcher
.NO_MATCH
); 
 390         mUriMatcher
.addURI(authority
, null
, ROOT_DIRECTORY
); 
 391         mUriMatcher
.addURI(authority
, "file/", SINGLE_FILE
); 
 392         mUriMatcher
.addURI(authority
, "file/#", SINGLE_FILE
); 
 393         mUriMatcher
.addURI(authority
, "dir/", DIRECTORY
); 
 394         mUriMatcher
.addURI(authority
, "dir/#", DIRECTORY
); 
 395         mUriMatcher
.addURI(authority
, "shares/", SHARES
); 
 396         mUriMatcher
.addURI(authority
, "shares/#", SHARES
); 
 407             String
[] selectionArgs
,  
 411         Cursor result 
= null
; 
 412         SQLiteDatabase db 
= mDbHelper
.getReadableDatabase(); 
 413         db
.beginTransaction(); 
 415             result 
= query(db
, uri
, projection
, selection
, selectionArgs
, sortOrder
); 
 416             db
.setTransactionSuccessful(); 
 423     private Cursor 
query( 
 428             String
[] selectionArgs
,  
 432         SQLiteQueryBuilder sqlQuery 
= new SQLiteQueryBuilder(); 
 434         sqlQuery
.setTables(ProviderTableMeta
.FILE_TABLE_NAME
); 
 435         sqlQuery
.setProjectionMap(mFileProjectionMap
); 
 437         switch (mUriMatcher
.match(uri
)) { 
 441             String folderId 
= uri
.getPathSegments().get(1); 
 442             sqlQuery
.appendWhere(ProviderTableMeta
.FILE_PARENT 
+ "=" 
 446             if (uri
.getPathSegments().size() > 1) { 
 447                 sqlQuery
.appendWhere(ProviderTableMeta
._ID 
+ "=" 
 448                         + uri
.getPathSegments().get(1)); 
 452             sqlQuery
.setTables(ProviderTableMeta
.OCSHARES_TABLE_NAME
); 
 453             sqlQuery
.setProjectionMap(mOCSharesProjectionMap
); 
 454             if (uri
.getPathSegments().size() > 1) { 
 455                 sqlQuery
.appendWhere(ProviderTableMeta
._ID 
+ "=" 
 456                         + uri
.getPathSegments().get(1)); 
 460             throw new IllegalArgumentException("Unknown uri id: " + uri
); 
 464         if (TextUtils
.isEmpty(sortOrder
)) { 
 465             if (mUriMatcher
.match(uri
) == SHARES
) { 
 466                 order 
= ProviderTableMeta
.OCSHARES_DEFAULT_SORT_ORDER
; 
 469                 order 
= ProviderTableMeta
.FILE_DEFAULT_SORT_ORDER
; 
 476         db
.execSQL("PRAGMA case_sensitive_like = true"); 
 477         Cursor c 
= sqlQuery
.query(db
, projection
, selection
, selectionArgs
, null
, null
, order
); 
 478         c
.setNotificationUri(getContext().getContentResolver(), uri
); 
 483     public int update(Uri uri
, ContentValues values
, String selection
, String
[] selectionArgs
) { 
 486         SQLiteDatabase db 
= mDbHelper
.getWritableDatabase(); 
 487         db
.beginTransaction(); 
 489             count 
= update(db
, uri
, values
, selection
, selectionArgs
); 
 490             db
.setTransactionSuccessful(); 
 494         getContext().getContentResolver().notifyChange(uri
, null
); 
 503             ContentValues values
,  
 505             String
[] selectionArgs
 
 507         switch (mUriMatcher
.match(uri
)) { 
 509                 return  0; //updateFolderSize(db, selectionArgs[0]); 
 512                         ProviderTableMeta
.OCSHARES_TABLE_NAME
, values
, selection
, selectionArgs
 
 516                         ProviderTableMeta
.FILE_TABLE_NAME
, values
, selection
, selectionArgs
 
 522     private int updateFolderSize(SQLiteDatabase db, String folderId) { 
 524         String [] whereArgs = new String[] { folderId }; 
 526         // read current size saved for the folder  
 528         long folderParentId = -1; 
 529         Uri selectFolderUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, folderId); 
 530         String[] folderProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH,  ProviderTableMeta.FILE_PARENT}; 
 531         String folderWhere = ProviderTableMeta._ID + "=?"; 
 532         Cursor folderCursor = query(db, selectFolderUri, folderProjection, folderWhere, whereArgs, null); 
 533         if (folderCursor != null && folderCursor.moveToFirst()) { 
 534             folderSize = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH));; 
 535             folderParentId = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_PARENT));; 
 537         folderCursor.close(); 
 539         // read and sum sizes of children 
 540         long childrenSize = 0; 
 541         Uri selectChildrenUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, folderId); 
 542         String[] childrenProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH,  ProviderTableMeta.FILE_PARENT}; 
 543         String childrenWhere = ProviderTableMeta.FILE_PARENT + "=?"; 
 544         Cursor childrenCursor = query(db, selectChildrenUri, childrenProjection, childrenWhere, whereArgs, null); 
 545         if (childrenCursor != null && childrenCursor.moveToFirst()) { 
 546             while (!childrenCursor.isAfterLast()) { 
 547                 childrenSize += childrenCursor.getLong(childrenCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH)); 
 548                 childrenCursor.moveToNext(); 
 551         childrenCursor.close(); 
 554         if (folderSize != childrenSize) { 
 555             Log_OC.d("FileContentProvider", "Updating " + folderSize + " to " + childrenSize); 
 556             ContentValues cv = new ContentValues(); 
 557             cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, childrenSize); 
 558             count = db.update(ProviderTableMeta.FILE_TABLE_NAME, cv, folderWhere, whereArgs); 
 560             // propagate update until root 
 561             if (folderParentId > FileDataStorageManager.ROOT_PARENT_ID) { 
 562                 Log_OC.d("FileContentProvider", "Propagating update to " + folderParentId); 
 563                 updateFolderSize(db, String.valueOf(folderParentId)); 
 565                 Log_OC.d("FileContentProvider", "NOT propagating to " + folderParentId); 
 568             Log_OC.d("FileContentProvider", "NOT updating, sizes are " + folderSize + " and " + childrenSize); 
 575     public ContentProviderResult
[] applyBatch (ArrayList
<ContentProviderOperation
> operations
)  
 576             throws OperationApplicationException 
{ 
 577         Log_OC
.d("FileContentProvider", "applying batch in provider " + this +  
 578                 " (temporary: " + isTemporary() + ")" ); 
 579         ContentProviderResult
[] results 
= new ContentProviderResult
[operations
.size()]; 
 582         SQLiteDatabase db 
= mDbHelper
.getWritableDatabase(); 
 583         db
.beginTransaction();  // it's supposed that transactions can be nested 
 585             for (ContentProviderOperation operation 
: operations
) { 
 586                 results
[i
] = operation
.apply(this, results
, i
); 
 589             db
.setTransactionSuccessful(); 
 593         Log_OC
.d("FileContentProvider", "applied batch in provider " + this); 
 598     class DataBaseHelper 
extends SQLiteOpenHelper 
{ 
 600         public DataBaseHelper(Context context
) { 
 601             super(context
, ProviderMeta
.DB_NAME
, null
, ProviderMeta
.DB_VERSION
); 
 606         public void onCreate(SQLiteDatabase db
) { 
 608             Log_OC
.i("SQL", "Entering in onCreate"); 
 609             db
.execSQL("CREATE TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ "(" 
 610                     + ProviderTableMeta
._ID 
+ " INTEGER PRIMARY KEY, " 
 611                     + ProviderTableMeta
.FILE_NAME 
+ " TEXT, " 
 612                     + ProviderTableMeta
.FILE_PATH 
+ " TEXT, " 
 613                     + ProviderTableMeta
.FILE_PARENT 
+ " INTEGER, " 
 614                     + ProviderTableMeta
.FILE_CREATION 
+ " INTEGER, " 
 615                     + ProviderTableMeta
.FILE_MODIFIED 
+ " INTEGER, " 
 616                     + ProviderTableMeta
.FILE_CONTENT_TYPE 
+ " TEXT, " 
 617                     + ProviderTableMeta
.FILE_CONTENT_LENGTH 
+ " INTEGER, " 
 618                     + ProviderTableMeta
.FILE_STORAGE_PATH 
+ " TEXT, " 
 619                     + ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ " TEXT, " 
 620                     + ProviderTableMeta
.FILE_LAST_SYNC_DATE 
+ " INTEGER, " 
 621                     + ProviderTableMeta
.FILE_KEEP_IN_SYNC 
+ " INTEGER, " 
 622                     + ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA 
+ " INTEGER, " 
 623                     + ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA 
+ " INTEGER, " 
 624                     + ProviderTableMeta
.FILE_ETAG 
+ " TEXT, "  
 625                     + ProviderTableMeta
.FILE_SHARE_BY_LINK 
+ " INTEGER, " 
 626                     + ProviderTableMeta
.FILE_PUBLIC_LINK  
+ " TEXT, " 
 627                     + ProviderTableMeta
.FILE_PERMISSIONS  
+ " TEXT null," 
 628                     + ProviderTableMeta
.FILE_REMOTE_ID  
+ " TEXT null," 
 629                     + ProviderTableMeta
.FILE_UPDATE_THUMBNAIL  
+ " INTEGER," //boolean 
 630                     + ProviderTableMeta
.FILE_IS_DOWNLOADING  
+ " INTEGER);" //boolean 
 633             // Create table ocshares 
 634             db
.execSQL("CREATE TABLE " + ProviderTableMeta
.OCSHARES_TABLE_NAME 
+ "(" 
 635                     + ProviderTableMeta
._ID 
+ " INTEGER PRIMARY KEY, " 
 636                     + ProviderTableMeta
.OCSHARES_FILE_SOURCE 
+ " INTEGER, " 
 637                     + ProviderTableMeta
.OCSHARES_ITEM_SOURCE 
+ " INTEGER, " 
 638                     + ProviderTableMeta
.OCSHARES_SHARE_TYPE 
+ " INTEGER, " 
 639                     + ProviderTableMeta
.OCSHARES_SHARE_WITH 
+ " TEXT, " 
 640                     + ProviderTableMeta
.OCSHARES_PATH 
+ " TEXT, " 
 641                     + ProviderTableMeta
.OCSHARES_PERMISSIONS
+ " INTEGER, " 
 642                     + ProviderTableMeta
.OCSHARES_SHARED_DATE 
+ " INTEGER, " 
 643                     + ProviderTableMeta
.OCSHARES_EXPIRATION_DATE 
+ " INTEGER, " 
 644                     + ProviderTableMeta
.OCSHARES_TOKEN 
+ " TEXT, " 
 645                     + ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME 
+ " TEXT, " 
 646                     + ProviderTableMeta
.OCSHARES_IS_DIRECTORY 
+ " INTEGER, "  // boolean 
 647                     + ProviderTableMeta
.OCSHARES_USER_ID 
+ " INTEGER, " 
 648                     + ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED 
+ " INTEGER,"  
 649                     + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER 
+ " TEXT );" ); 
 653         public void onUpgrade(SQLiteDatabase db
, int oldVersion
, int newVersion
) { 
 654             Log_OC
.i("SQL", "Entering in onUpgrade"); 
 655             boolean upgraded 
= false
;  
 656             if (oldVersion 
== 1 && newVersion 
>= 2) { 
 657                 Log_OC
.i("SQL", "Entering in the #1 ADD in onUpgrade"); 
 658                 db
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 659                            " ADD COLUMN " + ProviderTableMeta
.FILE_KEEP_IN_SYNC  
+ " INTEGER " + 
 663             if (oldVersion 
< 3 && newVersion 
>= 3) { 
 664                 Log_OC
.i("SQL", "Entering in the #2 ADD in onUpgrade"); 
 665                 db
.beginTransaction(); 
 667                     db
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 668                                " ADD COLUMN " + ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA  
+  
 669                                " INTEGER " + " DEFAULT 0"); 
 671                     // assume there are not local changes pending to upload 
 672                     db
.execSQL("UPDATE " + ProviderTableMeta
.FILE_TABLE_NAME 
+  
 673                             " SET " + ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA 
+ " = "  
 674                             + System
.currentTimeMillis() +  
 675                             " WHERE " + ProviderTableMeta
.FILE_STORAGE_PATH 
+ " IS NOT NULL"); 
 678                     db
.setTransactionSuccessful(); 
 683             if (oldVersion 
< 4 && newVersion 
>= 4) { 
 684                 Log_OC
.i("SQL", "Entering in the #3 ADD in onUpgrade"); 
 685                 db
.beginTransaction(); 
 687                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 688                            " ADD COLUMN " + ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA 
+ 
 689                            " INTEGER " + " DEFAULT 0"); 
 691                     db
.execSQL("UPDATE " + ProviderTableMeta
.FILE_TABLE_NAME 
+  
 692                            " SET " + ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA 
+ " = " + 
 693                             ProviderTableMeta
.FILE_MODIFIED 
+ 
 694                            " WHERE " + ProviderTableMeta
.FILE_STORAGE_PATH 
+ " IS NOT NULL"); 
 697                     db
.setTransactionSuccessful(); 
 703                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+  
 704                         ", newVersion == " + newVersion
); 
 706             if (oldVersion 
< 5 && newVersion 
>= 5) { 
 707                 Log_OC
.i("SQL", "Entering in the #4 ADD in onUpgrade"); 
 708                 db
.beginTransaction(); 
 710                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 711                             " ADD COLUMN " + ProviderTableMeta
.FILE_ETAG 
+ " TEXT " + 
 715                     db
.setTransactionSuccessful(); 
 721                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+  
 722                         ", newVersion == " + newVersion
); 
 724             if (oldVersion 
< 6 && newVersion 
>= 6) { 
 725                 Log_OC
.i("SQL", "Entering in the #5 ADD in onUpgrade"); 
 726                 db
.beginTransaction(); 
 728                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 729                             " ADD COLUMN " + ProviderTableMeta
.FILE_SHARE_BY_LINK 
+ " INTEGER " + 
 732                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 733                             " ADD COLUMN " + ProviderTableMeta
.FILE_PUBLIC_LINK 
+ " TEXT " + 
 736                     // Create table ocshares 
 737                     db
.execSQL("CREATE TABLE " + ProviderTableMeta
.OCSHARES_TABLE_NAME 
+ "(" 
 738                             + ProviderTableMeta
._ID 
+ " INTEGER PRIMARY KEY, " 
 739                             + ProviderTableMeta
.OCSHARES_FILE_SOURCE 
+ " INTEGER, " 
 740                             + ProviderTableMeta
.OCSHARES_ITEM_SOURCE 
+ " INTEGER, " 
 741                             + ProviderTableMeta
.OCSHARES_SHARE_TYPE 
+ " INTEGER, " 
 742                             + ProviderTableMeta
.OCSHARES_SHARE_WITH 
+ " TEXT, " 
 743                             + ProviderTableMeta
.OCSHARES_PATH 
+ " TEXT, " 
 744                             + ProviderTableMeta
.OCSHARES_PERMISSIONS
+ " INTEGER, " 
 745                             + ProviderTableMeta
.OCSHARES_SHARED_DATE 
+ " INTEGER, " 
 746                             + ProviderTableMeta
.OCSHARES_EXPIRATION_DATE 
+ " INTEGER, " 
 747                             + ProviderTableMeta
.OCSHARES_TOKEN 
+ " TEXT, " 
 748                             + ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME 
+ " TEXT, " 
 749                             + ProviderTableMeta
.OCSHARES_IS_DIRECTORY 
+ " INTEGER, "  // boolean 
 750                             + ProviderTableMeta
.OCSHARES_USER_ID 
+ " INTEGER, " 
 751                             + ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED 
+ " INTEGER,"  
 752                             + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER 
+ " TEXT );" ); 
 755                     db
.setTransactionSuccessful(); 
 761                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+  
 762                         ", newVersion == " + newVersion
); 
 764             if (oldVersion 
< 7 && newVersion 
>= 7) { 
 765                 Log_OC
.i("SQL", "Entering in the #7 ADD in onUpgrade"); 
 766                 db
.beginTransaction(); 
 768                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 769                             " ADD COLUMN " + ProviderTableMeta
.FILE_PERMISSIONS 
+ " TEXT " + 
 772                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 773                             " ADD COLUMN " + ProviderTableMeta
.FILE_REMOTE_ID 
+ " TEXT " + 
 777                     db
.setTransactionSuccessful(); 
 783                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+  
 784                         ", newVersion == " + newVersion
); 
 786             if (oldVersion 
< 8 && newVersion 
>= 8) { 
 787                 Log_OC
.i("SQL", "Entering in the #8 ADD in onUpgrade"); 
 788                 db
.beginTransaction(); 
 790                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 791                             " ADD COLUMN " + ProviderTableMeta
.FILE_UPDATE_THUMBNAIL 
+ " INTEGER " + 
 795                     db
.setTransactionSuccessful(); 
 801                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+ 
 802                         ", newVersion == " + newVersion
); 
 804             if (oldVersion 
< 9 && newVersion 
>= 9) { 
 805                 Log_OC
.i("SQL", "Entering in the #9 ADD in onUpgrade"); 
 806                 db
.beginTransaction(); 
 808                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 809                             " ADD COLUMN " + ProviderTableMeta
.FILE_IS_DOWNLOADING 
+ " INTEGER " + 
 813                     db
.setTransactionSuccessful(); 
 819                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+ 
 820                         ", newVersion == " + newVersion
);