SynchronizeFileOperation.java 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /* ownCloud Android client application
  2. * Copyright (C) 2012 Bartek Przybylski
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. */
  18. package com.owncloud.android.operations;
  19. import org.apache.http.HttpStatus;
  20. import org.apache.jackrabbit.webdav.MultiStatus;
  21. import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
  22. import android.accounts.Account;
  23. import android.content.Context;
  24. import android.content.Intent;
  25. import android.util.Log;
  26. import com.owncloud.android.datamodel.DataStorageManager;
  27. import com.owncloud.android.datamodel.OCFile;
  28. import com.owncloud.android.files.services.FileDownloader;
  29. import com.owncloud.android.files.services.FileUploader;
  30. import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
  31. import eu.alefzero.webdav.WebdavClient;
  32. import eu.alefzero.webdav.WebdavEntry;
  33. import eu.alefzero.webdav.WebdavUtils;
  34. public class SynchronizeFileOperation extends RemoteOperation {
  35. private String TAG = SynchronizeFileOperation.class.getSimpleName();
  36. private String mRemotePath;
  37. private DataStorageManager mStorageManager;
  38. private Account mAccount;
  39. private boolean mSyncFileContents;
  40. private boolean mLocalChangeAlreadyKnown;
  41. private Context mContext;
  42. private boolean mTransferWasRequested = false;
  43. public SynchronizeFileOperation(
  44. String remotePath,
  45. DataStorageManager dataStorageManager,
  46. Account account,
  47. boolean syncFileContents,
  48. boolean localChangeAlreadyKnown,
  49. Context context) {
  50. mRemotePath = remotePath;
  51. mStorageManager = dataStorageManager;
  52. mAccount = account;
  53. mSyncFileContents = syncFileContents;
  54. mLocalChangeAlreadyKnown = localChangeAlreadyKnown;
  55. mContext = context;
  56. }
  57. @Override
  58. protected RemoteOperationResult run(WebdavClient client) {
  59. PropFindMethod propfind = null;
  60. RemoteOperationResult result = null;
  61. mTransferWasRequested = false;
  62. try {
  63. OCFile localFile = mStorageManager.getFileByPath(mRemotePath);
  64. if (!localFile.isDown()) {
  65. /// easy decision
  66. requestForDownload(localFile);
  67. result = new RemoteOperationResult(ResultCode.OK);
  68. } else {
  69. /// local copy in the device -> need to think a bit more before do nothing
  70. propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
  71. int status = client.executeMethod(propfind);
  72. boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS;
  73. if (isMultiStatus) {
  74. MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
  75. WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
  76. client.getBaseUri().getPath());
  77. OCFile serverFile = fillOCFile(we);
  78. /// check changes in server and local file
  79. boolean serverChanged = false;
  80. if (serverFile.getEtag() != null) {
  81. serverChanged = (!serverFile.getEtag().equals(localFile.getEtag()));
  82. } else {
  83. // server without etags
  84. serverChanged = (serverFile.getModificationTimestamp() > localFile.getModificationTimestamp());
  85. }
  86. boolean localChanged = (mLocalChangeAlreadyKnown || localFile.getLocalModificationTimestamp() > localFile.getLastSyncDateForData());
  87. /// decide action to perform depending upon changes
  88. if (localChanged && serverChanged) {
  89. // conflict
  90. result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
  91. } else if (localChanged) {
  92. if (mSyncFileContents) {
  93. requestForUpload(localFile);
  94. // the local update of file properties will be done by the FileUploader service when the upload finishes
  95. } else {
  96. // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid;
  97. // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
  98. // that an upload is necessary (for instance, in FileObserverService).
  99. }
  100. result = new RemoteOperationResult(ResultCode.OK);
  101. } else if (serverChanged) {
  102. if (mSyncFileContents) {
  103. requestForDownload(serverFile);
  104. // the update of local data will be done later by the FileUploader service when the upload finishes
  105. } else {
  106. // TODO CHECK: is this really useful in some point in the code?
  107. serverFile.setKeepInSync(localFile.keepInSync());
  108. serverFile.setParentId(localFile.getParentId());
  109. mStorageManager.saveFile(serverFile);
  110. }
  111. result = new RemoteOperationResult(ResultCode.OK);
  112. } else {
  113. // nothing changed, nothing to do
  114. result = new RemoteOperationResult(ResultCode.OK);
  115. }
  116. } else {
  117. client.exhaustResponse(propfind.getResponseBodyAsStream());
  118. result = new RemoteOperationResult(false, status);
  119. }
  120. }
  121. Log.i(TAG, "Synchronizing " + mAccount.name + ", file " + mRemotePath + ": " + result.getLogMessage());
  122. } catch (Exception e) {
  123. result = new RemoteOperationResult(e);
  124. Log.e(TAG, "Synchronizing " + mAccount.name + ", file " + mRemotePath + ": " + result.getLogMessage(), result.getException());
  125. } finally {
  126. if (propfind != null)
  127. propfind.releaseConnection();
  128. }
  129. return result;
  130. }
  131. /**
  132. * Requests for an upload to the FileUploader service
  133. *
  134. * @param localFile OCFile object representing the file to upload
  135. */
  136. private void requestForUpload(OCFile localFile) {
  137. Intent i = new Intent(mContext, FileUploader.class);
  138. i.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
  139. i.putExtra(FileUploader.KEY_REMOTE_FILE, mRemotePath);
  140. i.putExtra(FileUploader.KEY_LOCAL_FILE, localFile.getStoragePath());
  141. i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
  142. i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
  143. mContext.startService(i);
  144. mTransferWasRequested = true;
  145. }
  146. /**
  147. * Requests for a download to the FileDownloader service
  148. *
  149. * @param file OCFile object representing the file to download
  150. */
  151. private void requestForDownload(OCFile file) {
  152. Intent i = new Intent(mContext, FileDownloader.class);
  153. i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
  154. i.putExtra(FileDownloader.EXTRA_FILE, file);
  155. mContext.startService(i);
  156. mTransferWasRequested = true;
  157. }
  158. /**
  159. * Creates and populates a new {@link OCFile} object with the data read from the server.
  160. *
  161. * @param we WebDAV entry read from the server for a WebDAV resource (remote file or folder).
  162. * @return New OCFile instance representing the remote resource described by we.
  163. */
  164. private OCFile fillOCFile(WebdavEntry we) {
  165. OCFile file = new OCFile(we.decodedPath());
  166. file.setCreationTimestamp(we.createTimestamp());
  167. file.setFileLength(we.contentLength());
  168. file.setMimetype(we.contentType());
  169. file.setModificationTimestamp(we.modifiedTimesamp());
  170. file.setLastSyncDateForProperties(System.currentTimeMillis());
  171. file.setLastSyncDateForData(0);
  172. return file;
  173. }
  174. public boolean transferWasRequested() {
  175. return mTransferWasRequested;
  176. }
  177. }