UploadsStorageManager.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. /**
  2. * ownCloud Android client application
  3. *
  4. * @author LukeOwncloud
  5. * @author David A. Velasco
  6. * @author masensio
  7. * Copyright (C) 2016 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. package com.owncloud.android.datamodel;
  22. import android.content.ContentResolver;
  23. import android.content.ContentValues;
  24. import android.database.Cursor;
  25. import android.net.Uri;
  26. import com.owncloud.android.db.OCUpload;
  27. import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
  28. import com.owncloud.android.db.UploadResult;
  29. import com.owncloud.android.files.services.FileUploader;
  30. import com.owncloud.android.lib.common.operations.RemoteOperationResult;
  31. import com.owncloud.android.lib.common.utils.Log_OC;
  32. import com.owncloud.android.operations.UploadFileOperation;
  33. import java.util.Calendar;
  34. import java.util.Observable;
  35. /**
  36. * Database helper for storing list of files to be uploaded, including status
  37. * information for each file.
  38. */
  39. public class UploadsStorageManager extends Observable {
  40. private ContentResolver mContentResolver;
  41. private static final String AND = " AND ";
  42. static private final String TAG = UploadsStorageManager.class.getSimpleName();
  43. public enum UploadStatus {
  44. /**
  45. * Upload currently in progress or scheduled to be executed.
  46. */
  47. UPLOAD_IN_PROGRESS(0),
  48. /**
  49. * Last upload failed.
  50. */
  51. UPLOAD_FAILED(1),
  52. /**
  53. * Upload was successful.
  54. */
  55. UPLOAD_SUCCEEDED(2);
  56. private final int value;
  57. UploadStatus(int value) {
  58. this.value = value;
  59. }
  60. public int getValue() {
  61. return value;
  62. }
  63. public static UploadStatus fromValue(int value) {
  64. switch (value) {
  65. case 0:
  66. return UPLOAD_IN_PROGRESS;
  67. case 1:
  68. return UPLOAD_FAILED;
  69. case 2:
  70. return UPLOAD_SUCCEEDED;
  71. }
  72. return null;
  73. }
  74. }
  75. public UploadsStorageManager(ContentResolver contentResolver) {
  76. if (contentResolver == null) {
  77. throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver");
  78. }
  79. mContentResolver = contentResolver;
  80. }
  81. /**
  82. * Stores an upload object in DB.
  83. *
  84. * @param ocUpload Upload object to store
  85. * @return upload id, -1 if the insert process fails.
  86. */
  87. public long storeUpload(OCUpload ocUpload) {
  88. Log_OC.v(TAG, "Inserting " + ocUpload.getLocalPath() + " with status=" + ocUpload.getUploadStatus());
  89. ContentValues cv = new ContentValues();
  90. cv.put(ProviderTableMeta.UPLOADS_LOCAL_PATH, ocUpload.getLocalPath());
  91. cv.put(ProviderTableMeta.UPLOADS_REMOTE_PATH, ocUpload.getRemotePath());
  92. cv.put(ProviderTableMeta.UPLOADS_ACCOUNT_NAME, ocUpload.getAccountName());
  93. cv.put(ProviderTableMeta.UPLOADS_FILE_SIZE, ocUpload.getFileSize());
  94. cv.put(ProviderTableMeta.UPLOADS_STATUS, ocUpload.getUploadStatus().value);
  95. cv.put(ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR, ocUpload.getLocalAction());
  96. cv.put(ProviderTableMeta.UPLOADS_FORCE_OVERWRITE, ocUpload.isForceOverwrite() ? 1 : 0);
  97. cv.put(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER, ocUpload.isCreateRemoteFolder() ? 1 : 0);
  98. cv.put(ProviderTableMeta.UPLOADS_LAST_RESULT, ocUpload.getLastResult().getValue());
  99. cv.put(ProviderTableMeta.UPLOADS_CREATED_BY, ocUpload.getCreadtedBy());
  100. Uri result = getDB().insert(ProviderTableMeta.CONTENT_URI_UPLOADS, cv);
  101. Log_OC.d(TAG, "storeUpload returns with: " + result + " for file: " + ocUpload.getLocalPath());
  102. if (result == null) {
  103. Log_OC.e(TAG, "Failed to insert item " + ocUpload.getLocalPath() + " into upload db.");
  104. return -1;
  105. } else {
  106. long new_id = Long.parseLong(result.getPathSegments().get(1));
  107. ocUpload.setUploadId(new_id);
  108. notifyObserversNow();
  109. return new_id;
  110. }
  111. }
  112. /**
  113. * Update an upload object in DB.
  114. *
  115. * @param ocUpload Upload object with state to update
  116. * @return num of updated uploads.
  117. */
  118. public int updateUpload(OCUpload ocUpload) {
  119. Log_OC.v(TAG, "Updating " + ocUpload.getLocalPath() + " with status=" + ocUpload.getUploadStatus());
  120. ContentValues cv = new ContentValues();
  121. cv.put(ProviderTableMeta.UPLOADS_LOCAL_PATH, ocUpload.getLocalPath());
  122. cv.put(ProviderTableMeta.UPLOADS_REMOTE_PATH, ocUpload.getRemotePath());
  123. cv.put(ProviderTableMeta.UPLOADS_ACCOUNT_NAME, ocUpload.getAccountName());
  124. cv.put(ProviderTableMeta.UPLOADS_STATUS, ocUpload.getUploadStatus().value);
  125. cv.put(ProviderTableMeta.UPLOADS_LAST_RESULT, ocUpload.getLastResult().getValue());
  126. cv.put(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP, ocUpload.getUploadEndTimestamp());
  127. int result = getDB().update(ProviderTableMeta.CONTENT_URI_UPLOADS,
  128. cv,
  129. ProviderTableMeta._ID + "=?",
  130. new String[]{String.valueOf(ocUpload.getUploadId())}
  131. );
  132. Log_OC.d(TAG, "updateUpload returns with: " + result + " for file: " + ocUpload.getLocalPath());
  133. if (result != 1) {
  134. Log_OC.e(TAG, "Failed to update item " + ocUpload.getLocalPath() + " into upload db.");
  135. } else {
  136. notifyObserversNow();
  137. }
  138. return result;
  139. }
  140. private int updateUploadInternal(Cursor c, UploadStatus status, UploadResult result, String remotePath,
  141. String localPath) {
  142. int r = 0;
  143. while (c.moveToNext()) {
  144. // read upload object and update
  145. OCUpload upload = createOCUploadFromCursor(c);
  146. String path = c.getString(c.getColumnIndex(ProviderTableMeta.UPLOADS_LOCAL_PATH));
  147. Log_OC.v(
  148. TAG,
  149. "Updating " + path + " with status:" + status + " and result:"
  150. + (result == null ? "null" : result.toString()) + " (old:"
  151. + upload.toFormattedString() + ")");
  152. upload.setUploadStatus(status);
  153. upload.setLastResult(result);
  154. upload.setRemotePath(remotePath);
  155. if(localPath != null) {
  156. upload.setLocalPath(localPath);
  157. }
  158. if (status == UploadStatus.UPLOAD_SUCCEEDED) {
  159. upload.setUploadEndTimestamp(Calendar.getInstance().getTimeInMillis());
  160. }
  161. // store update upload object to db
  162. r = updateUpload(upload);
  163. }
  164. return r;
  165. }
  166. /**
  167. * Update upload status of file uniquely referenced by id.
  168. *
  169. * @param id upload id.
  170. * @param status new status.
  171. * @param result new result of upload operation
  172. * @param remotePath path of the file to upload in the ownCloud storage
  173. * @param localPath path of the file to upload in the device storage
  174. * @return 1 if file status was updated, else 0.
  175. */
  176. public int updateUploadStatus(long id, UploadStatus status, UploadResult result, String remotePath,
  177. String localPath) {
  178. //Log_OC.v(TAG, "Updating "+filepath+" with uploadStatus="+status +" and result="+result);
  179. int returnValue = 0;
  180. Cursor c = getDB().query(
  181. ProviderTableMeta.CONTENT_URI_UPLOADS,
  182. null,
  183. ProviderTableMeta._ID + "=?",
  184. new String[]{String.valueOf(id)},
  185. null
  186. );
  187. if (c.getCount() != 1) {
  188. Log_OC.e(TAG, c.getCount() + " items for id=" + id
  189. + " available in UploadDb. Expected 1. Failed to update upload db.");
  190. } else {
  191. returnValue = updateUploadInternal(c, status, result, remotePath, localPath);
  192. }
  193. c.close();
  194. return returnValue;
  195. }
  196. /**
  197. * Should be called when some value of this DB was changed. All observers
  198. * are informed.
  199. */
  200. public void notifyObserversNow() {
  201. Log_OC.d(TAG, "notifyObserversNow");
  202. setChanged();
  203. notifyObservers();
  204. }
  205. /**
  206. * Remove an upload from the uploads list, known its target account and remote path.
  207. *
  208. * @param upload Upload instance to remove from persisted storage.
  209. *
  210. * @return true when the upload was stored and could be removed.
  211. */
  212. public int removeUpload(OCUpload upload) {
  213. int result = getDB().delete(
  214. ProviderTableMeta.CONTENT_URI_UPLOADS,
  215. ProviderTableMeta._ID + "=?" ,
  216. new String[]{Long.toString(upload.getUploadId())}
  217. );
  218. Log_OC.d(TAG, "delete returns " + result + " for upload " + upload);
  219. if (result > 0) {
  220. notifyObserversNow();
  221. }
  222. return result;
  223. }
  224. /**
  225. * Remove an upload from the uploads list, known its target account and remote path.
  226. *
  227. * @param accountName Name of the OC account target of the upload to remove.
  228. * @param remotePath Absolute path in the OC account target of the upload to remove.
  229. * @return true when one or more upload entries were removed
  230. */
  231. public int removeUpload(String accountName, String remotePath) {
  232. int result = getDB().delete(
  233. ProviderTableMeta.CONTENT_URI_UPLOADS,
  234. ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=? AND " + ProviderTableMeta.UPLOADS_REMOTE_PATH + "=?" ,
  235. new String[]{accountName, remotePath}
  236. );
  237. Log_OC.d(TAG, "delete returns " + result + " for file " + remotePath + " in " + accountName);
  238. if (result > 0) {
  239. notifyObserversNow();
  240. }
  241. return result;
  242. }
  243. /**
  244. * Remove all the uploads of a given account from the uploads list.
  245. *
  246. * @param accountName Name of the OC account target of the uploads to remove.
  247. * @return true when one or more upload entries were removed
  248. */
  249. public int removeUploads(String accountName) {
  250. int result = getDB().delete(
  251. ProviderTableMeta.CONTENT_URI_UPLOADS,
  252. ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=?",
  253. new String[]{accountName}
  254. );
  255. Log_OC.d(TAG, "delete returns " + result + " for uploads in " + accountName);
  256. if (result > 0) {
  257. notifyObserversNow();
  258. }
  259. return result;
  260. }
  261. public OCUpload[] getAllStoredUploads() {
  262. return getUploads(null, null);
  263. }
  264. private OCUpload[] getUploads(String selection, String[] selectionArgs) {
  265. Cursor c = getDB().query(
  266. ProviderTableMeta.CONTENT_URI_UPLOADS,
  267. null,
  268. selection,
  269. selectionArgs,
  270. null
  271. );
  272. OCUpload[] list = new OCUpload[c.getCount()];
  273. if (c.moveToFirst()) {
  274. do {
  275. OCUpload upload = createOCUploadFromCursor(c);
  276. if (upload == null) {
  277. Log_OC.e(TAG, "OCUpload could not be created from cursor");
  278. } else {
  279. list[c.getPosition()] = upload;
  280. }
  281. } while (c.moveToNext());
  282. }
  283. c.close();
  284. return list;
  285. }
  286. private OCUpload createOCUploadFromCursor(Cursor c) {
  287. OCUpload upload = null;
  288. if (c != null) {
  289. String localPath = c.getString(c.getColumnIndex(ProviderTableMeta.UPLOADS_LOCAL_PATH));
  290. String remotePath = c.getString(c.getColumnIndex(ProviderTableMeta.UPLOADS_REMOTE_PATH));
  291. String accountName = c.getString(c.getColumnIndex(ProviderTableMeta.UPLOADS_ACCOUNT_NAME));
  292. upload = new OCUpload(localPath, remotePath, accountName);
  293. upload.setFileSize(c.getLong(c.getColumnIndex(ProviderTableMeta.UPLOADS_FILE_SIZE)));
  294. upload.setUploadId(c.getLong(c.getColumnIndex(ProviderTableMeta._ID)));
  295. upload.setUploadStatus(
  296. UploadStatus.fromValue(c.getInt(c.getColumnIndex(ProviderTableMeta.UPLOADS_STATUS)))
  297. );
  298. upload.setLocalAction(c.getInt(c.getColumnIndex((ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR))));
  299. upload.setForceOverwrite(c.getInt(
  300. c.getColumnIndex(ProviderTableMeta.UPLOADS_FORCE_OVERWRITE)) == 1);
  301. upload.setCreateRemoteFolder(c.getInt(
  302. c.getColumnIndex(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER)) == 1);
  303. upload.setUploadEndTimestamp(c.getLong(c.getColumnIndex(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP)));
  304. upload.setLastResult(UploadResult.fromValue(
  305. c.getInt(c.getColumnIndex(ProviderTableMeta.UPLOADS_LAST_RESULT))));
  306. upload.setCreatedBy(c.getInt(c.getColumnIndex(ProviderTableMeta.UPLOADS_CREATED_BY)));
  307. }
  308. return upload;
  309. }
  310. /**
  311. * Get all uploads which are currently being uploaded or waiting in the queue to be uploaded.
  312. */
  313. public OCUpload[] getCurrentAndPendingUploads() {
  314. return getUploads(
  315. ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value + " OR " +
  316. ProviderTableMeta.UPLOADS_LAST_RESULT + "==" + UploadResult.DELAYED_FOR_WIFI.getValue() + " OR " +
  317. ProviderTableMeta.UPLOADS_LAST_RESULT + "==" + UploadResult.DELAYED_FOR_CHARGING.getValue(),
  318. null
  319. );
  320. }
  321. /**
  322. * Get all failed uploads.
  323. */
  324. public OCUpload[] getFailedUploads() {
  325. return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value, null);
  326. }
  327. /**
  328. * Get all uploads which where successfully completed.
  329. */
  330. public OCUpload[] getFinishedUploads() {
  331. return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value, null);
  332. }
  333. /**
  334. * Get all failed uploads, except for those that were not performed due to lack of Wifi connection
  335. * @return Array of failed uploads, except for those that were not performed due to lack of Wifi connection.
  336. */
  337. public OCUpload[] getFailedButNotDelayedUploads() {
  338. return getUploads(
  339. ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + AND +
  340. ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND +
  341. ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(),
  342. null
  343. );
  344. }
  345. private ContentResolver getDB() {
  346. return mContentResolver;
  347. }
  348. public long clearFailedButNotDelayedUploads() {
  349. long result = getDB().delete(
  350. ProviderTableMeta.CONTENT_URI_UPLOADS,
  351. ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + AND +
  352. ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND +
  353. ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(),
  354. null
  355. );
  356. Log_OC.d(TAG, "delete all failed uploads but those delayed for Wifi");
  357. if (result > 0) {
  358. notifyObserversNow();
  359. }
  360. return result;
  361. }
  362. public long clearSuccessfulUploads() {
  363. long result = getDB().delete(
  364. ProviderTableMeta.CONTENT_URI_UPLOADS,
  365. ProviderTableMeta.UPLOADS_STATUS + "=="+ UploadStatus.UPLOAD_SUCCEEDED.value, null
  366. );
  367. Log_OC.d(TAG, "delete all successful uploads");
  368. if (result > 0) {
  369. notifyObserversNow();
  370. }
  371. return result;
  372. }
  373. public long clearAllFinishedButNotDelayedUploads() {
  374. String[] whereArgs = new String[2];
  375. whereArgs[0] = String.valueOf(UploadStatus.UPLOAD_SUCCEEDED.value);
  376. whereArgs[1] = String.valueOf(UploadStatus.UPLOAD_FAILED.value);
  377. long result = getDB().delete(
  378. ProviderTableMeta.CONTENT_URI_UPLOADS,
  379. ProviderTableMeta.UPLOADS_STATUS + "=? OR " + ProviderTableMeta.UPLOADS_STATUS + "=? AND " +
  380. ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND +
  381. ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(),
  382. whereArgs
  383. );
  384. Log_OC.d(TAG, "delete all finished uploads");
  385. if (result > 0) {
  386. notifyObserversNow();
  387. }
  388. return result;
  389. }
  390. /**
  391. * Updates the persistent upload database with upload result.
  392. */
  393. public void updateDatabaseUploadResult(RemoteOperationResult uploadResult, UploadFileOperation upload) {
  394. // result: success or fail notification
  395. Log_OC.d(TAG, "updateDataseUploadResult uploadResult: " + uploadResult + " upload: " + upload);
  396. if (uploadResult.isCancelled()) {
  397. removeUpload(
  398. upload.getAccount().name,
  399. upload.getRemotePath()
  400. );
  401. } else {
  402. String localPath = (FileUploader.LOCAL_BEHAVIOUR_MOVE == upload.getLocalBehaviour())
  403. ? upload.getStoragePath() : null;
  404. if (uploadResult.isSuccess()) {
  405. updateUploadStatus(
  406. upload.getOCUploadId(),
  407. UploadStatus.UPLOAD_SUCCEEDED,
  408. UploadResult.UPLOADED,
  409. upload.getRemotePath(),
  410. localPath
  411. );
  412. } else {
  413. updateUploadStatus(
  414. upload.getOCUploadId(),
  415. UploadStatus.UPLOAD_FAILED,
  416. UploadResult.fromOperationResult(uploadResult),
  417. upload.getRemotePath(),
  418. localPath
  419. );
  420. }
  421. }
  422. }
  423. /**
  424. * Updates the persistent upload database with an upload now in progress.
  425. */
  426. public void updateDatabaseUploadStart(UploadFileOperation upload) {
  427. String localPath = (FileUploader.LOCAL_BEHAVIOUR_MOVE == upload.getLocalBehaviour())
  428. ? upload.getStoragePath() : null;
  429. updateUploadStatus(
  430. upload.getOCUploadId(),
  431. UploadStatus.UPLOAD_IN_PROGRESS,
  432. UploadResult.UNKNOWN,
  433. upload.getRemotePath(),
  434. localPath
  435. );
  436. }
  437. /**
  438. * Changes the status of any in progress upload from UploadStatus.UPLOAD_IN_PROGRESS
  439. * to UploadStatus.UPLOAD_FAILED
  440. *
  441. * @return Number of uploads which status was changed.
  442. */
  443. public int failInProgressUploads(UploadResult fail) {
  444. Log_OC.v(TAG, "Updating state of any killed upload");
  445. ContentValues cv = new ContentValues();
  446. cv.put(ProviderTableMeta.UPLOADS_STATUS, UploadStatus.UPLOAD_FAILED.getValue());
  447. cv.put(
  448. ProviderTableMeta.UPLOADS_LAST_RESULT,
  449. fail != null ? fail.getValue() : UploadResult.UNKNOWN.getValue()
  450. );
  451. cv.put(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP, Calendar.getInstance().getTimeInMillis());
  452. int result = getDB().update(
  453. ProviderTableMeta.CONTENT_URI_UPLOADS,
  454. cv,
  455. ProviderTableMeta.UPLOADS_STATUS + "=?",
  456. new String[]{String.valueOf(UploadStatus.UPLOAD_IN_PROGRESS.getValue())}
  457. );
  458. if (result == 0) {
  459. Log_OC.v(TAG, "No upload was killed");
  460. } else {
  461. Log_OC.w(TAG, Integer.toString(result) + " uploads where abruptly interrupted");
  462. notifyObserversNow();
  463. }
  464. return result;
  465. }
  466. }