SyncedFolderProvider.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. /**
  2. * Nextcloud Android client application
  3. *
  4. * @author Andy Scherzinger
  5. * Copyright (C) 2016 Andy Scherzinger
  6. * Copyright (C) 2016 Nextcloud.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  10. * License as published by the Free Software Foundation; either
  11. * version 3 of the License, or any later version.
  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 AFFERO GENERAL PUBLIC LICENSE for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public
  19. * License along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. package com.owncloud.android.datamodel;
  22. import android.accounts.Account;
  23. import android.content.ContentResolver;
  24. import android.content.ContentValues;
  25. import android.content.Context;
  26. import android.database.Cursor;
  27. import android.net.Uri;
  28. import android.support.annotation.NonNull;
  29. import com.owncloud.android.db.PreferenceManager;
  30. import com.owncloud.android.db.ProviderMeta;
  31. import com.owncloud.android.lib.common.utils.Log_OC;
  32. import java.io.File;
  33. import java.util.ArrayList;
  34. import java.util.List;
  35. import java.util.Observable;
  36. /**
  37. * Database provider for handling the persistence aspects of {@link SyncedFolder}s.
  38. */
  39. public class SyncedFolderProvider extends Observable {
  40. static private final String TAG = SyncedFolderProvider.class.getSimpleName();
  41. private ContentResolver mContentResolver;
  42. /**
  43. * constructor.
  44. *
  45. * @param contentResolver the ContentResolver to work with.
  46. */
  47. public SyncedFolderProvider(ContentResolver contentResolver) {
  48. if (contentResolver == null) {
  49. throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver");
  50. }
  51. mContentResolver = contentResolver;
  52. }
  53. /**
  54. * Stores a synced folder object in database.
  55. *
  56. * @param syncedFolder synced folder to store
  57. * @return synced folder id, -1 if the insert process fails.
  58. */
  59. public long storeSyncedFolder(SyncedFolder syncedFolder) {
  60. Log_OC.v(TAG, "Inserting " + syncedFolder.getLocalPath() + " with enabled=" + syncedFolder.isEnabled());
  61. ContentValues cv = createContentValuesFromSyncedFolder(syncedFolder);
  62. Uri result = mContentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS, cv);
  63. if (result != null) {
  64. return Long.parseLong(result.getPathSegments().get(1));
  65. } else {
  66. Log_OC.e(TAG, "Failed to insert item " + syncedFolder.getLocalPath() + " into folder sync db.");
  67. return -1;
  68. }
  69. }
  70. /**
  71. * get all synced folder entries.
  72. *
  73. * @return all synced folder entries, empty if none have been found
  74. */
  75. public List<SyncedFolder> getSyncedFolders() {
  76. Cursor cursor = mContentResolver.query(
  77. ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
  78. null,
  79. "1=1",
  80. null,
  81. null
  82. );
  83. if (cursor != null) {
  84. List<SyncedFolder> list = new ArrayList<>(cursor.getCount());
  85. if (cursor.moveToFirst()) {
  86. do {
  87. SyncedFolder syncedFolder = createSyncedFolderFromCursor(cursor);
  88. if (syncedFolder == null) {
  89. Log_OC.e(TAG, "SyncedFolder could not be created from cursor");
  90. } else {
  91. list.add(cursor.getPosition(), syncedFolder);
  92. }
  93. } while (cursor.moveToNext());
  94. }
  95. cursor.close();
  96. return list;
  97. } else {
  98. Log_OC.e(TAG, "DB error creating read all cursor for synced folders.");
  99. }
  100. return new ArrayList<>(0);
  101. }
  102. /**
  103. * Update upload status of file uniquely referenced by id.
  104. *
  105. * @param id synced folder id.
  106. * @param enabled new status.
  107. * @return the number of rows updated.
  108. */
  109. public int updateSyncedFolderEnabled(long id, Boolean enabled) {
  110. Log_OC.v(TAG, "Storing synced folder id" + id + " with enabled=" + enabled);
  111. int result = 0;
  112. Cursor cursor = mContentResolver.query(
  113. ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
  114. null,
  115. ProviderMeta.ProviderTableMeta._ID + "=?",
  116. new String[]{String.valueOf(id)},
  117. null
  118. );
  119. if (cursor != null && cursor.getCount() == 1) {
  120. while (cursor.moveToNext()) {
  121. // read sync folder object and update
  122. SyncedFolder syncedFolder = createSyncedFolderFromCursor(cursor);
  123. syncedFolder.setEnabled(enabled);
  124. // update sync folder object in db
  125. result = updateSyncFolder(syncedFolder);
  126. cursor.close();
  127. }
  128. } else {
  129. if (cursor == null) {
  130. Log_OC.e(TAG, "Sync folder db cursor for ID=" + id + " in NULL.");
  131. } else {
  132. Log_OC.e(TAG, cursor.getCount() + " items for id=" + id + " available in sync folder database. " +
  133. "Expected 1. Failed to update sync folder db.");
  134. }
  135. }
  136. return result;
  137. }
  138. /**
  139. * find a synced folder by local path.
  140. *
  141. * @param localPath the local path of the local folder
  142. * @return the synced folder if found, else null
  143. */
  144. public SyncedFolder findByLocalPath(String localPath) {
  145. SyncedFolder result = null;
  146. Cursor cursor = mContentResolver.query(
  147. ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
  148. null,
  149. ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH + "== \"" + localPath + "\"",
  150. null,
  151. null
  152. );
  153. if (cursor != null && cursor.getCount() == 1) {
  154. result = createSyncedFolderFromCursor(cursor);
  155. cursor.close();
  156. } else {
  157. if (cursor == null) {
  158. Log_OC.e(TAG, "Sync folder db cursor for local path=" + localPath + " in NULL.");
  159. } else {
  160. Log_OC.e(TAG, cursor.getCount() + " items for local path=" + localPath
  161. + " available in sync folder db. Expected 1. Failed to update sync folder db.");
  162. }
  163. }
  164. return result;
  165. }
  166. /**
  167. * Delete all synced folders for an account
  168. *
  169. * @param account whose synced folders should be deleted
  170. */
  171. public int deleteSyncFoldersForAccount(Account account) {
  172. int result = mContentResolver.delete(
  173. ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
  174. ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + " = ?",
  175. new String[]{String.valueOf(account.name)}
  176. );
  177. return result;
  178. }
  179. /**
  180. * Delete a synced folder from the db
  181. *
  182. * @param id for the synced folder.
  183. */
  184. private int deleteSyncFolderWithId(long id) {
  185. return mContentResolver.delete(
  186. ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
  187. ProviderMeta.ProviderTableMeta._ID + " = ?",
  188. new String[]{String.valueOf(id)}
  189. );
  190. }
  191. /**
  192. * Try to figure out if a path exists for synced folder, and if not, go one folder back
  193. * Otherwise, delete the entry
  194. *
  195. * @param context the context.
  196. */
  197. public void updateAutoUploadPaths(Context context) {
  198. List<SyncedFolder> syncedFolders = getSyncedFolders();
  199. for (int i = 0; i < syncedFolders.size(); i++) {
  200. SyncedFolder syncedFolder = syncedFolders.get(i);
  201. if (!new File(syncedFolder.getLocalPath()).exists()) {
  202. String localPath = syncedFolder.getLocalPath();
  203. if (localPath.endsWith("/")) {
  204. localPath = localPath.substring(0, localPath.lastIndexOf("/"));
  205. }
  206. localPath = localPath.substring(0, localPath.lastIndexOf("/"));
  207. if (new File(localPath).exists()) {
  208. syncedFolders.get(i).setLocalPath(localPath);
  209. updateSyncFolder(syncedFolder);
  210. } else {
  211. deleteSyncFolderWithId(syncedFolder.getId());
  212. }
  213. }
  214. }
  215. if (context != null) {
  216. PreferenceManager.setAutoUploadPathsUpdate(context, true);
  217. }
  218. }
  219. /**
  220. * delete any records of synchronized folders that are not within the given list of ids.
  221. *
  222. * @param context the context.
  223. * @param ids the list of ids to be excluded from deletion.
  224. * @return number of deleted records.
  225. */
  226. public int deleteSyncedFoldersNotInList(Context context, ArrayList<Long> ids) {
  227. int result = mContentResolver.delete(
  228. ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
  229. ProviderMeta.ProviderTableMeta._ID + " NOT IN (?)",
  230. new String[]{String.valueOf(ids)}
  231. );
  232. if (result > 0 && context != null) {
  233. PreferenceManager.setLegacyClean(context, true);
  234. }
  235. return result;
  236. }
  237. /**
  238. * delete record of synchronized folder with the given id.
  239. */
  240. public int deleteSyncedFolder(long id) {
  241. return mContentResolver.delete(
  242. ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
  243. ProviderMeta.ProviderTableMeta._ID + " = ?",
  244. new String[]{String.valueOf(id)}
  245. );
  246. }
  247. /**
  248. * update given synced folder.
  249. *
  250. * @param syncedFolder the synced folder to be updated.
  251. * @return the number of rows updated.
  252. */
  253. public int updateSyncFolder(SyncedFolder syncedFolder) {
  254. Log_OC.v(TAG, "Updating " + syncedFolder.getLocalPath() + " with enabled=" + syncedFolder.isEnabled());
  255. ContentValues cv = createContentValuesFromSyncedFolder(syncedFolder);
  256. int result = mContentResolver.update(
  257. ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
  258. cv,
  259. ProviderMeta.ProviderTableMeta._ID + "=?",
  260. new String[]{String.valueOf(syncedFolder.getId())}
  261. );
  262. return result;
  263. }
  264. /**
  265. * maps a cursor into a SyncedFolder object.
  266. *
  267. * @param cursor the db cursor
  268. * @return the mapped SyncedFolder, null if cursor is null
  269. */
  270. private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) {
  271. SyncedFolder syncedFolder = null;
  272. if (cursor != null) {
  273. long id = cursor.getLong(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta._ID));
  274. String localPath = cursor.getString(cursor.getColumnIndex(
  275. ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH));
  276. String remotePath = cursor.getString(cursor.getColumnIndex(
  277. ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH));
  278. Boolean wifiOnly = cursor.getInt(cursor.getColumnIndex(
  279. ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY)) == 1;
  280. Boolean chargingOnly = cursor.getInt(cursor.getColumnIndex(
  281. ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY)) == 1;
  282. Boolean subfolderByDate = cursor.getInt(cursor.getColumnIndex(
  283. ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE)) == 1;
  284. String accountName = cursor.getString(cursor.getColumnIndex(
  285. ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT));
  286. Integer uploadAction = cursor.getInt(cursor.getColumnIndex(
  287. ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION));
  288. Boolean enabled = cursor.getInt(cursor.getColumnIndex(
  289. ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED)) == 1;
  290. MediaFolderType type = MediaFolderType.getById(cursor.getInt(cursor.getColumnIndex(
  291. ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE)));
  292. syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate,
  293. accountName, uploadAction, enabled, type);
  294. }
  295. return syncedFolder;
  296. }
  297. /**
  298. * create ContentValues object based on given SyncedFolder.
  299. *
  300. * @param syncedFolder the synced folder
  301. * @return the corresponding ContentValues object
  302. */
  303. @NonNull
  304. private ContentValues createContentValuesFromSyncedFolder(SyncedFolder syncedFolder) {
  305. ContentValues cv = new ContentValues();
  306. cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH, syncedFolder.getLocalPath());
  307. cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH, syncedFolder.getRemotePath());
  308. cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY, syncedFolder.getWifiOnly());
  309. cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY, syncedFolder.getChargingOnly());
  310. cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED, syncedFolder.isEnabled());
  311. cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE, syncedFolder.getSubfolderByDate());
  312. cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT, syncedFolder.getAccount());
  313. cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION, syncedFolder.getUploadAction());
  314. cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE, syncedFolder.getType().getId());
  315. return cv;
  316. }
  317. }