OfflineSyncJob.java 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Mario Danic
  5. * @author Chris Narkiewicz
  6. * Copyright (C) 2018 Mario Danic
  7. * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  11. * License as published by the Free Software Foundation; either
  12. * version 3 of the License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public
  20. * License along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. package com.owncloud.android.jobs;
  23. import android.accounts.Account;
  24. import android.content.Context;
  25. import android.os.Build;
  26. import android.os.PowerManager;
  27. import com.evernote.android.job.Job;
  28. import com.evernote.android.job.JobManager;
  29. import com.evernote.android.job.JobRequest;
  30. import com.nextcloud.client.account.UserAccountManager;
  31. import com.nextcloud.client.device.PowerManagementService;
  32. import com.nextcloud.client.network.ConnectivityService;
  33. import com.owncloud.android.MainApp;
  34. import com.owncloud.android.datamodel.FileDataStorageManager;
  35. import com.owncloud.android.datamodel.OCFile;
  36. import com.owncloud.android.lib.common.operations.RemoteOperationResult;
  37. import com.owncloud.android.lib.common.utils.Log_OC;
  38. import com.owncloud.android.lib.resources.files.CheckEtagRemoteOperation;
  39. import com.owncloud.android.operations.SynchronizeFileOperation;
  40. import com.owncloud.android.utils.FileStorageUtils;
  41. import java.io.File;
  42. import java.util.Set;
  43. import androidx.annotation.NonNull;
  44. import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR;
  45. import static com.owncloud.android.datamodel.OCFile.ROOT_PATH;
  46. public class OfflineSyncJob extends Job {
  47. public static final String TAG = "OfflineSyncJob";
  48. private static final String WAKELOCK_TAG_SEPARATION = ":";
  49. private final UserAccountManager userAccountManager;
  50. private final ConnectivityService connectivityService;
  51. private final PowerManagementService powerManagementService;
  52. OfflineSyncJob(UserAccountManager userAccountManager, ConnectivityService connectivityService, PowerManagementService powerManagementService) {
  53. this.userAccountManager = userAccountManager;
  54. this.connectivityService = connectivityService;
  55. this.powerManagementService = powerManagementService;
  56. }
  57. @NonNull
  58. @Override
  59. protected Result onRunJob(@NonNull Params params) {
  60. final Context context = getContext();
  61. PowerManager.WakeLock wakeLock = null;
  62. if (!powerManagementService.isPowerSavingEnabled() &&
  63. connectivityService.getActiveNetworkType() == JobRequest.NetworkType.UNMETERED &&
  64. !connectivityService.isInternetWalled()) {
  65. Set<Job> jobs = JobManager.instance().getAllJobsForTag(TAG);
  66. for (Job job : jobs) {
  67. if (!job.isFinished() && !job.equals(this)) {
  68. return Result.SUCCESS;
  69. }
  70. }
  71. if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
  72. PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
  73. if (powerManager != null) {
  74. wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, MainApp.getAuthority() +
  75. WAKELOCK_TAG_SEPARATION + TAG);
  76. wakeLock.acquire(10 * 60 * 1000);
  77. }
  78. }
  79. Account[] accounts = userAccountManager.getAccounts();
  80. for (Account account : accounts) {
  81. FileDataStorageManager storageManager = new FileDataStorageManager(account,
  82. getContext().getContentResolver());
  83. OCFile ocRoot = storageManager.getFileByPath(ROOT_PATH);
  84. if (ocRoot.getStoragePath() == null) {
  85. break;
  86. }
  87. recursive(new File(ocRoot.getStoragePath()), storageManager, account);
  88. }
  89. if (wakeLock != null) {
  90. wakeLock.release();
  91. }
  92. }
  93. return Result.SUCCESS;
  94. }
  95. private void recursive(File folder, FileDataStorageManager storageManager, Account account) {
  96. String downloadFolder = FileStorageUtils.getSavePath(account.name);
  97. String folderName = folder.getAbsolutePath().replaceFirst(downloadFolder, "") + PATH_SEPARATOR;
  98. Log_OC.d(TAG, folderName + ": enter");
  99. // exit
  100. if (folder.listFiles() == null) {
  101. return;
  102. }
  103. OCFile ocFolder = storageManager.getFileByPath(folderName);
  104. Log_OC.d(TAG, folderName + ": currentEtag: " + ocFolder.getEtag());
  105. // check for etag change, if false, skip
  106. CheckEtagRemoteOperation checkEtagOperation = new CheckEtagRemoteOperation(ocFolder.getRemotePath(),
  107. ocFolder.getEtagOnServer());
  108. RemoteOperationResult result = checkEtagOperation.execute(account, getContext());
  109. // eTag changed, sync file
  110. switch (result.getCode()) {
  111. case ETAG_UNCHANGED:
  112. Log_OC.d(TAG, folderName + ": eTag unchanged");
  113. return;
  114. case FILE_NOT_FOUND:
  115. boolean removalResult = storageManager.removeFolder(ocFolder, true, true);
  116. if (!removalResult) {
  117. Log_OC.e(TAG, "removal of " + ocFolder.getStoragePath() + " failed: file not found");
  118. }
  119. return;
  120. default:
  121. case ETAG_CHANGED:
  122. Log_OC.d(TAG, folderName + ": eTag changed");
  123. break;
  124. }
  125. // iterate over downloaded files
  126. File[] files = folder.listFiles(File::isFile);
  127. if (files != null) {
  128. for (File file : files) {
  129. OCFile ocFile = storageManager.getFileByLocalPath(file.getPath());
  130. SynchronizeFileOperation synchronizeFileOperation = new SynchronizeFileOperation(ocFile.getRemotePath(),
  131. account, true, getContext());
  132. synchronizeFileOperation.execute(storageManager, getContext());
  133. }
  134. }
  135. // recursive into folder
  136. File[] subfolders = folder.listFiles(File::isDirectory);
  137. if (subfolders != null) {
  138. for (File subfolder : subfolders) {
  139. recursive(subfolder, storageManager, account);
  140. }
  141. }
  142. // update eTag
  143. try {
  144. String updatedEtag = (String) result.getData().get(0);
  145. ocFolder.setEtagOnServer(updatedEtag);
  146. storageManager.saveFile(ocFolder);
  147. } catch (Exception e) {
  148. Log_OC.e(TAG, "Failed to update etag on " + folder.getAbsolutePath(), e);
  149. }
  150. }
  151. }