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
.datamodel
.ThumbnailsCacheManager
; 
  26 import com
.owncloud
.android
.db
.ProviderMeta
; 
  27 import com
.owncloud
.android
.db
.ProviderMeta
.ProviderTableMeta
; 
  28 import com
.owncloud
.android
.lib
.common
.utils
.Log_OC
; 
  29 import com
.owncloud
.android
.lib
.resources
.shares
.ShareType
; 
  31 import android
.content
.ContentProvider
; 
  32 import android
.content
.ContentProviderOperation
; 
  33 import android
.content
.ContentProviderResult
; 
  34 import android
.content
.ContentUris
; 
  35 import android
.content
.ContentValues
; 
  36 import android
.content
.Context
; 
  37 import android
.content
.OperationApplicationException
; 
  38 import android
.content
.UriMatcher
; 
  39 import android
.database
.Cursor
; 
  40 import android
.database
.SQLException
; 
  41 import android
.database
.sqlite
.SQLiteDatabase
; 
  42 import android
.database
.sqlite
.SQLiteOpenHelper
; 
  43 import android
.database
.sqlite
.SQLiteQueryBuilder
; 
  44 import android
.net
.Uri
; 
  45 import android
.text
.TextUtils
; 
  48  * The ContentProvider for the ownCloud App. 
  50  * @author Bartek Przybylski 
  51  * @author David A. Velasco 
  54 public class FileContentProvider 
extends ContentProvider 
{ 
  56     private DataBaseHelper mDbHelper
; 
  58     // Projection for filelist table 
  59     private static HashMap
<String
, String
> mFileProjectionMap
; 
  61         mFileProjectionMap 
= new HashMap
<String
, String
>(); 
  62         mFileProjectionMap
.put(ProviderTableMeta
._ID
, ProviderTableMeta
._ID
); 
  63         mFileProjectionMap
.put(ProviderTableMeta
.FILE_PARENT
, 
  64                 ProviderTableMeta
.FILE_PARENT
); 
  65         mFileProjectionMap
.put(ProviderTableMeta
.FILE_PATH
, 
  66                 ProviderTableMeta
.FILE_PATH
); 
  67         mFileProjectionMap
.put(ProviderTableMeta
.FILE_NAME
, 
  68                 ProviderTableMeta
.FILE_NAME
); 
  69         mFileProjectionMap
.put(ProviderTableMeta
.FILE_CREATION
, 
  70                 ProviderTableMeta
.FILE_CREATION
); 
  71         mFileProjectionMap
.put(ProviderTableMeta
.FILE_MODIFIED
, 
  72                 ProviderTableMeta
.FILE_MODIFIED
); 
  73         mFileProjectionMap
.put(ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
, 
  74                 ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
); 
  75         mFileProjectionMap
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, 
  76                 ProviderTableMeta
.FILE_CONTENT_LENGTH
); 
  77         mFileProjectionMap
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, 
  78                 ProviderTableMeta
.FILE_CONTENT_TYPE
); 
  79         mFileProjectionMap
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, 
  80                 ProviderTableMeta
.FILE_STORAGE_PATH
); 
  81         mFileProjectionMap
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, 
  82                 ProviderTableMeta
.FILE_LAST_SYNC_DATE
); 
  83         mFileProjectionMap
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
, 
  84                 ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
); 
  85         mFileProjectionMap
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, 
  86                 ProviderTableMeta
.FILE_KEEP_IN_SYNC
); 
  87         mFileProjectionMap
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, 
  88                 ProviderTableMeta
.FILE_ACCOUNT_OWNER
); 
  89         mFileProjectionMap
.put(ProviderTableMeta
.FILE_ETAG
,  
  90                 ProviderTableMeta
.FILE_ETAG
); 
  91         mFileProjectionMap
.put(ProviderTableMeta
.FILE_SHARE_BY_LINK
, 
  92                 ProviderTableMeta
.FILE_SHARE_BY_LINK
); 
  93         mFileProjectionMap
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, 
  94                 ProviderTableMeta
.FILE_PUBLIC_LINK
); 
  95         mFileProjectionMap
.put(ProviderTableMeta
.FILE_PERMISSIONS
, 
  96                 ProviderTableMeta
.FILE_PERMISSIONS
); 
  97         mFileProjectionMap
.put(ProviderTableMeta
.FILE_REMOTE_ID
, 
  98                 ProviderTableMeta
.FILE_REMOTE_ID
); 
  99         mFileProjectionMap
.put(ProviderTableMeta
.FILE_UPDATE_THUMBNAIL
, 
 100                 ProviderTableMeta
.FILE_UPDATE_THUMBNAIL
); 
 103     private static final int SINGLE_FILE 
= 1; 
 104     private static final int DIRECTORY 
= 2; 
 105     private static final int ROOT_DIRECTORY 
= 3; 
 106     private static final int SHARES 
= 4; 
 108     private static final String TAG 
= FileContentProvider
.class.getSimpleName(); 
 110     // Projection for ocshares table 
 111     private static HashMap
<String
, String
> mOCSharesProjectionMap
; 
 113         mOCSharesProjectionMap 
= new HashMap
<String
, String
>(); 
 114         mOCSharesProjectionMap
.put(ProviderTableMeta
._ID
, ProviderTableMeta
._ID
); 
 115         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_FILE_SOURCE
, 
 116                 ProviderTableMeta
.OCSHARES_FILE_SOURCE
); 
 117         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_ITEM_SOURCE
, 
 118                 ProviderTableMeta
.OCSHARES_ITEM_SOURCE
); 
 119         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_SHARE_TYPE
, 
 120                 ProviderTableMeta
.OCSHARES_SHARE_TYPE
); 
 121         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_SHARE_WITH
, 
 122                 ProviderTableMeta
.OCSHARES_SHARE_WITH
); 
 123         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_PATH
, 
 124                 ProviderTableMeta
.OCSHARES_PATH
); 
 125         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_PERMISSIONS
, 
 126                 ProviderTableMeta
.OCSHARES_PERMISSIONS
); 
 127         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_SHARED_DATE
, 
 128                 ProviderTableMeta
.OCSHARES_SHARED_DATE
); 
 129         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
, 
 130                 ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
); 
 131         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_TOKEN
, 
 132                 ProviderTableMeta
.OCSHARES_TOKEN
); 
 133         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
, 
 134                 ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
); 
 135         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_IS_DIRECTORY
, 
 136                 ProviderTableMeta
.OCSHARES_IS_DIRECTORY
); 
 137         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_USER_ID
, 
 138                 ProviderTableMeta
.OCSHARES_USER_ID
); 
 139         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
, 
 140                 ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
); 
 141         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
, 
 142                 ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
); 
 145     private UriMatcher mUriMatcher
; 
 148     public int delete(Uri uri
, String where
, String
[] whereArgs
) { 
 149         //Log_OC.d(TAG, "Deleting " + uri + " at provider " + this); 
 151         SQLiteDatabase db 
= mDbHelper
.getWritableDatabase(); 
 152         db
.beginTransaction(); 
 154             count 
= delete(db
, uri
, where
, whereArgs
); 
 155             db
.setTransactionSuccessful(); 
 159         getContext().getContentResolver().notifyChange(uri
, null
); 
 163     private int delete(SQLiteDatabase db
, Uri uri
, String where
, String
[] whereArgs
) { 
 165         switch (mUriMatcher
.match(uri
)) { 
 167             Cursor c 
= query(db
, uri
, null
, where
, whereArgs
, null
); 
 168             String remoteId 
= ""; 
 169             if (c 
!= null 
&& c
.moveToFirst()) { 
 170                 remoteId 
= c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_REMOTE_ID
)); 
 171                 //ThumbnailsCacheManager.removeFileFromCache(remoteId); 
 173             Log_OC
.d(TAG
, "Removing FILE " + remoteId
); 
 175             count 
= db
.delete(ProviderTableMeta
.FILE_TABLE_NAME
, 
 176                     ProviderTableMeta
._ID
 
 178                             + uri
.getPathSegments().get(1) 
 179                             + (!TextUtils
.isEmpty(where
) ? 
" AND (" + where
 
 180                                     + ")" : ""), whereArgs
); 
 188             // deletion of folder is recursive 
 190             Uri folderUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, Long.parseLong(uri.getPathSegments().get(1))); 
 191             Cursor folder = query(db, folderUri, null, null, null, null); 
 192             String folderName = "(unknown)"; 
 193             if (folder != null && folder.moveToFirst()) { 
 194                 folderName = folder.getString(folder.getColumnIndex(ProviderTableMeta.FILE_PATH)); 
 197             Cursor children 
= query(uri
, null
, null
, null
, null
); 
 198             if (children 
!= null 
&& children
.moveToFirst())  { 
 202                 while (!children
.isAfterLast()) { 
 203                     childId 
= children
.getLong(children
.getColumnIndex(ProviderTableMeta
._ID
)); 
 204                     isDir 
= "DIR".equals(children
.getString( 
 205                             children
.getColumnIndex(ProviderTableMeta
.FILE_CONTENT_TYPE
) 
 207                     //remotePath = children.getString(children.getColumnIndex(ProviderTableMeta.FILE_PATH)); 
 211                             ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_DIR
, childId
),  
 218                             ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_FILE
, childId
), 
 223                     children
.moveToNext(); 
 227                 Log_OC.d(TAG, "No child to remove in DIRECTORY " + folderName); 
 229             Log_OC.d(TAG, "Removing DIRECTORY " + folderName + " (or maybe not) "); 
 231             count 
+= db
.delete(ProviderTableMeta
.FILE_TABLE_NAME
, 
 232                     ProviderTableMeta
._ID
 
 234                     + uri
.getPathSegments().get(1) 
 235                     + (!TextUtils
.isEmpty(where
) ? 
" AND (" + where
 
 236                             + ")" : ""), whereArgs
); 
 238              if (folder != null) { 
 243             //Log_OC.d(TAG, "Removing ROOT!"); 
 244             count 
= db
.delete(ProviderTableMeta
.FILE_TABLE_NAME
, where
, whereArgs
); 
 247             count 
= db
.delete(ProviderTableMeta
.OCSHARES_TABLE_NAME
, where
, whereArgs
); 
 250             //Log_OC.e(TAG, "Unknown uri " + uri); 
 251             throw new IllegalArgumentException("Unknown uri: " + uri
.toString()); 
 257     public String 
getType(Uri uri
) { 
 258         switch (mUriMatcher
.match(uri
)) { 
 260             return ProviderTableMeta
.CONTENT_TYPE
; 
 262             return ProviderTableMeta
.CONTENT_TYPE_ITEM
; 
 264             throw new IllegalArgumentException("Unknown Uri id." 
 270     public Uri 
insert(Uri uri
, ContentValues values
) { 
 272         SQLiteDatabase db 
= mDbHelper
.getWritableDatabase(); 
 273         db
.beginTransaction(); 
 275             newUri 
= insert(db
, uri
, values
); 
 276             db
.setTransactionSuccessful(); 
 280         getContext().getContentResolver().notifyChange(newUri
, null
); 
 284     private Uri 
insert(SQLiteDatabase db
, Uri uri
, ContentValues values
) { 
 285         switch (mUriMatcher
.match(uri
)){ 
 288             String remotePath 
= values
.getAsString(ProviderTableMeta
.FILE_PATH
); 
 289             String accountName 
= values
.getAsString(ProviderTableMeta
.FILE_ACCOUNT_OWNER
); 
 290             String
[] projection 
= new String
[] { 
 291                     ProviderTableMeta
._ID
, ProviderTableMeta
.FILE_PATH
,  
 292                     ProviderTableMeta
.FILE_ACCOUNT_OWNER 
 
 294             String where 
= ProviderTableMeta
.FILE_PATH 
+ "=? AND " +  
 295                     ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=?"; 
 296             String
[] whereArgs 
= new String
[] {remotePath
, accountName
}; 
 297             Cursor doubleCheck 
= query(db
, uri
, projection
, where
, whereArgs
, null
); 
 298             // ugly patch; serious refactorization is needed to reduce work in  
 299             // FileDataStorageManager and bring it to FileContentProvider 
 300             if (doubleCheck 
== null 
|| !doubleCheck
.moveToFirst()) {      
 301                 long rowId 
= db
.insert(ProviderTableMeta
.FILE_TABLE_NAME
, null
, values
); 
 303                     Uri insertedFileUri 
=  
 304                             ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_FILE
, rowId
); 
 305                     return insertedFileUri
; 
 307                     throw new SQLException("ERROR " + uri
); 
 310                 // file is already inserted; race condition, let's avoid a duplicated entry 
 311                 Uri insertedFileUri 
= ContentUris
.withAppendedId( 
 312                         ProviderTableMeta
.CONTENT_URI_FILE
,  
 313                         doubleCheck
.getLong(doubleCheck
.getColumnIndex(ProviderTableMeta
._ID
)) 
 317                 return insertedFileUri
; 
 321             String path 
= values
.getAsString(ProviderTableMeta
.OCSHARES_PATH
); 
 322             String accountNameShare
= values
.getAsString(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
); 
 323             String
[] projectionShare 
= new String
[] { 
 324                     ProviderTableMeta
._ID
, ProviderTableMeta
.OCSHARES_PATH
,  
 325                     ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER 
 
 327             String whereShare 
= ProviderTableMeta
.OCSHARES_PATH 
+ "=? AND " +  
 328                     ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER 
+ "=?"; 
 329             String
[] whereArgsShare 
= new String
[] {path
, accountNameShare
}; 
 330             Uri insertedShareUri 
= null
; 
 331             Cursor doubleCheckShare 
=  
 332                     query(db
, uri
, projectionShare
, whereShare
, whereArgsShare
, null
); 
 333             // ugly patch; serious refactorization is needed to reduce work in  
 334             // FileDataStorageManager and bring it to FileContentProvider 
 335             if (doubleCheckShare 
== null 
|| !doubleCheckShare
.moveToFirst()) {      
 336                 long rowId 
= db
.insert(ProviderTableMeta
.OCSHARES_TABLE_NAME
, null
, values
); 
 339                             ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_SHARE
, rowId
); 
 341                     throw new SQLException("ERROR " + uri
); 
 345                 // file is already inserted; race condition, let's avoid a duplicated entry 
 346                 insertedShareUri 
= ContentUris
.withAppendedId( 
 347                         ProviderTableMeta
.CONTENT_URI_SHARE
,  
 348                         doubleCheckShare
.getLong( 
 349                                 doubleCheckShare
.getColumnIndex(ProviderTableMeta
._ID
) 
 352                 doubleCheckShare
.close(); 
 354             updateFilesTableAccordingToShareInsertion(db
, uri
, values
); 
 355             return insertedShareUri
; 
 359             throw new IllegalArgumentException("Unknown uri id: " + uri
); 
 364     private void updateFilesTableAccordingToShareInsertion( 
 365             SQLiteDatabase db
, Uri uri
, ContentValues shareValues
 
 367         ContentValues fileValues 
= new ContentValues(); 
 369                 ProviderTableMeta
.FILE_SHARE_BY_LINK
,  
 370                 ShareType
.PUBLIC_LINK
.getValue() ==  
 371                     shareValues
.getAsInteger(ProviderTableMeta
.OCSHARES_SHARE_TYPE
)? 
1 : 0 
 373         String whereShare 
= ProviderTableMeta
.FILE_PATH 
+ "=? AND " +  
 374                 ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=?"; 
 375         String
[] whereArgsShare 
= new String
[] { 
 376                 shareValues
.getAsString(ProviderTableMeta
.OCSHARES_PATH
),  
 377                 shareValues
.getAsString(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
) 
 379         db
.update(ProviderTableMeta
.FILE_TABLE_NAME
, fileValues
, whereShare
, whereArgsShare
); 
 384     public boolean onCreate() { 
 385         mDbHelper 
= new DataBaseHelper(getContext()); 
 387         String authority 
= getContext().getResources().getString(R
.string
.authority
); 
 388         mUriMatcher 
= new UriMatcher(UriMatcher
.NO_MATCH
); 
 389         mUriMatcher
.addURI(authority
, null
, ROOT_DIRECTORY
); 
 390         mUriMatcher
.addURI(authority
, "file/", SINGLE_FILE
); 
 391         mUriMatcher
.addURI(authority
, "file/#", SINGLE_FILE
); 
 392         mUriMatcher
.addURI(authority
, "dir/", DIRECTORY
); 
 393         mUriMatcher
.addURI(authority
, "dir/#", DIRECTORY
); 
 394         mUriMatcher
.addURI(authority
, "shares/", SHARES
); 
 395         mUriMatcher
.addURI(authority
, "shares/#", SHARES
); 
 406             String
[] selectionArgs
,  
 410         Cursor result 
= null
; 
 411         SQLiteDatabase db 
= mDbHelper
.getReadableDatabase(); 
 412         db
.beginTransaction(); 
 414             result 
= query(db
, uri
, projection
, selection
, selectionArgs
, sortOrder
); 
 415             db
.setTransactionSuccessful(); 
 422     private Cursor 
query( 
 427             String
[] selectionArgs
,  
 431         SQLiteQueryBuilder sqlQuery 
= new SQLiteQueryBuilder(); 
 433         sqlQuery
.setTables(ProviderTableMeta
.FILE_TABLE_NAME
); 
 434         sqlQuery
.setProjectionMap(mFileProjectionMap
); 
 436         switch (mUriMatcher
.match(uri
)) { 
 440             String folderId 
= uri
.getPathSegments().get(1); 
 441             sqlQuery
.appendWhere(ProviderTableMeta
.FILE_PARENT 
+ "=" 
 445             if (uri
.getPathSegments().size() > 1) { 
 446                 sqlQuery
.appendWhere(ProviderTableMeta
._ID 
+ "=" 
 447                         + uri
.getPathSegments().get(1)); 
 451             sqlQuery
.setTables(ProviderTableMeta
.OCSHARES_TABLE_NAME
); 
 452             sqlQuery
.setProjectionMap(mOCSharesProjectionMap
); 
 453             if (uri
.getPathSegments().size() > 1) { 
 454                 sqlQuery
.appendWhere(ProviderTableMeta
._ID 
+ "=" 
 455                         + uri
.getPathSegments().get(1)); 
 459             throw new IllegalArgumentException("Unknown uri id: " + uri
); 
 463         if (TextUtils
.isEmpty(sortOrder
)) { 
 464             if (mUriMatcher
.match(uri
) == SHARES
) { 
 465                 order 
= ProviderTableMeta
.OCSHARES_DEFAULT_SORT_ORDER
; 
 468                 order 
= ProviderTableMeta
.FILE_DEFAULT_SORT_ORDER
; 
 475         db
.execSQL("PRAGMA case_sensitive_like = true"); 
 476         Cursor c 
= sqlQuery
.query(db
, projection
, selection
, selectionArgs
, null
, null
, order
); 
 477         c
.setNotificationUri(getContext().getContentResolver(), uri
); 
 482     public int update(Uri uri
, ContentValues values
, String selection
, String
[] selectionArgs
) { 
 485         SQLiteDatabase db 
= mDbHelper
.getWritableDatabase(); 
 486         db
.beginTransaction(); 
 488             count 
= update(db
, uri
, values
, selection
, selectionArgs
); 
 489             db
.setTransactionSuccessful(); 
 493         getContext().getContentResolver().notifyChange(uri
, null
); 
 502             ContentValues values
,  
 504             String
[] selectionArgs
 
 506         switch (mUriMatcher
.match(uri
)) { 
 508                 return  0; //updateFolderSize(db, selectionArgs[0]); 
 511                         ProviderTableMeta
.OCSHARES_TABLE_NAME
, values
, selection
, selectionArgs
 
 515                         ProviderTableMeta
.FILE_TABLE_NAME
, values
, selection
, selectionArgs
 
 521     private int updateFolderSize(SQLiteDatabase db, String folderId) { 
 523         String [] whereArgs = new String[] { folderId }; 
 525         // read current size saved for the folder  
 527         long folderParentId = -1; 
 528         Uri selectFolderUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, folderId); 
 529         String[] folderProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH,  ProviderTableMeta.FILE_PARENT}; 
 530         String folderWhere = ProviderTableMeta._ID + "=?"; 
 531         Cursor folderCursor = query(db, selectFolderUri, folderProjection, folderWhere, whereArgs, null); 
 532         if (folderCursor != null && folderCursor.moveToFirst()) { 
 533             folderSize = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH));; 
 534             folderParentId = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_PARENT));; 
 536         folderCursor.close(); 
 538         // read and sum sizes of children 
 539         long childrenSize = 0; 
 540         Uri selectChildrenUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, folderId); 
 541         String[] childrenProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH,  ProviderTableMeta.FILE_PARENT}; 
 542         String childrenWhere = ProviderTableMeta.FILE_PARENT + "=?"; 
 543         Cursor childrenCursor = query(db, selectChildrenUri, childrenProjection, childrenWhere, whereArgs, null); 
 544         if (childrenCursor != null && childrenCursor.moveToFirst()) { 
 545             while (!childrenCursor.isAfterLast()) { 
 546                 childrenSize += childrenCursor.getLong(childrenCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH)); 
 547                 childrenCursor.moveToNext(); 
 550         childrenCursor.close(); 
 553         if (folderSize != childrenSize) { 
 554             Log_OC.d("FileContentProvider", "Updating " + folderSize + " to " + childrenSize); 
 555             ContentValues cv = new ContentValues(); 
 556             cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, childrenSize); 
 557             count = db.update(ProviderTableMeta.FILE_TABLE_NAME, cv, folderWhere, whereArgs); 
 559             // propagate update until root 
 560             if (folderParentId > FileDataStorageManager.ROOT_PARENT_ID) { 
 561                 Log_OC.d("FileContentProvider", "Propagating update to " + folderParentId); 
 562                 updateFolderSize(db, String.valueOf(folderParentId)); 
 564                 Log_OC.d("FileContentProvider", "NOT propagating to " + folderParentId); 
 567             Log_OC.d("FileContentProvider", "NOT updating, sizes are " + folderSize + " and " + childrenSize); 
 574     public ContentProviderResult
[] applyBatch (ArrayList
<ContentProviderOperation
> operations
)  
 575             throws OperationApplicationException 
{ 
 576         Log_OC
.d("FileContentProvider", "applying batch in provider " + this +  
 577                 " (temporary: " + isTemporary() + ")" ); 
 578         ContentProviderResult
[] results 
= new ContentProviderResult
[operations
.size()]; 
 581         SQLiteDatabase db 
= mDbHelper
.getWritableDatabase(); 
 582         db
.beginTransaction();  // it's supposed that transactions can be nested 
 584             for (ContentProviderOperation operation 
: operations
) { 
 585                 results
[i
] = operation
.apply(this, results
, i
); 
 588             db
.setTransactionSuccessful(); 
 592         Log_OC
.d("FileContentProvider", "applied batch in provider " + this); 
 597     class DataBaseHelper 
extends SQLiteOpenHelper 
{ 
 599         public DataBaseHelper(Context context
) { 
 600             super(context
, ProviderMeta
.DB_NAME
, null
, ProviderMeta
.DB_VERSION
); 
 605         public void onCreate(SQLiteDatabase db
) { 
 607             Log_OC
.i("SQL", "Entering in onCreate"); 
 608             db
.execSQL("CREATE TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ "(" 
 609                     + ProviderTableMeta
._ID 
+ " INTEGER PRIMARY KEY, " 
 610                     + ProviderTableMeta
.FILE_NAME 
+ " TEXT, " 
 611                     + ProviderTableMeta
.FILE_PATH 
+ " TEXT, " 
 612                     + ProviderTableMeta
.FILE_PARENT 
+ " INTEGER, " 
 613                     + ProviderTableMeta
.FILE_CREATION 
+ " INTEGER, " 
 614                     + ProviderTableMeta
.FILE_MODIFIED 
+ " INTEGER, " 
 615                     + ProviderTableMeta
.FILE_CONTENT_TYPE 
+ " TEXT, " 
 616                     + ProviderTableMeta
.FILE_CONTENT_LENGTH 
+ " INTEGER, " 
 617                     + ProviderTableMeta
.FILE_STORAGE_PATH 
+ " TEXT, " 
 618                     + ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ " TEXT, " 
 619                     + ProviderTableMeta
.FILE_LAST_SYNC_DATE 
+ " INTEGER, " 
 620                     + ProviderTableMeta
.FILE_KEEP_IN_SYNC 
+ " INTEGER, " 
 621                     + ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA 
+ " INTEGER, " 
 622                     + ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA 
+ " INTEGER, " 
 623                     + ProviderTableMeta
.FILE_ETAG 
+ " TEXT, "  
 624                     + ProviderTableMeta
.FILE_SHARE_BY_LINK 
+ " INTEGER, " 
 625                     + ProviderTableMeta
.FILE_PUBLIC_LINK  
+ " TEXT, " 
 626                     + ProviderTableMeta
.FILE_PERMISSIONS  
+ " TEXT null," 
 627                     + ProviderTableMeta
.FILE_REMOTE_ID  
+ " TEXT null," 
 628                     + ProviderTableMeta
.FILE_UPDATE_THUMBNAIL  
+ " INTEGER);" //boolean 
 631             // Create table ocshares 
 632             db
.execSQL("CREATE TABLE " + ProviderTableMeta
.OCSHARES_TABLE_NAME 
+ "(" 
 633                     + ProviderTableMeta
._ID 
+ " INTEGER PRIMARY KEY, " 
 634                     + ProviderTableMeta
.OCSHARES_FILE_SOURCE 
+ " INTEGER, " 
 635                     + ProviderTableMeta
.OCSHARES_ITEM_SOURCE 
+ " INTEGER, " 
 636                     + ProviderTableMeta
.OCSHARES_SHARE_TYPE 
+ " INTEGER, " 
 637                     + ProviderTableMeta
.OCSHARES_SHARE_WITH 
+ " TEXT, " 
 638                     + ProviderTableMeta
.OCSHARES_PATH 
+ " TEXT, " 
 639                     + ProviderTableMeta
.OCSHARES_PERMISSIONS
+ " INTEGER, " 
 640                     + ProviderTableMeta
.OCSHARES_SHARED_DATE 
+ " INTEGER, " 
 641                     + ProviderTableMeta
.OCSHARES_EXPIRATION_DATE 
+ " INTEGER, " 
 642                     + ProviderTableMeta
.OCSHARES_TOKEN 
+ " TEXT, " 
 643                     + ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME 
+ " TEXT, " 
 644                     + ProviderTableMeta
.OCSHARES_IS_DIRECTORY 
+ " INTEGER, "  // boolean 
 645                     + ProviderTableMeta
.OCSHARES_USER_ID 
+ " INTEGER, " 
 646                     + ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED 
+ " INTEGER,"  
 647                     + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER 
+ " TEXT );" ); 
 651         public void onUpgrade(SQLiteDatabase db
, int oldVersion
, int newVersion
) { 
 652             Log_OC
.i("SQL", "Entering in onUpgrade"); 
 653             boolean upgraded 
= false
;  
 654             if (oldVersion 
== 1 && newVersion 
>= 2) { 
 655                 Log_OC
.i("SQL", "Entering in the #1 ADD in onUpgrade"); 
 656                 db
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 657                            " ADD COLUMN " + ProviderTableMeta
.FILE_KEEP_IN_SYNC  
+ " INTEGER " + 
 661             if (oldVersion 
< 3 && newVersion 
>= 3) { 
 662                 Log_OC
.i("SQL", "Entering in the #2 ADD in onUpgrade"); 
 663                 db
.beginTransaction(); 
 665                     db
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 666                                " ADD COLUMN " + ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA  
+  
 667                                " INTEGER " + " DEFAULT 0"); 
 669                     // assume there are not local changes pending to upload 
 670                     db
.execSQL("UPDATE " + ProviderTableMeta
.FILE_TABLE_NAME 
+  
 671                             " SET " + ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA 
+ " = "  
 672                             + System
.currentTimeMillis() +  
 673                             " WHERE " + ProviderTableMeta
.FILE_STORAGE_PATH 
+ " IS NOT NULL"); 
 676                     db
.setTransactionSuccessful(); 
 681             if (oldVersion 
< 4 && newVersion 
>= 4) { 
 682                 Log_OC
.i("SQL", "Entering in the #3 ADD in onUpgrade"); 
 683                 db
.beginTransaction(); 
 685                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 686                            " ADD COLUMN " + ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA 
+ 
 687                            " INTEGER " + " DEFAULT 0"); 
 689                     db
.execSQL("UPDATE " + ProviderTableMeta
.FILE_TABLE_NAME 
+  
 690                            " SET " + ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA 
+ " = " + 
 691                             ProviderTableMeta
.FILE_MODIFIED 
+ 
 692                            " WHERE " + ProviderTableMeta
.FILE_STORAGE_PATH 
+ " IS NOT NULL"); 
 695                     db
.setTransactionSuccessful(); 
 701                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+  
 702                         ", newVersion == " + newVersion
); 
 704             if (oldVersion 
< 5 && newVersion 
>= 5) { 
 705                 Log_OC
.i("SQL", "Entering in the #4 ADD in onUpgrade"); 
 706                 db
.beginTransaction(); 
 708                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 709                             " ADD COLUMN " + ProviderTableMeta
.FILE_ETAG 
+ " TEXT " + 
 713                     db
.setTransactionSuccessful(); 
 719                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+  
 720                         ", newVersion == " + newVersion
); 
 722             if (oldVersion 
< 6 && newVersion 
>= 6) { 
 723                 Log_OC
.i("SQL", "Entering in the #5 ADD in onUpgrade"); 
 724                 db
.beginTransaction(); 
 726                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 727                             " ADD COLUMN " + ProviderTableMeta
.FILE_SHARE_BY_LINK 
+ " INTEGER " + 
 730                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 731                             " ADD COLUMN " + ProviderTableMeta
.FILE_PUBLIC_LINK 
+ " TEXT " + 
 734                     // Create table ocshares 
 735                     db
.execSQL("CREATE TABLE " + ProviderTableMeta
.OCSHARES_TABLE_NAME 
+ "(" 
 736                             + ProviderTableMeta
._ID 
+ " INTEGER PRIMARY KEY, " 
 737                             + ProviderTableMeta
.OCSHARES_FILE_SOURCE 
+ " INTEGER, " 
 738                             + ProviderTableMeta
.OCSHARES_ITEM_SOURCE 
+ " INTEGER, " 
 739                             + ProviderTableMeta
.OCSHARES_SHARE_TYPE 
+ " INTEGER, " 
 740                             + ProviderTableMeta
.OCSHARES_SHARE_WITH 
+ " TEXT, " 
 741                             + ProviderTableMeta
.OCSHARES_PATH 
+ " TEXT, " 
 742                             + ProviderTableMeta
.OCSHARES_PERMISSIONS
+ " INTEGER, " 
 743                             + ProviderTableMeta
.OCSHARES_SHARED_DATE 
+ " INTEGER, " 
 744                             + ProviderTableMeta
.OCSHARES_EXPIRATION_DATE 
+ " INTEGER, " 
 745                             + ProviderTableMeta
.OCSHARES_TOKEN 
+ " TEXT, " 
 746                             + ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME 
+ " TEXT, " 
 747                             + ProviderTableMeta
.OCSHARES_IS_DIRECTORY 
+ " INTEGER, "  // boolean 
 748                             + ProviderTableMeta
.OCSHARES_USER_ID 
+ " INTEGER, " 
 749                             + ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED 
+ " INTEGER,"  
 750                             + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER 
+ " TEXT );" ); 
 753                     db
.setTransactionSuccessful(); 
 759                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+  
 760                         ", newVersion == " + newVersion
); 
 762             if (oldVersion 
< 7 && newVersion 
>= 7) { 
 763                 Log_OC
.i("SQL", "Entering in the #7 ADD in onUpgrade"); 
 764                 db
.beginTransaction(); 
 766                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 767                             " ADD COLUMN " + ProviderTableMeta
.FILE_PERMISSIONS 
+ " TEXT " + 
 770                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 771                             " ADD COLUMN " + ProviderTableMeta
.FILE_REMOTE_ID 
+ " TEXT " + 
 775                     db
.setTransactionSuccessful(); 
 781                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+  
 782                         ", newVersion == " + newVersion
); 
 784             if (oldVersion 
< 8 && newVersion 
>= 8) { 
 785                 Log_OC
.i("SQL", "Entering in the #8 ADD in onUpgrade"); 
 786                 db
.beginTransaction(); 
 788                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 789                             " ADD COLUMN " + ProviderTableMeta
.FILE_UPDATE_THUMBNAIL 
+ " INTEGER " + 
 793                     db
.setTransactionSuccessful(); 
 799                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+  
 800                         ", newVersion == " + newVersion
);