Merge branch 'develop' into add_cookie_based_session_support
[pub/Android/ownCloud.git] / src / com / owncloud / android / operations / SynchronizeFileOperation.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012 Bartek Przybylski
3 * Copyright (C) 2012-2014 ownCloud Inc.
4 *
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.
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
19 package com.owncloud.android.operations;
20
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.resources.files.ReadRemoteFileOperation;
29 import com.owncloud.android.operations.common.SyncOperation;
30 import com.owncloud.android.utils.FileStorageUtils;
31 import com.owncloud.android.utils.Log_OC;
32
33 import android.accounts.Account;
34 import android.content.Context;
35 import android.content.Intent;
36
37 /**
38 * Remote operation performing the read of remote file in the ownCloud server.
39 *
40 * @author David A. Velasco
41 * @author masensio
42 */
43
44 public class SynchronizeFileOperation extends SyncOperation {
45
46 private String TAG = SynchronizeFileOperation.class.getSimpleName();
47
48 private OCFile mLocalFile;
49 private String mRemotePath;
50 private OCFile mServerFile;
51 private Account mAccount;
52 private boolean mSyncFileContents;
53 private Context mContext;
54
55 private boolean mTransferWasRequested = false;
56
57
58 /**
59 * Constructor.
60 *
61 * Uses remotePath to retrieve all the data in local cache and remote server when the operation
62 * is executed, instead of reusing {@link OCFile} instances.
63 *
64 * @param
65 * @param account ownCloud account holding the file.
66 * @param syncFileContents When 'true', transference of data will be started by the
67 * operation if needed and no conflict is detected.
68 * @param context Android context; needed to start transfers.
69 */
70 public SynchronizeFileOperation(
71 String remotePath,
72 Account account,
73 boolean syncFileContents,
74 Context context) {
75
76 mRemotePath = remotePath;
77 mLocalFile = null;
78 mServerFile = null;
79 mAccount = account;
80 mSyncFileContents = syncFileContents;
81 mContext = context;
82 }
83
84
85 /**
86 * Constructor allowing to reuse {@link OCFile} instances just queried from cache or network.
87 *
88 * Useful for folder / account synchronizations.
89 *
90 * @param localFile Data of file currently hold in device cache. MUSTN't be null.
91 * @param serverFile Data of file just retrieved from network. If null, will be
92 * retrieved from network by the operation when executed.
93 * @param account ownCloud account holding the file.
94 * @param syncFileContents When 'true', transference of data will be started by the
95 * operation if needed and no conflict is detected.
96 * @param context Android context; needed to start transfers.
97 */
98 public SynchronizeFileOperation(
99 OCFile localFile,
100 OCFile serverFile,
101 Account account,
102 boolean syncFileContents,
103 Context context) {
104
105 mLocalFile = localFile;
106 mServerFile = serverFile;
107 mRemotePath = localFile.getRemotePath();
108 mAccount = account;
109 mSyncFileContents = syncFileContents;
110 mContext = context;
111 }
112
113
114 @Override
115 protected RemoteOperationResult run(OwnCloudClient client) {
116
117 RemoteOperationResult result = null;
118 mTransferWasRequested = false;
119
120 if (mLocalFile == null) {
121 // Get local file from the DB
122 mLocalFile = getStorageManager().getFileByPath(mRemotePath);
123 }
124
125 if (!mLocalFile.isDown()) {
126 /// easy decision
127 requestForDownload(mLocalFile);
128 result = new RemoteOperationResult(ResultCode.OK);
129
130 } else {
131 /// local copy in the device -> need to think a bit more before do anything
132
133 if (mServerFile == null) {
134 ReadRemoteFileOperation operation = new ReadRemoteFileOperation(mRemotePath);
135 result = operation.execute(client);
136 if (result.isSuccess()){
137 mServerFile = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
138 mServerFile.setLastSyncDateForProperties(System.currentTimeMillis());
139 }
140 }
141
142 if (mServerFile != null) {
143
144 /// check changes in server and local file
145 boolean serverChanged = false;
146 /* time for eTag is coming, but not yet
147 if (mServerFile.getEtag() != null) {
148 serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag())); // TODO could this be dangerous when the user upgrades the server from non-tagged to tagged?
149 } else { */
150 // server without etags
151 serverChanged = (mServerFile.getModificationTimestamp() != mLocalFile.getModificationTimestampAtLastSyncForData());
152 //}
153 boolean localChanged = (mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData());
154 // TODO this will be always true after the app is upgraded to database version 2; will result in unnecessary uploads
155
156 /// decide action to perform depending upon changes
157 //if (!mLocalFile.getEtag().isEmpty() && localChanged && serverChanged) {
158 if (localChanged && serverChanged) {
159 result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
160
161 } else if (localChanged) {
162 if (mSyncFileContents) {
163 requestForUpload(mLocalFile);
164 // the local update of file properties will be done by the FileUploader service when the upload finishes
165 } else {
166 // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid;
167 // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
168 // that an upload is necessary (for instance, in FileObserverService).
169 }
170 result = new RemoteOperationResult(ResultCode.OK);
171
172 } else if (serverChanged) {
173 if (mSyncFileContents) {
174 requestForDownload(mLocalFile); // local, not server; we won't to keep the value of keepInSync!
175 // the update of local data will be done later by the FileUploader service when the upload finishes
176 } else {
177 // TODO CHECK: is this really useful in some point in the code?
178 mServerFile.setKeepInSync(mLocalFile.keepInSync());
179 mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
180 mServerFile.setStoragePath(mLocalFile.getStoragePath());
181 mServerFile.setParentId(mLocalFile.getParentId());
182 getStorageManager().saveFile(mServerFile);
183
184 }
185 result = new RemoteOperationResult(ResultCode.OK);
186
187 } else {
188 // nothing changed, nothing to do
189 result = new RemoteOperationResult(ResultCode.OK);
190 }
191
192 }
193
194 }
195
196 Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage());
197
198 return result;
199 }
200
201
202 /**
203 * Requests for an upload to the FileUploader service
204 *
205 * @param file OCFile object representing the file to upload
206 */
207 private void requestForUpload(OCFile file) {
208 Intent i = new Intent(mContext, FileUploader.class);
209 i.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
210 i.putExtra(FileUploader.KEY_FILE, file);
211 /*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!
212 i.putExtra(FileUploader.KEY_LOCAL_FILE, localFile.getStoragePath());*/
213 i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
214 i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
215 mContext.startService(i);
216 mTransferWasRequested = true;
217 }
218
219
220 /**
221 * Requests for a download to the FileDownloader service
222 *
223 * @param file OCFile object representing the file to download
224 */
225 private void requestForDownload(OCFile file) {
226 Intent i = new Intent(mContext, FileDownloader.class);
227 i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
228 i.putExtra(FileDownloader.EXTRA_FILE, file);
229 mContext.startService(i);
230 mTransferWasRequested = true;
231 }
232
233
234 public boolean transferWasRequested() {
235 return mTransferWasRequested;
236 }
237
238
239 public OCFile getLocalFile() {
240 return mLocalFile;
241 }
242
243 }