ConflictsResolveActivity.java 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*
  2. * ownCloud Android client application
  3. *
  4. * @author Bartek Przybylski
  5. * @author David A. Velasco Copyright (C) 2012 Bartek Przybylski Copyright (C) 2016 ownCloud Inc.
  6. * <p>
  7. * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
  8. * License version 2, as published by the Free Software Foundation.
  9. * <p>
  10. * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
  11. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  12. * details.
  13. * <p/>
  14. * You should have received a copy of the GNU General Public License along with this program. If not, see
  15. * <http://www.gnu.org/licenses/>.
  16. */
  17. package com.owncloud.android.ui.activity;
  18. import android.accounts.Account;
  19. import android.content.Context;
  20. import android.content.Intent;
  21. import android.os.Bundle;
  22. import android.widget.Toast;
  23. import com.nextcloud.client.account.User;
  24. import com.nextcloud.java.util.Optional;
  25. import com.owncloud.android.R;
  26. import com.owncloud.android.datamodel.OCFile;
  27. import com.owncloud.android.datamodel.UploadsStorageManager;
  28. import com.owncloud.android.db.OCUpload;
  29. import com.owncloud.android.files.services.FileDownloader;
  30. import com.owncloud.android.files.services.FileUploader;
  31. import com.owncloud.android.files.services.NameCollisionPolicy;
  32. import com.owncloud.android.lib.common.operations.RemoteOperationResult;
  33. import com.owncloud.android.lib.common.utils.Log_OC;
  34. import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation;
  35. import com.owncloud.android.lib.resources.files.model.RemoteFile;
  36. import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
  37. import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision;
  38. import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener;
  39. import com.owncloud.android.utils.FileStorageUtils;
  40. import javax.inject.Inject;
  41. import androidx.annotation.NonNull;
  42. import androidx.fragment.app.Fragment;
  43. import androidx.fragment.app.FragmentTransaction;
  44. /**
  45. * Wrapper activity which will be launched if keep-in-sync file will be modified by external application.
  46. */
  47. public class ConflictsResolveActivity extends FileActivity implements OnConflictDecisionMadeListener {
  48. /**
  49. * A nullable upload entry that must be removed when and if the conflict is resolved.
  50. */
  51. public static final String EXTRA_CONFLICT_UPLOAD_ID = "CONFLICT_UPLOAD_ID";
  52. /**
  53. * Specify the upload local behaviour when there is no CONFLICT_UPLOAD.
  54. */
  55. public static final String EXTRA_LOCAL_BEHAVIOUR = "LOCAL_BEHAVIOUR";
  56. public static final String EXTRA_EXISTING_FILE = "EXISTING_FILE";
  57. private static final String TAG = ConflictsResolveActivity.class.getSimpleName();
  58. @Inject UploadsStorageManager uploadsStorageManager;
  59. private long conflictUploadId;
  60. private OCFile existingFile;
  61. private OCFile newFile;
  62. private int localBehaviour = FileUploader.LOCAL_BEHAVIOUR_FORGET;
  63. protected OnConflictDecisionMadeListener listener;
  64. public static Intent createIntent(OCFile file,
  65. User user,
  66. long conflictUploadId,
  67. Integer flag,
  68. Context context) {
  69. Intent intent = new Intent(context, ConflictsResolveActivity.class);
  70. if (flag != null) {
  71. intent.setFlags(intent.getFlags() | flag);
  72. }
  73. intent.putExtra(EXTRA_FILE, file);
  74. intent.putExtra(EXTRA_USER, user);
  75. intent.putExtra(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId);
  76. return intent;
  77. }
  78. @Override
  79. protected void onCreate(Bundle savedInstanceState) {
  80. super.onCreate(savedInstanceState);
  81. if (savedInstanceState != null) {
  82. conflictUploadId = savedInstanceState.getLong(EXTRA_CONFLICT_UPLOAD_ID);
  83. existingFile = savedInstanceState.getParcelable(EXTRA_EXISTING_FILE);
  84. localBehaviour = savedInstanceState.getInt(EXTRA_LOCAL_BEHAVIOUR);
  85. } else {
  86. conflictUploadId = getIntent().getLongExtra(EXTRA_CONFLICT_UPLOAD_ID, -1);
  87. existingFile = getIntent().getParcelableExtra(EXTRA_EXISTING_FILE);
  88. localBehaviour = getIntent().getIntExtra(EXTRA_LOCAL_BEHAVIOUR, localBehaviour);
  89. }
  90. OCUpload upload = uploadsStorageManager.getUploadById(conflictUploadId);
  91. if (upload != null) {
  92. localBehaviour = upload.getLocalAction();
  93. }
  94. // new file was modified locally in file system
  95. newFile = getFile();
  96. listener = decision -> {
  97. OCFile file = newFile; // local file got changed, so either upload it or replace it again by server
  98. // version
  99. switch (decision) {
  100. case CANCEL:
  101. // nothing to do
  102. break;
  103. case KEEP_LOCAL: // Upload
  104. FileUploader.uploadUpdateFile(
  105. getBaseContext(),
  106. getAccount(),
  107. file,
  108. localBehaviour,
  109. NameCollisionPolicy.OVERWRITE
  110. );
  111. uploadsStorageManager.removeUpload(upload);
  112. break;
  113. case KEEP_BOTH: // Upload
  114. FileUploader.uploadUpdateFile(
  115. getBaseContext(),
  116. getAccount(),
  117. file,
  118. localBehaviour,
  119. NameCollisionPolicy.RENAME
  120. );
  121. uploadsStorageManager.removeUpload(upload);
  122. break;
  123. case KEEP_SERVER: // Download
  124. if (!shouldDeleteLocal()) {
  125. // Overwrite local file
  126. Intent intent = new Intent(getBaseContext(), FileDownloader.class);
  127. intent.putExtra(FileDownloader.EXTRA_USER, getUser().orElseThrow(RuntimeException::new));
  128. intent.putExtra(FileDownloader.EXTRA_FILE, file);
  129. intent.putExtra(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId);
  130. startService(intent);
  131. } else {
  132. uploadsStorageManager.removeUpload(upload);
  133. }
  134. break;
  135. }
  136. finish();
  137. };
  138. }
  139. @Override
  140. protected void onSaveInstanceState(@NonNull Bundle outState) {
  141. super.onSaveInstanceState(outState);
  142. outState.putLong(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId);
  143. outState.putParcelable(EXTRA_EXISTING_FILE, existingFile);
  144. outState.putInt(EXTRA_LOCAL_BEHAVIOUR, localBehaviour);
  145. }
  146. @Override
  147. public void conflictDecisionMade(Decision decision) {
  148. listener.conflictDecisionMade(decision);
  149. }
  150. @Override
  151. protected void onStart() {
  152. super.onStart();
  153. if (getAccount() == null) {
  154. finish();
  155. return;
  156. }
  157. if (newFile == null) {
  158. Log_OC.e(TAG, "No file received");
  159. finish();
  160. return;
  161. }
  162. if (existingFile == null) {
  163. // fetch info of existing file from server
  164. ReadFileRemoteOperation operation = new ReadFileRemoteOperation(newFile.getRemotePath());
  165. new Thread(() -> {
  166. try {
  167. RemoteOperationResult result = operation.execute(getAccount(), this);
  168. if (result.isSuccess()) {
  169. existingFile = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
  170. existingFile.setLastSyncDateForProperties(System.currentTimeMillis());
  171. startDialog();
  172. } else {
  173. showErrorAndFinish();
  174. }
  175. } catch (Exception e) {
  176. showErrorAndFinish();
  177. }
  178. }).start();
  179. } else {
  180. startDialog();
  181. }
  182. }
  183. private void startDialog() {
  184. Optional<User> userOptional = getUser();
  185. if (!userOptional.isPresent()) {
  186. showErrorAndFinish();
  187. }
  188. // Check whether the file is contained in the current Account
  189. Fragment prev = getSupportFragmentManager().findFragmentByTag("conflictDialog");
  190. FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
  191. if (prev != null) {
  192. fragmentTransaction.remove(prev);
  193. }
  194. if (existingFile != null && getStorageManager().fileExists(newFile.getRemotePath())) {
  195. ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(existingFile,
  196. newFile,
  197. userOptional.get());
  198. dialog.show(fragmentTransaction, "conflictDialog");
  199. } else {
  200. // Account was changed to a different one - just finish
  201. showErrorAndFinish();
  202. }
  203. }
  204. private void showErrorAndFinish() {
  205. runOnUiThread(() -> Toast.makeText(this, R.string.conflict_dialog_error, Toast.LENGTH_LONG).show());
  206. finish();
  207. }
  208. /**
  209. * @return whether the local version of the files is to be deleted.
  210. */
  211. private boolean shouldDeleteLocal() {
  212. return localBehaviour == FileUploader.LOCAL_BEHAVIOUR_DELETE;
  213. }
  214. }