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
.resources
.files
.FileUtils
; 
  28 import com
.owncloud
.android
.lib
.resources
.shares
.ShareType
; 
  29 import com
.owncloud
.android
.utils
.Log_OC
; 
  33 import android
.content
.ContentProvider
; 
  34 import android
.content
.ContentProviderOperation
; 
  35 import android
.content
.ContentProviderResult
; 
  36 import android
.content
.ContentUris
; 
  37 import android
.content
.ContentValues
; 
  38 import android
.content
.Context
; 
  39 import android
.content
.OperationApplicationException
; 
  40 import android
.content
.UriMatcher
; 
  41 import android
.database
.Cursor
; 
  42 import android
.database
.SQLException
; 
  43 import android
.database
.sqlite
.SQLiteDatabase
; 
  44 import android
.database
.sqlite
.SQLiteOpenHelper
; 
  45 import android
.database
.sqlite
.SQLiteQueryBuilder
; 
  46 import android
.net
.Uri
; 
  47 import android
.text
.TextUtils
; 
  50  * The ContentProvider for the ownCloud App. 
  52  * @author Bartek Przybylski 
  53  * @author David A. Velasco 
  56 public class FileContentProvider 
extends ContentProvider 
{ 
  58     private DataBaseHelper mDbHelper
; 
  60     // Projection for filelist table 
  61     private static HashMap
<String
, String
> mFileProjectionMap
; 
  63         mFileProjectionMap 
= new HashMap
<String
, String
>(); 
  64         mFileProjectionMap
.put(ProviderTableMeta
._ID
, ProviderTableMeta
._ID
); 
  65         mFileProjectionMap
.put(ProviderTableMeta
.FILE_PARENT
, 
  66                 ProviderTableMeta
.FILE_PARENT
); 
  67         mFileProjectionMap
.put(ProviderTableMeta
.FILE_PATH
, 
  68                 ProviderTableMeta
.FILE_PATH
); 
  69         mFileProjectionMap
.put(ProviderTableMeta
.FILE_NAME
, 
  70                 ProviderTableMeta
.FILE_NAME
); 
  71         mFileProjectionMap
.put(ProviderTableMeta
.FILE_CREATION
, 
  72                 ProviderTableMeta
.FILE_CREATION
); 
  73         mFileProjectionMap
.put(ProviderTableMeta
.FILE_MODIFIED
, 
  74                 ProviderTableMeta
.FILE_MODIFIED
); 
  75         mFileProjectionMap
.put(ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
, 
  76                 ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA
); 
  77         mFileProjectionMap
.put(ProviderTableMeta
.FILE_CONTENT_LENGTH
, 
  78                 ProviderTableMeta
.FILE_CONTENT_LENGTH
); 
  79         mFileProjectionMap
.put(ProviderTableMeta
.FILE_CONTENT_TYPE
, 
  80                 ProviderTableMeta
.FILE_CONTENT_TYPE
); 
  81         mFileProjectionMap
.put(ProviderTableMeta
.FILE_STORAGE_PATH
, 
  82                 ProviderTableMeta
.FILE_STORAGE_PATH
); 
  83         mFileProjectionMap
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE
, 
  84                 ProviderTableMeta
.FILE_LAST_SYNC_DATE
); 
  85         mFileProjectionMap
.put(ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
, 
  86                 ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA
); 
  87         mFileProjectionMap
.put(ProviderTableMeta
.FILE_KEEP_IN_SYNC
, 
  88                 ProviderTableMeta
.FILE_KEEP_IN_SYNC
); 
  89         mFileProjectionMap
.put(ProviderTableMeta
.FILE_ACCOUNT_OWNER
, 
  90                 ProviderTableMeta
.FILE_ACCOUNT_OWNER
); 
  91         mFileProjectionMap
.put(ProviderTableMeta
.FILE_ETAG
,  
  92                 ProviderTableMeta
.FILE_ETAG
); 
  93         mFileProjectionMap
.put(ProviderTableMeta
.FILE_SHARE_BY_LINK
, 
  94                 ProviderTableMeta
.FILE_SHARE_BY_LINK
); 
  95         mFileProjectionMap
.put(ProviderTableMeta
.FILE_PUBLIC_LINK
, 
  96                 ProviderTableMeta
.FILE_PUBLIC_LINK
); 
  99     private static final int SINGLE_FILE 
= 1; 
 100     private static final int DIRECTORY 
= 2; 
 101     private static final int ROOT_DIRECTORY 
= 3; 
 102     private static final int SHARES 
= 4; 
 104     private static final String TAG 
= FileContentProvider
.class.getSimpleName(); 
 106     // Projection for ocshares table 
 107     private static HashMap
<String
, String
> mOCSharesProjectionMap
; 
 109         mOCSharesProjectionMap 
= new HashMap
<String
, String
>(); 
 110         mOCSharesProjectionMap
.put(ProviderTableMeta
._ID
, ProviderTableMeta
._ID
); 
 111         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_FILE_SOURCE
, 
 112                 ProviderTableMeta
.OCSHARES_FILE_SOURCE
); 
 113         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_ITEM_SOURCE
, 
 114                 ProviderTableMeta
.OCSHARES_ITEM_SOURCE
); 
 115         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_SHARE_TYPE
, 
 116                 ProviderTableMeta
.OCSHARES_SHARE_TYPE
); 
 117         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_SHARE_WITH
, 
 118                 ProviderTableMeta
.OCSHARES_SHARE_WITH
); 
 119         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_PATH
, 
 120                 ProviderTableMeta
.OCSHARES_PATH
); 
 121         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_PERMISSIONS
, 
 122                 ProviderTableMeta
.OCSHARES_PERMISSIONS
); 
 123         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_SHARED_DATE
, 
 124                 ProviderTableMeta
.OCSHARES_SHARED_DATE
); 
 125         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
, 
 126                 ProviderTableMeta
.OCSHARES_EXPIRATION_DATE
); 
 127         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_TOKEN
, 
 128                 ProviderTableMeta
.OCSHARES_TOKEN
); 
 129         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
, 
 130                 ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME
); 
 131         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_IS_DIRECTORY
, 
 132                 ProviderTableMeta
.OCSHARES_IS_DIRECTORY
); 
 133         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_USER_ID
, 
 134                 ProviderTableMeta
.OCSHARES_USER_ID
); 
 135         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
, 
 136                 ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED
); 
 137         mOCSharesProjectionMap
.put(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
, 
 138                 ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
); 
 141     private UriMatcher mUriMatcher
; 
 144     public int delete(Uri uri
, String where
, String
[] whereArgs
) { 
 145         Log_OC
.d(TAG
, "Deleting " + uri 
+ " at provider " + this); 
 147         SQLiteDatabase db 
= mDbHelper
.getWritableDatabase(); 
 148         db
.beginTransaction(); 
 150         // Get parentId to notify the change 
 151         long parentId 
= getParentId(uri
); 
 155             count 
= delete(db
, uri
, where
, whereArgs
); 
 156             db
.setTransactionSuccessful(); 
 160         Log_OC
.d(TAG
, "Uri " + uri
); 
 161         getContext().getContentResolver().notifyChange(uri
, null
); 
 163         // Notify the change to the parent folder 
 164         notifyChangeToParentUri(parentId
); 
 168     private long getParentId(Uri uri
) { 
 171         if (mUriMatcher
.match(uri
) == SINGLE_FILE 
|| mUriMatcher
.match(uri
) == DIRECTORY
) { 
 172             String fileId 
= uri
.toString().substring(uri
.toString().lastIndexOf(FileUtils
.PATH_SEPARATOR
) + 1); 
 173             Uri selectFileUri 
= Uri
.withAppendedPath(ProviderTableMeta
.CONTENT_URI_FILE
, fileId
); 
 174             String
[] fileProjection 
= new String
[] { ProviderTableMeta
.FILE_PARENT 
}; 
 175             Cursor fileCursor 
= query(selectFileUri
, fileProjection
, null
, null
, null
); 
 177             if (fileCursor 
!= null  
&& fileCursor
.moveToFirst()) { 
 178                 parentId 
= fileCursor
.getLong(fileCursor
.getColumnIndex(ProviderTableMeta
.FILE_PARENT
)); 
 182         Log_OC
.d(TAG
, "getParentId = " + parentId
); 
 186     private void notifyChangeToParentUri(long parentId
) { 
 187         if (parentId 
!= -1) { 
 188             Uri parentUri 
= Uri
.withAppendedPath( 
 189                     ProviderTableMeta
.CONTENT_URI_DIR
,  
 190                     String
.valueOf(parentId
)); 
 191             Log_OC
.d(TAG
, "ParentUri " + parentUri
); 
 192             getContext().getContentResolver().notifyChange(parentUri
, null
); 
 196     private int delete(SQLiteDatabase db
, Uri uri
, String where
, String
[] whereArgs
) { 
 198         switch (mUriMatcher
.match(uri
)) { 
 200             /*Cursor c = query(db, uri, null, where, whereArgs, null); 
 201             String remotePath = "(unexisting)"; 
 202             if (c != null && c.moveToFirst()) { 
 203                 remotePath = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH)); 
 205             Log_OC.d(TAG, "Removing FILE " + remotePath); 
 207             count 
= db
.delete(ProviderTableMeta
.FILE_TABLE_NAME
, 
 208                     ProviderTableMeta
._ID
 
 210                             + uri
.getPathSegments().get(1) 
 211                             + (!TextUtils
.isEmpty(where
) ? 
" AND (" + where
 
 212                                     + ")" : ""), whereArgs
); 
 220             // deletion of folder is recursive 
 222             Uri folderUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, Long.parseLong(uri.getPathSegments().get(1))); 
 223             Cursor folder = query(db, folderUri, null, null, null, null); 
 224             String folderName = "(unknown)"; 
 225             if (folder != null && folder.moveToFirst()) { 
 226                 folderName = folder.getString(folder.getColumnIndex(ProviderTableMeta.FILE_PATH)); 
 229             Cursor children 
= query(uri
, null
, null
, null
, null
); 
 230             if (children 
!= null 
&& children
.moveToFirst())  { 
 234                 while (!children
.isAfterLast()) { 
 235                     childId 
= children
.getLong(children
.getColumnIndex(ProviderTableMeta
._ID
)); 
 236                     isDir 
= "DIR".equals(children
.getString(children
.getColumnIndex(ProviderTableMeta
.FILE_CONTENT_TYPE
))); 
 237                     //remotePath = children.getString(children.getColumnIndex(ProviderTableMeta.FILE_PATH)); 
 239                         count 
+= delete(db
, ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_DIR
, childId
), null
, null
); 
 241                         count 
+= delete(db
, ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_FILE
, childId
), null
, null
); 
 243                     children
.moveToNext(); 
 247                 Log_OC.d(TAG, "No child to remove in DIRECTORY " + folderName); 
 249             Log_OC.d(TAG, "Removing DIRECTORY " + folderName + " (or maybe not) "); 
 251             count 
+= db
.delete(ProviderTableMeta
.FILE_TABLE_NAME
, 
 252                     ProviderTableMeta
._ID
 
 254                     + uri
.getPathSegments().get(1) 
 255                     + (!TextUtils
.isEmpty(where
) ? 
" AND (" + where
 
 256                             + ")" : ""), whereArgs
); 
 258              if (folder != null) { 
 263             //Log_OC.d(TAG, "Removing ROOT!"); 
 264             count 
= db
.delete(ProviderTableMeta
.FILE_TABLE_NAME
, where
, whereArgs
); 
 267             count 
= db
.delete(ProviderTableMeta
.OCSHARES_TABLE_NAME
, where
, whereArgs
); 
 270             //Log_OC.e(TAG, "Unknown uri " + uri); 
 271             throw new IllegalArgumentException("Unknown uri: " + uri
.toString()); 
 278     public String 
getType(Uri uri
) { 
 279         switch (mUriMatcher
.match(uri
)) { 
 281             return ProviderTableMeta
.CONTENT_TYPE
; 
 283             return ProviderTableMeta
.CONTENT_TYPE_ITEM
; 
 285             throw new IllegalArgumentException("Unknown Uri id." 
 291     public Uri 
insert(Uri uri
, ContentValues values
) { 
 292         //Log_OC.d(TAG, "Inserting " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this); 
 293         Log_OC
.d(TAG
, "Uri " + uri
); 
 295         SQLiteDatabase db 
= mDbHelper
.getWritableDatabase(); 
 296         db
.beginTransaction(); 
 300             newUri 
= insert(db
, uri
, values
); 
 301             db
.setTransactionSuccessful(); 
 305         getContext().getContentResolver().notifyChange(newUri
, null
); 
 307         // Get parentId to notify the change 
 308         long parentId 
= getParentId(newUri
); 
 309         // Notify the change to the parent folder 
 310         notifyChangeToParentUri(parentId
); 
 315     private Uri 
insert(SQLiteDatabase db
, Uri uri
, ContentValues values
) { 
 316         switch (mUriMatcher
.match(uri
)){ 
 319             String remotePath 
= values
.getAsString(ProviderTableMeta
.FILE_PATH
); 
 320             String accountName 
= values
.getAsString(ProviderTableMeta
.FILE_ACCOUNT_OWNER
); 
 321             String
[] projection 
= new String
[] {ProviderTableMeta
._ID
, ProviderTableMeta
.FILE_PATH
, ProviderTableMeta
.FILE_ACCOUNT_OWNER 
}; 
 322             String where 
= ProviderTableMeta
.FILE_PATH 
+ "=? AND " + ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=?"; 
 323             String
[] whereArgs 
= new String
[] {remotePath
, accountName
}; 
 324             Cursor doubleCheck 
= query(db
, uri
, projection
, where
, whereArgs
, null
); 
 325             if (doubleCheck 
== null 
|| !doubleCheck
.moveToFirst()) {    // ugly patch; serious refactorization is needed to reduce work in FileDataStorageManager and bring it to FileContentProvider  
 326                 long rowId 
= db
.insert(ProviderTableMeta
.FILE_TABLE_NAME
, null
, values
); 
 328                     Uri insertedFileUri 
= ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_FILE
, rowId
); 
 329                     //Log_OC.d(TAG, "Inserted " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this); 
 330                     return insertedFileUri
; 
 332                     //Log_OC.d(TAG, "Error while inserting " + values.getAsString(ProviderTableMeta.FILE_PATH)  + " at provider " + this); 
 333                     throw new SQLException("ERROR " + uri
); 
 336                 // file is already inserted; race condition, let's avoid a duplicated entry 
 337                 Uri insertedFileUri 
= ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_FILE
, doubleCheck
.getLong(doubleCheck
.getColumnIndex(ProviderTableMeta
._ID
))); 
 340                 return insertedFileUri
; 
 344             String path 
= values
.getAsString(ProviderTableMeta
.OCSHARES_PATH
); 
 345             String accountNameShare
= values
.getAsString(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
); 
 346             String
[] projectionShare 
= new String
[] {ProviderTableMeta
._ID
, ProviderTableMeta
.OCSHARES_PATH
, ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER 
}; 
 347             String whereShare 
= ProviderTableMeta
.OCSHARES_PATH 
+ "=? AND " + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER 
+ "=?"; 
 348             String
[] whereArgsShare 
= new String
[] {path
, accountNameShare
}; 
 349             Uri insertedShareUri 
= null
; 
 350             Cursor doubleCheckShare 
= query(db
, uri
, projectionShare
, whereShare
, whereArgsShare
, null
); 
 351             if (doubleCheckShare 
== null 
|| !doubleCheckShare
.moveToFirst()) {    // ugly patch; serious refactorization is needed to reduce work in FileDataStorageManager and bring it to FileContentProvider  
 352                 long rowId 
= db
.insert(ProviderTableMeta
.OCSHARES_TABLE_NAME
, null
, values
); 
 354                     insertedShareUri 
= ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_SHARE
, rowId
); 
 356                     throw new SQLException("ERROR " + uri
); 
 360                 // file is already inserted; race condition, let's avoid a duplicated entry 
 361                 insertedShareUri 
= ContentUris
.withAppendedId(ProviderTableMeta
.CONTENT_URI_SHARE
, doubleCheckShare
.getLong(doubleCheckShare
.getColumnIndex(ProviderTableMeta
._ID
))); 
 362                 doubleCheckShare
.close(); 
 364             updateFilesTableAccordingToShareInsertion(db
, uri
, values
); 
 365             return insertedShareUri
; 
 369             throw new IllegalArgumentException("Unknown uri id: " + uri
); 
 374     private void updateFilesTableAccordingToShareInsertion(SQLiteDatabase db
, Uri uri
, ContentValues shareValues
) { 
 375         ContentValues fileValues 
= new ContentValues(); 
 376         fileValues
.put(ProviderTableMeta
.FILE_SHARE_BY_LINK
,  
 377                             ShareType
.PUBLIC_LINK
.getValue() == shareValues
.getAsInteger(ProviderTableMeta
.OCSHARES_SHARE_TYPE
)? 
1 : 0); 
 378         String whereShare 
= ProviderTableMeta
.FILE_PATH 
+ "=? AND " + ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=?"; 
 379         String
[] whereArgsShare 
= new String
[] { 
 380                 shareValues
.getAsString(ProviderTableMeta
.OCSHARES_PATH
),  
 381                 shareValues
.getAsString(ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER
) 
 383         db
.update(ProviderTableMeta
.FILE_TABLE_NAME
, fileValues
, whereShare
, whereArgsShare
); 
 388     public boolean onCreate() { 
 389         mDbHelper 
= new DataBaseHelper(getContext()); 
 391         String authority 
= getContext().getResources().getString(R
.string
.authority
); 
 392         mUriMatcher 
= new UriMatcher(UriMatcher
.NO_MATCH
); 
 393         mUriMatcher
.addURI(authority
, null
, ROOT_DIRECTORY
); 
 394         mUriMatcher
.addURI(authority
, "file/", SINGLE_FILE
); 
 395         mUriMatcher
.addURI(authority
, "file/#", SINGLE_FILE
); 
 396         mUriMatcher
.addURI(authority
, "dir/", DIRECTORY
); 
 397         mUriMatcher
.addURI(authority
, "dir/#", DIRECTORY
); 
 398         mUriMatcher
.addURI(authority
, "shares/", SHARES
); 
 399         mUriMatcher
.addURI(authority
, "shares/#", SHARES
); 
 406     public Cursor 
query(Uri uri
, String
[] projection
, String selection
, String
[] selectionArgs
, String sortOrder
) { 
 407         Cursor result 
= null
; 
 408         SQLiteDatabase db 
= mDbHelper
.getReadableDatabase(); 
 409         db
.beginTransaction(); 
 411             result 
= query(db
, uri
, projection
, selection
, selectionArgs
, sortOrder
); 
 412             db
.setTransactionSuccessful(); 
 419     private Cursor 
query(SQLiteDatabase db
, Uri uri
, String
[] projection
, String selection
, String
[] selectionArgs
, String sortOrder
) { 
 420         SQLiteQueryBuilder sqlQuery 
= new SQLiteQueryBuilder(); 
 422         sqlQuery
.setTables(ProviderTableMeta
.FILE_TABLE_NAME
); 
 423         sqlQuery
.setProjectionMap(mFileProjectionMap
); 
 425         switch (mUriMatcher
.match(uri
)) { 
 429             String folderId 
= uri
.getPathSegments().get(1); 
 430             sqlQuery
.appendWhere(ProviderTableMeta
.FILE_PARENT 
+ "=" 
 434             if (uri
.getPathSegments().size() > 1) { 
 435                 sqlQuery
.appendWhere(ProviderTableMeta
._ID 
+ "=" 
 436                         + uri
.getPathSegments().get(1)); 
 440             sqlQuery
.setTables(ProviderTableMeta
.OCSHARES_TABLE_NAME
); 
 441             sqlQuery
.setProjectionMap(mOCSharesProjectionMap
); 
 442             if (uri
.getPathSegments().size() > 1) { 
 443                 sqlQuery
.appendWhere(ProviderTableMeta
._ID 
+ "=" 
 444                         + uri
.getPathSegments().get(1)); 
 448             throw new IllegalArgumentException("Unknown uri id: " + uri
); 
 452         if (TextUtils
.isEmpty(sortOrder
)) { 
 453             if (mUriMatcher
.match(uri
) == SHARES
) { 
 454                 order 
= ProviderTableMeta
.OCSHARES_DEFAULT_SORT_ORDER
; 
 457                 order 
= ProviderTableMeta
.FILE_DEFAULT_SORT_ORDER
; 
 464         db
.execSQL("PRAGMA case_sensitive_like = true"); 
 465         Cursor c 
= sqlQuery
.query(db
, projection
, selection
, selectionArgs
, null
, null
, order
); 
 466         Log_OC
.d(TAG
, "setting notification URI: " + uri
); 
 467         c
.setNotificationUri(getContext().getContentResolver(), uri
); 
 472     public int update(Uri uri
, ContentValues values
, String selection
, String
[] selectionArgs
) { 
 474         Log_OC
.d(TAG
, "Updating " + values
.getAsString(ProviderTableMeta
.FILE_PATH
) + " at provider " + this); 
 475         Log_OC
.d(TAG
, "Uri " + uri
); 
 477         SQLiteDatabase db 
= mDbHelper
.getWritableDatabase(); 
 478         db
.beginTransaction(); 
 480             count 
= update(db
, uri
, values
, selection
, selectionArgs
); 
 481             db
.setTransactionSuccessful(); 
 485         getContext().getContentResolver().notifyChange(uri
, null
); 
 487         // Get parentId to notify the change 
 488         long parentId 
= getParentId(uri
); 
 489         // Notify the change to the parent folder 
 490         notifyChangeToParentUri(parentId
); 
 497     private int update(SQLiteDatabase db
, Uri uri
, ContentValues values
, String selection
, String
[] selectionArgs
) { 
 498         switch (mUriMatcher
.match(uri
)) { 
 500                 return  0; //updateFolderSize(db, selectionArgs[0]); 
 502                 return db
.update(ProviderTableMeta
.OCSHARES_TABLE_NAME
, values
, selection
, selectionArgs
); 
 504                 return db
.update(ProviderTableMeta
.FILE_TABLE_NAME
, values
, selection
, selectionArgs
); 
 509     private int updateFolderSize(SQLiteDatabase db, String folderId) { 
 511         String [] whereArgs = new String[] { folderId }; 
 513         // read current size saved for the folder  
 515         long folderParentId = -1; 
 516         Uri selectFolderUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, folderId); 
 517         String[] folderProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH,  ProviderTableMeta.FILE_PARENT}; 
 518         String folderWhere = ProviderTableMeta._ID + "=?"; 
 519         Cursor folderCursor = query(db, selectFolderUri, folderProjection, folderWhere, whereArgs, null); 
 520         if (folderCursor != null && folderCursor.moveToFirst()) { 
 521             folderSize = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH));; 
 522             folderParentId = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_PARENT));; 
 524         folderCursor.close(); 
 526         // read and sum sizes of children 
 527         long childrenSize = 0; 
 528         Uri selectChildrenUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, folderId); 
 529         String[] childrenProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH,  ProviderTableMeta.FILE_PARENT}; 
 530         String childrenWhere = ProviderTableMeta.FILE_PARENT + "=?"; 
 531         Cursor childrenCursor = query(db, selectChildrenUri, childrenProjection, childrenWhere, whereArgs, null); 
 532         if (childrenCursor != null && childrenCursor.moveToFirst()) { 
 533             while (!childrenCursor.isAfterLast()) { 
 534                 childrenSize += childrenCursor.getLong(childrenCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH)); 
 535                 childrenCursor.moveToNext(); 
 538         childrenCursor.close(); 
 541         if (folderSize != childrenSize) { 
 542             Log_OC.d("FileContentProvider", "Updating " + folderSize + " to " + childrenSize); 
 543             ContentValues cv = new ContentValues(); 
 544             cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, childrenSize); 
 545             count = db.update(ProviderTableMeta.FILE_TABLE_NAME, cv, folderWhere, whereArgs); 
 547             // propagate update until root 
 548             if (folderParentId > FileDataStorageManager.ROOT_PARENT_ID) { 
 549                 Log_OC.d("FileContentProvider", "Propagating update to " + folderParentId); 
 550                 updateFolderSize(db, String.valueOf(folderParentId)); 
 552                 Log_OC.d("FileContentProvider", "NOT propagating to " + folderParentId); 
 555             Log_OC.d("FileContentProvider", "NOT updating, sizes are " + folderSize + " and " + childrenSize); 
 562     public ContentProviderResult
[] applyBatch (ArrayList
<ContentProviderOperation
> operations
) throws OperationApplicationException 
{ 
 563         Log_OC
.d("FileContentProvider", "applying batch in provider " + this + " (temporary: " + isTemporary() + ")" ); 
 564         ContentProviderResult
[] results 
= new ContentProviderResult
[operations
.size()]; 
 567         SQLiteDatabase db 
= mDbHelper
.getWritableDatabase(); 
 568         db
.beginTransaction();  // it's supposed that transactions can be nested 
 570             for (ContentProviderOperation operation 
: operations
) { 
 571                 results
[i
] = operation
.apply(this, results
, i
); 
 574             db
.setTransactionSuccessful(); 
 578         Log_OC
.d("FileContentProvider", "applied batch in provider " + this); 
 583     class DataBaseHelper 
extends SQLiteOpenHelper 
{ 
 585         public DataBaseHelper(Context context
) { 
 586             super(context
, ProviderMeta
.DB_NAME
, null
, ProviderMeta
.DB_VERSION
); 
 591         public void onCreate(SQLiteDatabase db
) { 
 593             Log_OC
.i("SQL", "Entering in onCreate"); 
 594             db
.execSQL("CREATE TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ "(" 
 595                     + ProviderTableMeta
._ID 
+ " INTEGER PRIMARY KEY, " 
 596                     + ProviderTableMeta
.FILE_NAME 
+ " TEXT, " 
 597                     + ProviderTableMeta
.FILE_PATH 
+ " TEXT, " 
 598                     + ProviderTableMeta
.FILE_PARENT 
+ " INTEGER, " 
 599                     + ProviderTableMeta
.FILE_CREATION 
+ " INTEGER, " 
 600                     + ProviderTableMeta
.FILE_MODIFIED 
+ " INTEGER, " 
 601                     + ProviderTableMeta
.FILE_CONTENT_TYPE 
+ " TEXT, " 
 602                     + ProviderTableMeta
.FILE_CONTENT_LENGTH 
+ " INTEGER, " 
 603                     + ProviderTableMeta
.FILE_STORAGE_PATH 
+ " TEXT, " 
 604                     + ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ " TEXT, " 
 605                     + ProviderTableMeta
.FILE_LAST_SYNC_DATE 
+ " INTEGER, " 
 606                     + ProviderTableMeta
.FILE_KEEP_IN_SYNC 
+ " INTEGER, " 
 607                     + ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA 
+ " INTEGER, " 
 608                     + ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA 
+ " INTEGER, " 
 609                     + ProviderTableMeta
.FILE_ETAG 
+ " TEXT, "  
 610                     + ProviderTableMeta
.FILE_SHARE_BY_LINK 
+ " INTEGER, " 
 611                     + ProviderTableMeta
.FILE_PUBLIC_LINK  
+ " TEXT );" 
 614             // Create table ocshares 
 615             db
.execSQL("CREATE TABLE " + ProviderTableMeta
.OCSHARES_TABLE_NAME 
+ "(" 
 616                     + ProviderTableMeta
._ID 
+ " INTEGER PRIMARY KEY, " 
 617                     + ProviderTableMeta
.OCSHARES_FILE_SOURCE 
+ " INTEGER, " 
 618                     + ProviderTableMeta
.OCSHARES_ITEM_SOURCE 
+ " INTEGER, " 
 619                     + ProviderTableMeta
.OCSHARES_SHARE_TYPE 
+ " INTEGER, " 
 620                     + ProviderTableMeta
.OCSHARES_SHARE_WITH 
+ " TEXT, " 
 621                     + ProviderTableMeta
.OCSHARES_PATH 
+ " TEXT, " 
 622                     + ProviderTableMeta
.OCSHARES_PERMISSIONS
+ " INTEGER, " 
 623                     + ProviderTableMeta
.OCSHARES_SHARED_DATE 
+ " INTEGER, " 
 624                     + ProviderTableMeta
.OCSHARES_EXPIRATION_DATE 
+ " INTEGER, " 
 625                     + ProviderTableMeta
.OCSHARES_TOKEN 
+ " TEXT, " 
 626                     + ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME 
+ " TEXT, " 
 627                     + ProviderTableMeta
.OCSHARES_IS_DIRECTORY 
+ " INTEGER, "  // boolean 
 628                     + ProviderTableMeta
.OCSHARES_USER_ID 
+ " INTEGER, " 
 629                     + ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED 
+ " INTEGER,"  
 630                     + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER 
+ " TEXT );" ); 
 634         public void onUpgrade(SQLiteDatabase db
, int oldVersion
, int newVersion
) { 
 635             Log_OC
.i("SQL", "Entering in onUpgrade"); 
 636             boolean upgraded 
= false
;  
 637             if (oldVersion 
== 1 && newVersion 
>= 2) { 
 638                 Log_OC
.i("SQL", "Entering in the #1 ADD in onUpgrade"); 
 639                 db
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 640                            " ADD COLUMN " + ProviderTableMeta
.FILE_KEEP_IN_SYNC  
+ " INTEGER " + 
 644             if (oldVersion 
< 3 && newVersion 
>= 3) { 
 645                 Log_OC
.i("SQL", "Entering in the #2 ADD in onUpgrade"); 
 646                 db
.beginTransaction(); 
 648                     db
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 649                                " ADD COLUMN " + ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA  
+ " INTEGER " + 
 652                     // assume there are not local changes pending to upload 
 653                     db
.execSQL("UPDATE " + ProviderTableMeta
.FILE_TABLE_NAME 
+  
 654                             " SET " + ProviderTableMeta
.FILE_LAST_SYNC_DATE_FOR_DATA 
+ " = " + System
.currentTimeMillis() +  
 655                             " WHERE " + ProviderTableMeta
.FILE_STORAGE_PATH 
+ " IS NOT NULL"); 
 658                     db
.setTransactionSuccessful(); 
 663             if (oldVersion 
< 4 && newVersion 
>= 4) { 
 664                 Log_OC
.i("SQL", "Entering in the #3 ADD in onUpgrade"); 
 665                 db
.beginTransaction(); 
 667                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 668                            " ADD COLUMN " + ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA  
+ " INTEGER " + 
 671                     db
.execSQL("UPDATE " + ProviderTableMeta
.FILE_TABLE_NAME 
+  
 672                            " SET " + ProviderTableMeta
.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA 
+ " = " + ProviderTableMeta
.FILE_MODIFIED 
+  
 673                            " WHERE " + ProviderTableMeta
.FILE_STORAGE_PATH 
+ " IS NOT NULL"); 
 676                     db
.setTransactionSuccessful(); 
 682                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+ ", newVersion == " + newVersion
); 
 684             if (oldVersion 
< 5 && newVersion 
>= 5) { 
 685                 Log_OC
.i("SQL", "Entering in the #4 ADD in onUpgrade"); 
 686                 db
.beginTransaction(); 
 688                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 689                             " ADD COLUMN " + ProviderTableMeta
.FILE_ETAG 
+ " TEXT " + 
 693                     db
.setTransactionSuccessful(); 
 699                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+ ", newVersion == " + newVersion
); 
 701             if (oldVersion 
< 6 && newVersion 
>= 6) { 
 702                 Log_OC
.i("SQL", "Entering in the #5 ADD in onUpgrade"); 
 703                 db
.beginTransaction(); 
 705                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 706                             " ADD COLUMN " + ProviderTableMeta
.FILE_SHARE_BY_LINK 
+ " INTEGER " + 
 709                     db 
.execSQL("ALTER TABLE " + ProviderTableMeta
.FILE_TABLE_NAME 
+ 
 710                             " ADD COLUMN " + ProviderTableMeta
.FILE_PUBLIC_LINK 
+ " TEXT " + 
 713                     // Create table ocshares 
 714                     db
.execSQL("CREATE TABLE " + ProviderTableMeta
.OCSHARES_TABLE_NAME 
+ "(" 
 715                             + ProviderTableMeta
._ID 
+ " INTEGER PRIMARY KEY, " 
 716                             + ProviderTableMeta
.OCSHARES_FILE_SOURCE 
+ " INTEGER, " 
 717                             + ProviderTableMeta
.OCSHARES_ITEM_SOURCE 
+ " INTEGER, " 
 718                             + ProviderTableMeta
.OCSHARES_SHARE_TYPE 
+ " INTEGER, " 
 719                             + ProviderTableMeta
.OCSHARES_SHARE_WITH 
+ " TEXT, " 
 720                             + ProviderTableMeta
.OCSHARES_PATH 
+ " TEXT, " 
 721                             + ProviderTableMeta
.OCSHARES_PERMISSIONS
+ " INTEGER, " 
 722                             + ProviderTableMeta
.OCSHARES_SHARED_DATE 
+ " INTEGER, " 
 723                             + ProviderTableMeta
.OCSHARES_EXPIRATION_DATE 
+ " INTEGER, " 
 724                             + ProviderTableMeta
.OCSHARES_TOKEN 
+ " TEXT, " 
 725                             + ProviderTableMeta
.OCSHARES_SHARE_WITH_DISPLAY_NAME 
+ " TEXT, " 
 726                             + ProviderTableMeta
.OCSHARES_IS_DIRECTORY 
+ " INTEGER, "  // boolean 
 727                             + ProviderTableMeta
.OCSHARES_USER_ID 
+ " INTEGER, " 
 728                             + ProviderTableMeta
.OCSHARES_ID_REMOTE_SHARED 
+ " INTEGER,"  
 729                             + ProviderTableMeta
.OCSHARES_ACCOUNT_OWNER 
+ " TEXT );" ); 
 732                     db
.setTransactionSuccessful(); 
 738                 Log_OC
.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion 
+ ", newVersion == " + newVersion
);