FileContentProvider.java 43 KB

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