FilesSyncHelper.java 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Mario Danic
  5. * @author Chris Narkiewicz
  6. *
  7. * Copyright (C) 2017 Mario Danic
  8. * Copyright (C) 2017 Nextcloud
  9. * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  13. * License as published by the Free Software Foundation; either
  14. * version 3 of the License, or any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  20. *
  21. * You should have received a copy of the GNU Affero General Public
  22. * License along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. */
  24. package com.owncloud.android.utils;
  25. import android.accounts.Account;
  26. import android.content.ContentResolver;
  27. import android.content.Context;
  28. import android.database.Cursor;
  29. import android.net.Uri;
  30. import android.os.Build;
  31. import android.provider.MediaStore;
  32. import com.nextcloud.client.account.UserAccountManager;
  33. import com.nextcloud.client.core.Clock;
  34. import com.nextcloud.client.device.PowerManagementService;
  35. import com.nextcloud.client.jobs.BackgroundJobManager;
  36. import com.nextcloud.client.network.ConnectivityService;
  37. import com.nextcloud.client.preferences.AppPreferences;
  38. import com.owncloud.android.MainApp;
  39. import com.owncloud.android.datamodel.FilesystemDataProvider;
  40. import com.owncloud.android.datamodel.MediaFolderType;
  41. import com.owncloud.android.datamodel.SyncedFolder;
  42. import com.owncloud.android.datamodel.SyncedFolderProvider;
  43. import com.owncloud.android.datamodel.UploadsStorageManager;
  44. import com.owncloud.android.db.OCUpload;
  45. import com.owncloud.android.files.services.FileUploader;
  46. import com.owncloud.android.lib.common.utils.Log_OC;
  47. import org.lukhnos.nnio.file.FileVisitResult;
  48. import org.lukhnos.nnio.file.Files;
  49. import org.lukhnos.nnio.file.Path;
  50. import org.lukhnos.nnio.file.Paths;
  51. import org.lukhnos.nnio.file.SimpleFileVisitor;
  52. import org.lukhnos.nnio.file.attribute.BasicFileAttributes;
  53. import java.io.File;
  54. import java.io.IOException;
  55. import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR;
  56. /**
  57. * Various utilities that make auto upload tick
  58. */
  59. public final class FilesSyncHelper {
  60. public static final String TAG = "FileSyncHelper";
  61. public static final String GLOBAL = "global";
  62. public static final int ContentSyncJobId = 315;
  63. private FilesSyncHelper() {
  64. // utility class -> private constructor
  65. }
  66. private static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder) {
  67. final Context context = MainApp.getAppContext();
  68. final ContentResolver contentResolver = context.getContentResolver();
  69. final long enabledTimestampMs = syncedFolder.getEnabledTimestampMs();
  70. if (syncedFolder.isEnabled() && (syncedFolder.isExisting() || enabledTimestampMs >= 0)) {
  71. MediaFolderType mediaType = syncedFolder.getType();
  72. if (mediaType == MediaFolderType.IMAGE) {
  73. FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.INTERNAL_CONTENT_URI
  74. , syncedFolder);
  75. FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
  76. syncedFolder);
  77. } else if (mediaType == MediaFolderType.VIDEO) {
  78. FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.INTERNAL_CONTENT_URI,
  79. syncedFolder);
  80. FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
  81. syncedFolder);
  82. } else {
  83. try {
  84. FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver);
  85. Path path = Paths.get(syncedFolder.getLocalPath());
  86. Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
  87. @Override
  88. public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
  89. File file = path.toFile();
  90. if (syncedFolder.isExisting() || attrs.lastModifiedTime().toMillis() >= enabledTimestampMs) {
  91. filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(),
  92. attrs.lastModifiedTime().toMillis(),
  93. file.isDirectory(), syncedFolder);
  94. }
  95. return FileVisitResult.CONTINUE;
  96. }
  97. @Override
  98. public FileVisitResult visitFileFailed(Path file, IOException exc) {
  99. return FileVisitResult.CONTINUE;
  100. }
  101. });
  102. } catch (IOException e) {
  103. Log_OC.e(TAG, "Something went wrong while indexing files for auto upload", e);
  104. }
  105. }
  106. }
  107. }
  108. public static void insertAllDBEntries(AppPreferences preferences,
  109. Clock clock,
  110. boolean skipCustom) {
  111. final Context context = MainApp.getAppContext();
  112. final ContentResolver contentResolver = context.getContentResolver();
  113. SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver, preferences, clock);
  114. for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
  115. if (syncedFolder.isEnabled() && (!skipCustom || syncedFolder.getType() != MediaFolderType.CUSTOM)) {
  116. insertAllDBEntriesForSyncedFolder(syncedFolder);
  117. }
  118. }
  119. }
  120. private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder) {
  121. final Context context = MainApp.getAppContext();
  122. final ContentResolver contentResolver = context.getContentResolver();
  123. Cursor cursor;
  124. int column_index_data;
  125. int column_index_date_modified;
  126. final FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver);
  127. String contentPath;
  128. boolean isFolder;
  129. String[] projection = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DATE_MODIFIED};
  130. String path = syncedFolder.getLocalPath();
  131. if (!path.endsWith(PATH_SEPARATOR)) {
  132. path = path + PATH_SEPARATOR;
  133. }
  134. path = path + "%";
  135. long enabledTimestampMs = syncedFolder.getEnabledTimestampMs();
  136. cursor = context.getContentResolver().query(uri, projection, MediaStore.MediaColumns.DATA + " LIKE ?",
  137. new String[]{path}, null);
  138. if (cursor != null) {
  139. column_index_data = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
  140. column_index_date_modified = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED);
  141. while (cursor.moveToNext()) {
  142. contentPath = cursor.getString(column_index_data);
  143. isFolder = new File(contentPath).isDirectory();
  144. if (syncedFolder.isExisting() || cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000.0) {
  145. filesystemDataProvider.storeOrUpdateFileValue(contentPath,
  146. cursor.getLong(column_index_date_modified), isFolder,
  147. syncedFolder);
  148. }
  149. }
  150. cursor.close();
  151. }
  152. }
  153. public static void restartJobsIfNeeded(final UploadsStorageManager uploadsStorageManager,
  154. final UserAccountManager accountManager,
  155. final ConnectivityService connectivityService,
  156. final PowerManagementService powerManagementService) {
  157. final Context context = MainApp.getAppContext();
  158. boolean accountExists;
  159. OCUpload[] failedUploads = uploadsStorageManager.getFailedUploads();
  160. for (OCUpload failedUpload : failedUploads) {
  161. accountExists = false;
  162. // check if accounts still exists
  163. for (Account account : accountManager.getAccounts()) {
  164. if (account.name.equals(failedUpload.getAccountName())) {
  165. accountExists = true;
  166. break;
  167. }
  168. }
  169. if (!accountExists) {
  170. uploadsStorageManager.removeUpload(failedUpload);
  171. }
  172. }
  173. new Thread(() -> {
  174. if (connectivityService.getConnectivity().isConnected() && !connectivityService.isInternetWalled()) {
  175. FileUploader.retryFailedUploads(
  176. context,
  177. null,
  178. uploadsStorageManager,
  179. connectivityService,
  180. accountManager,
  181. powerManagementService,
  182. null
  183. );
  184. }
  185. }).start();
  186. }
  187. public static void scheduleFilesSyncIfNeeded(Context context, BackgroundJobManager jobManager) {
  188. jobManager.schedulePeriodicFilesSyncJob();
  189. if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
  190. jobManager.scheduleContentObserverJob();
  191. }
  192. }
  193. }