1 /* ownCloud Android client application
2 * Copyright (C) 2012 Bartek Przybylski
3 * Copyright (C) 2012-2014 ownCloud Inc.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2,
7 * as published by the Free Software Foundation.
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 com
.owncloud
.android
.datamodel
.OCFile
;
22 import com
.owncloud
.android
.files
.services
.FileDownloader
;
23 import com
.owncloud
.android
.files
.services
.FileUploader
;
24 import com
.owncloud
.android
.lib
.common
.OwnCloudClient
;
25 import com
.owncloud
.android
.lib
.resources
.files
.RemoteFile
;
26 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperationResult
;
27 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperationResult
.ResultCode
;
28 import com
.owncloud
.android
.lib
.common
.utils
.Log_OC
;
29 import com
.owncloud
.android
.lib
.resources
.files
.ReadRemoteFileOperation
;
30 import com
.owncloud
.android
.operations
.common
.SyncOperation
;
31 import com
.owncloud
.android
.utils
.FileStorageUtils
;
33 import android
.accounts
.Account
;
34 import android
.content
.Context
;
35 import android
.content
.Intent
;
38 * Remote operation performing the read of remote file in the ownCloud server.
40 * @author David A. Velasco
44 public class SynchronizeFileOperation
extends SyncOperation
{
46 private String TAG
= SynchronizeFileOperation
.class.getSimpleName();
48 private OCFile mLocalFile
;
49 private String mRemotePath
;
50 private OCFile mServerFile
;
51 private Account mAccount
;
52 private boolean mSyncFileContents
;
53 private Context mContext
;
55 private boolean mTransferWasRequested
= false
;
58 * When 'false', uploads to the server are not done; only downloads or conflict detection.
59 * This is a temporal field.
60 * TODO Remove when 'folder synchronization' replaces 'folder download'.
62 private boolean mAllowUploads
;
66 * Constructor for "full synchronization mode".
68 * Uses remotePath to retrieve all the data both in local cache and in the remote OC server when the operation
69 * is executed, instead of reusing {@link OCFile} instances.
71 * Useful for direct synchronization of a single file.
74 * @param account ownCloud account holding the file.
75 * @param syncFileContents When 'true', transference of data will be started by the
76 * operation if needed and no conflict is detected.
77 * @param context Android context; needed to start transfers.
79 public SynchronizeFileOperation(
82 boolean syncFileContents
,
85 mRemotePath
= remotePath
;
89 mSyncFileContents
= syncFileContents
;
96 * Constructor allowing to reuse {@link OCFile} instances just queried from local cache or from remote OC server.
98 * Useful to include this operation as part of the synchronization of a folder (or a full account), avoiding the
99 * repetition of fetch operations (both in local database or remote server).
101 * At least data from local cache must be provided. If you don't have them, use the other constructor.
103 * @param localFile Data of file (just) retrieved from local cache/database. MUSTN't be null.
104 * @param serverFile Data of file (just) retrieved from a remote server. If null, will be
105 * retrieved from network by the operation when executed.
106 * @param account ownCloud account holding the file.
107 * @param syncFileContents When 'true', transference of data will be started by the
108 * operation if needed and no conflict is detected.
109 * @param context Android context; needed to start transfers.
111 public SynchronizeFileOperation(
115 boolean syncFileContents
,
118 mLocalFile
= localFile
;
119 mServerFile
= serverFile
;
120 mRemotePath
= localFile
.getRemotePath(); // this will crash if localFile == null; use the other constructor
122 mSyncFileContents
= syncFileContents
;
124 mAllowUploads
= true
;
129 * Temporal constructor.
131 * Extends the previous one to allow constrained synchronizations where uploads are never performed - only
132 * downloads or conflict detection.
134 * Do not use unless you are involved in 'folder synchronization' or 'folder download' work in progress.
136 * TODO Remove when 'folder synchronization' replaces 'folder download'.
138 * @param localFile Data of file (just) retrieved from local cache/database. MUSTN't be null.
139 * @param serverFile Data of file (just) retrieved from a remote server. If null, will be
140 * retrieved from network by the operation when executed.
141 * @param account ownCloud account holding the file.
142 * @param syncFileContents When 'true', transference of data will be started by the
143 * operation if needed and no conflict is detected.
144 * @param allowUploads When 'false', uploads to the server are not done; only downloads or conflict
146 * @param context Android context; needed to start transfers.
148 public SynchronizeFileOperation(
152 boolean syncFileContents
,
153 boolean allowUploads
,
156 mLocalFile
= localFile
;
157 mServerFile
= serverFile
;
158 mRemotePath
= localFile
.getRemotePath(); // this will crash if localFile == null; use the other constructor
160 mSyncFileContents
= syncFileContents
;
162 mAllowUploads
= allowUploads
;
167 protected RemoteOperationResult
run(OwnCloudClient client
) {
169 RemoteOperationResult result
= null
;
170 mTransferWasRequested
= false
;
172 if (mLocalFile
== null
) {
173 // Get local file from the DB
174 mLocalFile
= getStorageManager().getFileByPath(mRemotePath
);
177 if (!mLocalFile
.isDown()) {
179 requestForDownload(mLocalFile
);
180 result
= new RemoteOperationResult(ResultCode
.OK
);
183 /// local copy in the device -> need to think a bit more before do anything
185 if (mServerFile
== null
) {
186 ReadRemoteFileOperation operation
= new ReadRemoteFileOperation(mRemotePath
);
187 result
= operation
.execute(client
);
188 if (result
.isSuccess()){
189 mServerFile
= FileStorageUtils
.fillOCFile((RemoteFile
) result
.getData().get(0));
190 mServerFile
.setLastSyncDateForProperties(System
.currentTimeMillis());
194 if (mServerFile
!= null
) {
196 /// check changes in server and local file
197 boolean serverChanged
= false
;
198 /* time for eTag is coming, but not yet
199 if (mServerFile.getEtag() != null) {
200 serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag()));
203 mServerFile
.getModificationTimestamp() != mLocalFile
.getModificationTimestampAtLastSyncForData()
206 boolean localChanged
= (
207 mLocalFile
.getLocalModificationTimestamp() > mLocalFile
.getLastSyncDateForData()
210 /// decide action to perform depending upon changes
211 //if (!mLocalFile.getEtag().isEmpty() && localChanged && serverChanged) {
212 if (localChanged
&& serverChanged
) {
213 result
= new RemoteOperationResult(ResultCode
.SYNC_CONFLICT
);
215 } else if (localChanged
) {
216 if (mSyncFileContents
&& mAllowUploads
) {
217 requestForUpload(mLocalFile
);
218 // the local update of file properties will be done by the FileUploader service when the upload finishes
220 // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid;
221 // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
222 // that an upload is necessary (for instance, in FileObserverService).
224 result
= new RemoteOperationResult(ResultCode
.OK
);
226 } else if (serverChanged
) {
227 mLocalFile
.setRemoteId(mServerFile
.getRemoteId());
229 if (mSyncFileContents
) {
230 requestForDownload(mLocalFile
); // local, not server; we won't to keep the value of keepInSync!
231 // the update of local data will be done later by the FileUploader service when the upload finishes
233 // TODO CHECK: is this really useful in some point in the code?
234 mServerFile
.setKeepInSync(mLocalFile
.keepInSync());
235 mServerFile
.setLastSyncDateForData(mLocalFile
.getLastSyncDateForData());
236 mServerFile
.setStoragePath(mLocalFile
.getStoragePath());
237 mServerFile
.setParentId(mLocalFile
.getParentId());
238 getStorageManager().saveFile(mServerFile
);
241 result
= new RemoteOperationResult(ResultCode
.OK
);
244 // nothing changed, nothing to do
245 result
= new RemoteOperationResult(ResultCode
.OK
);
252 Log_OC
.i(TAG
, "Synchronizing " + mAccount
.name
+ ", file " + mLocalFile
.getRemotePath() + ": "
253 + result
.getLogMessage());
260 * Requests for an upload to the FileUploader service
262 * @param file OCFile object representing the file to upload
264 private void requestForUpload(OCFile file
) {
265 Intent i
= new Intent(mContext
, FileUploader
.class);
266 i
.putExtra(FileUploader
.KEY_ACCOUNT
, mAccount
);
267 i
.putExtra(FileUploader
.KEY_FILE
, file
);
268 /*i.putExtra(FileUploader.KEY_REMOTE_FILE, mRemotePath); // doing this we would lose the value of keepInSync in the road, and maybe it's not updated in the database when the FileUploader service gets it!
269 i.putExtra(FileUploader.KEY_LOCAL_FILE, localFile.getStoragePath());*/
270 i
.putExtra(FileUploader
.KEY_UPLOAD_TYPE
, FileUploader
.UPLOAD_SINGLE_FILE
);
271 i
.putExtra(FileUploader
.KEY_FORCE_OVERWRITE
, true
);
272 mContext
.startService(i
);
273 mTransferWasRequested
= true
;
278 * Requests for a download to the FileDownloader service
280 * @param file OCFile object representing the file to download
282 private void requestForDownload(OCFile file
) {
283 Intent i
= new Intent(mContext
, FileDownloader
.class);
284 i
.putExtra(FileDownloader
.EXTRA_ACCOUNT
, mAccount
);
285 i
.putExtra(FileDownloader
.EXTRA_FILE
, file
);
286 mContext
.startService(i
);
287 mTransferWasRequested
= true
;
291 public boolean transferWasRequested() {
292 return mTransferWasRequested
;
296 public OCFile
getLocalFile() {