1 /* ownCloud Android client application
2 * Copyright (C) 2012 Bartek Przybylski
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.
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.
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/>.
19 package com
.owncloud
.android
.operations
;
21 import org
.apache
.http
.HttpStatus
;
22 import org
.apache
.jackrabbit
.webdav
.MultiStatus
;
23 import org
.apache
.jackrabbit
.webdav
.client
.methods
.PropFindMethod
;
25 import android
.accounts
.Account
;
26 import android
.content
.Context
;
27 import android
.content
.Intent
;
28 import android
.util
.Log
;
30 import com
.owncloud
.android
.datamodel
.DataStorageManager
;
31 import com
.owncloud
.android
.datamodel
.OCFile
;
32 import com
.owncloud
.android
.files
.services
.FileDownloader
;
33 import com
.owncloud
.android
.files
.services
.FileUploader
;
34 import com
.owncloud
.android
.operations
.RemoteOperationResult
.ResultCode
;
36 import eu
.alefzero
.webdav
.WebdavClient
;
37 import eu
.alefzero
.webdav
.WebdavEntry
;
38 import eu
.alefzero
.webdav
.WebdavUtils
;
40 public class SynchronizeFileOperation
extends RemoteOperation
{
42 private String TAG
= SynchronizeFileOperation
.class.getSimpleName();
43 private String mRemotePath
;
44 private DataStorageManager mStorageManager
;
45 private Account mAccount
;
46 private boolean mSyncFileContents
;
47 private boolean mLocalChangeAlreadyKnown
;
48 private Context mContext
;
50 private boolean mTransferWasRequested
= false
;
52 public SynchronizeFileOperation(
54 DataStorageManager dataStorageManager
,
56 boolean syncFileContents
,
57 boolean localChangeAlreadyKnown
,
60 mRemotePath
= remotePath
;
61 mStorageManager
= dataStorageManager
;
63 mSyncFileContents
= syncFileContents
;
64 mLocalChangeAlreadyKnown
= localChangeAlreadyKnown
;
70 protected RemoteOperationResult
run(WebdavClient client
) {
72 PropFindMethod propfind
= null
;
73 RemoteOperationResult result
= null
;
74 mTransferWasRequested
= false
;
76 OCFile localFile
= mStorageManager
.getFileByPath(mRemotePath
);
78 if (!localFile
.isDown()) {
80 requestForDownload(localFile
);
81 result
= new RemoteOperationResult(ResultCode
.OK
);
84 /// local copy in the device -> need to think a bit more before do nothing
86 propfind
= new PropFindMethod(client
.getBaseUri() + WebdavUtils
.encodePath(mRemotePath
));
87 int status
= client
.executeMethod(propfind
);
88 boolean isMultiStatus
= status
== HttpStatus
.SC_MULTI_STATUS
;
90 MultiStatus resp
= propfind
.getResponseBodyAsMultiStatus();
91 WebdavEntry we
= new WebdavEntry(resp
.getResponses()[0],
92 client
.getBaseUri().getPath());
93 OCFile serverFile
= fillOCFile(we
);
95 /// check changes in server and local file
96 boolean serverChanged
= false
;
97 if (serverFile
.getEtag() != null
) {
98 serverChanged
= (!serverFile
.getEtag().equals(localFile
.getEtag()));
100 // server without etags
101 serverChanged
= (serverFile
.getModificationTimestamp() > localFile
.getModificationTimestamp());
103 boolean localChanged
= (mLocalChangeAlreadyKnown
|| localFile
.getLocalModificationTimestamp() > localFile
.getLastSyncDateForData());
105 /// decide action to perform depending upon changes
106 if (localChanged
&& serverChanged
) {
108 result
= new RemoteOperationResult(ResultCode
.SYNC_CONFLICT
);
110 } else if (localChanged
) {
111 if (mSyncFileContents
) {
112 requestForUpload(localFile
);
113 // the local update of file properties will be done by the FileUploader service when the upload finishes
115 // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid;
116 // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
117 // that an upload is necessary (for instance, in FileObserverService).
119 result
= new RemoteOperationResult(ResultCode
.OK
);
121 } else if (serverChanged
) {
122 if (mSyncFileContents
) {
123 requestForDownload(serverFile
);
124 // the update of local data will be done later by the FileUploader service when the upload finishes
126 // TODO CHECK: is this really useful in some point in the code?
127 serverFile
.setKeepInSync(localFile
.keepInSync());
128 serverFile
.setParentId(localFile
.getParentId());
129 mStorageManager
.saveFile(serverFile
);
132 result
= new RemoteOperationResult(ResultCode
.OK
);
135 // nothing changed, nothing to do
136 result
= new RemoteOperationResult(ResultCode
.OK
);
140 client
.exhaustResponse(propfind
.getResponseBodyAsStream());
141 result
= new RemoteOperationResult(false
, status
);
146 Log
.i(TAG
, "Synchronizing " + mAccount
.name
+ ", file " + mRemotePath
+ ": " + result
.getLogMessage());
148 } catch (Exception e
) {
149 result
= new RemoteOperationResult(e
);
150 Log
.e(TAG
, "Synchronizing " + mAccount
.name
+ ", file " + mRemotePath
+ ": " + result
.getLogMessage(), result
.getException());
153 if (propfind
!= null
)
154 propfind
.releaseConnection();
161 * Requests for an upload to the FileUploader service
163 * @param localFile OCFile object representing the file to upload
165 private void requestForUpload(OCFile localFile
) {
166 Intent i
= new Intent(mContext
, FileUploader
.class);
167 i
.putExtra(FileUploader
.KEY_ACCOUNT
, mAccount
);
168 i
.putExtra(FileUploader
.KEY_REMOTE_FILE
, mRemotePath
);
169 i
.putExtra(FileUploader
.KEY_LOCAL_FILE
, localFile
.getStoragePath());
170 i
.putExtra(FileUploader
.KEY_UPLOAD_TYPE
, FileUploader
.UPLOAD_SINGLE_FILE
);
171 i
.putExtra(FileUploader
.KEY_FORCE_OVERWRITE
, true
);
172 mContext
.startService(i
);
173 mTransferWasRequested
= true
;
178 * Requests for a download to the FileDownloader service
180 * @param file OCFile object representing the file to download
182 private void requestForDownload(OCFile file
) {
183 Intent i
= new Intent(mContext
, FileDownloader
.class);
184 i
.putExtra(FileDownloader
.EXTRA_ACCOUNT
, mAccount
);
185 i
.putExtra(FileDownloader
.EXTRA_FILE
, file
);
186 mContext
.startService(i
);
187 mTransferWasRequested
= true
;
192 * Creates and populates a new {@link OCFile} object with the data read from the server.
194 * @param we WebDAV entry read from the server for a WebDAV resource (remote file or folder).
195 * @return New OCFile instance representing the remote resource described by we.
197 private OCFile
fillOCFile(WebdavEntry we
) {
198 OCFile file
= new OCFile(we
.decodedPath());
199 file
.setCreationTimestamp(we
.createTimestamp());
200 file
.setFileLength(we
.contentLength());
201 file
.setMimetype(we
.contentType());
202 file
.setModificationTimestamp(we
.modifiedTimesamp());
203 file
.setLastSyncDateForProperties(System
.currentTimeMillis());
204 file
.setLastSyncDateForData(0);
209 public boolean transferWasRequested() {
210 return mTransferWasRequested
;