FilesSyncHelper.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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.BatteryStatus;
  35. import com.nextcloud.client.device.PowerManagementService;
  36. import com.nextcloud.client.jobs.BackgroundJobManager;
  37. import com.nextcloud.client.network.ConnectivityService;
  38. import com.nextcloud.client.preferences.AppPreferences;
  39. import com.owncloud.android.MainApp;
  40. import com.owncloud.android.datamodel.FilesystemDataProvider;
  41. import com.owncloud.android.datamodel.MediaFolderType;
  42. import com.owncloud.android.datamodel.SyncedFolder;
  43. import com.owncloud.android.datamodel.SyncedFolderProvider;
  44. import com.owncloud.android.datamodel.UploadsStorageManager;
  45. import com.owncloud.android.db.OCUpload;
  46. import com.owncloud.android.files.services.FileUploader;
  47. import com.owncloud.android.lib.common.utils.Log_OC;
  48. import org.lukhnos.nnio.file.FileVisitResult;
  49. import org.lukhnos.nnio.file.Files;
  50. import org.lukhnos.nnio.file.Path;
  51. import org.lukhnos.nnio.file.Paths;
  52. import org.lukhnos.nnio.file.SimpleFileVisitor;
  53. import org.lukhnos.nnio.file.attribute.BasicFileAttributes;
  54. import java.io.File;
  55. import java.io.IOException;
  56. import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR;
  57. /**
  58. * Various utilities that make auto upload tick
  59. */
  60. public final class FilesSyncHelper {
  61. public static final String TAG = "FileSyncHelper";
  62. public static final String GLOBAL = "global";
  63. public static final int ContentSyncJobId = 315;
  64. private FilesSyncHelper() {
  65. // utility class -> private constructor
  66. }
  67. private static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder) {
  68. final Context context = MainApp.getAppContext();
  69. final ContentResolver contentResolver = context.getContentResolver();
  70. final long enabledTimestampMs = syncedFolder.getEnabledTimestampMs();
  71. if (syncedFolder.isEnabled() && (syncedFolder.isExisting() || enabledTimestampMs >= 0)) {
  72. MediaFolderType mediaType = syncedFolder.getType();
  73. if (mediaType == MediaFolderType.IMAGE) {
  74. FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.INTERNAL_CONTENT_URI
  75. , syncedFolder);
  76. FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
  77. syncedFolder);
  78. } else if (mediaType == MediaFolderType.VIDEO) {
  79. FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.INTERNAL_CONTENT_URI,
  80. syncedFolder);
  81. FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
  82. syncedFolder);
  83. } else {
  84. try {
  85. FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver);
  86. Path path = Paths.get(syncedFolder.getLocalPath());
  87. Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
  88. @Override
  89. public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
  90. File file = path.toFile();
  91. if (syncedFolder.isExisting() || attrs.lastModifiedTime().toMillis() >= enabledTimestampMs) {
  92. filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(),
  93. attrs.lastModifiedTime().toMillis(),
  94. file.isDirectory(), syncedFolder);
  95. }
  96. return FileVisitResult.CONTINUE;
  97. }
  98. @Override
  99. public FileVisitResult visitFileFailed(Path file, IOException exc) {
  100. return FileVisitResult.CONTINUE;
  101. }
  102. });
  103. } catch (IOException e) {
  104. Log_OC.e(TAG, "Something went wrong while indexing files for auto upload", e);
  105. }
  106. }
  107. }
  108. }
  109. public static void insertAllDBEntries(AppPreferences preferences,
  110. Clock clock,
  111. boolean skipCustom) {
  112. final Context context = MainApp.getAppContext();
  113. final ContentResolver contentResolver = context.getContentResolver();
  114. SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver, preferences, clock);
  115. for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
  116. if (syncedFolder.isEnabled() && (!skipCustom || syncedFolder.getType() != MediaFolderType.CUSTOM)) {
  117. insertAllDBEntriesForSyncedFolder(syncedFolder);
  118. }
  119. }
  120. }
  121. private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder) {
  122. final Context context = MainApp.getAppContext();
  123. final ContentResolver contentResolver = context.getContentResolver();
  124. Cursor cursor;
  125. int column_index_data;
  126. int column_index_date_modified;
  127. final FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver);
  128. String contentPath;
  129. boolean isFolder;
  130. String[] projection = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DATE_MODIFIED};
  131. String path = syncedFolder.getLocalPath();
  132. if (!path.endsWith(PATH_SEPARATOR)) {
  133. path = path + PATH_SEPARATOR;
  134. }
  135. path = path + "%";
  136. long enabledTimestampMs = syncedFolder.getEnabledTimestampMs();
  137. cursor = context.getContentResolver().query(uri, projection, MediaStore.MediaColumns.DATA + " LIKE ?",
  138. new String[]{path}, null);
  139. if (cursor != null) {
  140. column_index_data = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
  141. column_index_date_modified = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED);
  142. while (cursor.moveToNext()) {
  143. contentPath = cursor.getString(column_index_data);
  144. isFolder = new File(contentPath).isDirectory();
  145. if (syncedFolder.isExisting() || cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000.0) {
  146. filesystemDataProvider.storeOrUpdateFileValue(contentPath,
  147. cursor.getLong(column_index_date_modified), isFolder,
  148. syncedFolder);
  149. }
  150. }
  151. cursor.close();
  152. }
  153. }
  154. public static void restartJobsIfNeeded(final UploadsStorageManager uploadsStorageManager,
  155. final UserAccountManager accountManager,
  156. final ConnectivityService connectivityService,
  157. final PowerManagementService powerManagementService) {
  158. final Context context = MainApp.getAppContext();
  159. boolean accountExists;
  160. boolean whileChargingOnly = true;
  161. boolean useWifiOnly = true;
  162. OCUpload[] failedUploads = uploadsStorageManager.getFailedUploads();
  163. for (OCUpload failedUpload : failedUploads) {
  164. accountExists = false;
  165. if(!failedUpload.isWhileChargingOnly()){
  166. whileChargingOnly = false;
  167. }
  168. if(!failedUpload.isUseWifiOnly())
  169. {
  170. useWifiOnly = false;
  171. }
  172. // check if accounts still exists
  173. for (Account account : accountManager.getAccounts()) {
  174. if (account.name.equals(failedUpload.getAccountName())) {
  175. accountExists = true;
  176. break;
  177. }
  178. }
  179. if (!accountExists) {
  180. uploadsStorageManager.removeUpload(failedUpload);
  181. }
  182. }
  183. failedUploads = uploadsStorageManager.getFailedUploads();
  184. if(failedUploads.length == 0)
  185. {
  186. //nothing to do
  187. return;
  188. }
  189. if(whileChargingOnly){
  190. final BatteryStatus batteryStatus = powerManagementService.getBattery();
  191. final boolean charging = batteryStatus.isCharging() || batteryStatus.isFull();
  192. if(!charging){
  193. //all uploads requires charging
  194. return;
  195. }
  196. }
  197. if (useWifiOnly && !connectivityService.getConnectivity().isWifi()){
  198. //all uploads requires wifi
  199. return;
  200. }
  201. new Thread(() -> {
  202. if (connectivityService.getConnectivity().isConnected() && !connectivityService.isInternetWalled()) {
  203. FileUploader.retryFailedUploads(
  204. context,
  205. null,
  206. uploadsStorageManager,
  207. connectivityService,
  208. accountManager,
  209. powerManagementService,
  210. null
  211. );
  212. }
  213. }).start();
  214. }
  215. public static void scheduleFilesSyncIfNeeded(Context context, BackgroundJobManager jobManager) {
  216. jobManager.schedulePeriodicFilesSyncJob();
  217. if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
  218. jobManager.scheduleContentObserverJob();
  219. }
  220. }
  221. }