SyncedFolderProvider.java 14 KB

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