Refactoring of WebdavClient creation and setup
[pub/Android/ownCloud.git] / src / com / owncloud / android / syncadapter / FileSyncAdapter.java
1 /* ownCloud Android client application
2 * Copyright (C) 2011 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
19 package com.owncloud.android.syncadapter;
20
21 import java.io.IOException;
22 import java.util.List;
23 import java.util.Vector;
24
25 import org.apache.jackrabbit.webdav.DavException;
26 import org.apache.jackrabbit.webdav.MultiStatus;
27 import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
28
29 import com.owncloud.android.datamodel.FileDataStorageManager;
30 import com.owncloud.android.datamodel.OCFile;
31 import com.owncloud.android.files.services.FileDownloader;
32
33 import android.accounts.Account;
34 import android.accounts.AuthenticatorException;
35 import android.accounts.OperationCanceledException;
36 import android.content.ContentProviderClient;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.content.SyncResult;
40 import android.os.Bundle;
41 import android.util.Log;
42 import eu.alefzero.webdav.WebdavEntry;
43 import eu.alefzero.webdav.WebdavUtils;
44
45 /**
46 * SyncAdapter implementation for syncing sample SyncAdapter contacts to the
47 * platform ContactOperations provider.
48 *
49 * @author Bartek Przybylski
50 */
51 public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
52
53 private final static String TAG = "FileSyncAdapter";
54
55 /* Commented code for ugly performance tests
56 private final static int MAX_DELAYS = 100;
57 private static long[] mResponseDelays = new long[MAX_DELAYS];
58 private static long[] mSaveDelays = new long[MAX_DELAYS];
59 private int mDelaysIndex = 0;
60 private int mDelaysCount = 0;
61 */
62
63 private long mCurrentSyncTime;
64 private boolean mCancellation;
65 private Account mAccount;
66
67 public FileSyncAdapter(Context context, boolean autoInitialize) {
68 super(context, autoInitialize);
69 }
70
71 @Override
72 public synchronized void onPerformSync(Account account, Bundle extras,
73 String authority, ContentProviderClient provider,
74 SyncResult syncResult) {
75
76 mCancellation = false;
77 mAccount = account;
78
79 this.setAccount(mAccount);
80 this.setContentProvider(provider);
81 this.setStorageManager(new FileDataStorageManager(mAccount,
82 getContentProvider()));
83
84 /* Commented code for ugly performance tests
85 mDelaysIndex = 0;
86 mDelaysCount = 0;
87 */
88
89
90 Log.d(TAG, "syncing owncloud account " + mAccount.name);
91
92 sendStickyBroadcast(true, null); // message to signal the start to the UI
93
94 PropFindMethod query = null;
95 try {
96 mCurrentSyncTime = System.currentTimeMillis();
97 query = new PropFindMethod(getUri().toString() + "/");
98 getClient().executeMethod(query);
99 MultiStatus resp = null;
100 resp = query.getResponseBodyAsMultiStatus();
101
102 if (resp.getResponses().length > 0) {
103 WebdavEntry we = new WebdavEntry(resp.getResponses()[0], getUri().getPath());
104 OCFile file = fillOCFile(we);
105 file.setParentId(0);
106 getStorageManager().saveFile(file);
107 if (!mCancellation) {
108 fetchData(getUri().toString(), syncResult, file.getFileId());
109 }
110 }
111 } catch (OperationCanceledException e) {
112 e.printStackTrace();
113 } catch (AuthenticatorException e) {
114 syncResult.stats.numAuthExceptions++;
115 e.printStackTrace();
116 } catch (IOException e) {
117 syncResult.stats.numIoExceptions++;
118 e.printStackTrace();
119 } catch (DavException e) {
120 syncResult.stats.numIoExceptions++;
121 e.printStackTrace();
122 } catch (Throwable t) {
123 // TODO update syncResult
124 Log.e(TAG, "problem while synchronizing owncloud account " + account.name, t);
125 t.printStackTrace();
126
127 } finally {
128 if (query != null)
129 query.releaseConnection(); // let the connection available for other methods
130 }
131
132 /* Commented code for ugly performance tests
133 long sum = 0, mean = 0, max = 0, min = Long.MAX_VALUE;
134 for (int i=0; i<MAX_DELAYS && i<mDelaysCount; i++) {
135 sum += mResponseDelays[i];
136 max = Math.max(max, mResponseDelays[i]);
137 min = Math.min(min, mResponseDelays[i]);
138 }
139 mean = sum / mDelaysCount;
140 Log.e(TAG, "SYNC STATS - response: mean time = " + mean + " ; max time = " + max + " ; min time = " + min);
141
142 sum = 0; max = 0; min = Long.MAX_VALUE;
143 for (int i=0; i<MAX_DELAYS && i<mDelaysCount; i++) {
144 sum += mSaveDelays[i];
145 max = Math.max(max, mSaveDelays[i]);
146 min = Math.min(min, mSaveDelays[i]);
147 }
148 mean = sum / mDelaysCount;
149 Log.e(TAG, "SYNC STATS - save: mean time = " + mean + " ; max time = " + max + " ; min time = " + min);
150 Log.e(TAG, "SYNC STATS - folders measured: " + mDelaysCount);
151 */
152
153 sendStickyBroadcast(false, null);
154 }
155
156 private void fetchData(String uri, SyncResult syncResult, long parentId) {
157 PropFindMethod query = null;
158 try {
159 Log.d(TAG, "fetching " + uri);
160
161 // remote request
162 query = new PropFindMethod(uri);
163 /* Commented code for ugly performance tests
164 long responseDelay = System.currentTimeMillis();
165 */
166 getClient().executeMethod(query);
167 /* Commented code for ugly performance tests
168 responseDelay = System.currentTimeMillis() - responseDelay;
169 Log.e(TAG, "syncing: RESPONSE TIME for " + uri + " contents, " + responseDelay + "ms");
170 */
171 MultiStatus resp = null;
172 resp = query.getResponseBodyAsMultiStatus();
173
174 // insertion or update of files
175 List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
176 for (int i = 1; i < resp.getResponses().length; ++i) {
177 WebdavEntry we = new WebdavEntry(resp.getResponses()[i], getUri().getPath());
178 OCFile file = fillOCFile(we);
179 file.setParentId(parentId);
180 if (getStorageManager().getFileByPath(file.getRemotePath()) != null &&
181 getStorageManager().getFileByPath(file.getRemotePath()).keepInSync() &&
182 file.getModificationTimestamp() > getStorageManager().getFileByPath(file.getRemotePath())
183 .getModificationTimestamp()) {
184 Intent intent = new Intent(this.getContext(), FileDownloader.class);
185 intent.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());
186 intent.putExtra(FileDownloader.EXTRA_FILE_PATH, file.getRemotePath());
187 intent.putExtra(FileDownloader.EXTRA_REMOTE_PATH, file.getRemotePath());
188 intent.putExtra(FileDownloader.EXTRA_FILE_SIZE, file.getFileLength());
189 file.setKeepInSync(true);
190 getContext().startService(intent);
191 }
192 if (getStorageManager().getFileByPath(file.getRemotePath()) != null)
193 file.setKeepInSync(getStorageManager().getFileByPath(file.getRemotePath()).keepInSync());
194
195 //Log.v(TAG, "adding file: " + file);
196 updatedFiles.add(file);
197 if (parentId == 0)
198 parentId = file.getFileId();
199 }
200 /* Commented code for ugly performance tests
201 long saveDelay = System.currentTimeMillis();
202 */
203 getStorageManager().saveFiles(updatedFiles); // all "at once" ; trying to get a best performance in database update
204 /* Commented code for ugly performance tests
205 saveDelay = System.currentTimeMillis() - saveDelay;
206 Log.e(TAG, "syncing: SAVE TIME for " + uri + " contents, " + mSaveDelays[mDelaysIndex] + "ms");
207 */
208
209 // removal of obsolete files
210 Vector<OCFile> files = getStorageManager().getDirectoryContent(
211 getStorageManager().getFileById(parentId));
212 OCFile file;
213 for (int i=0; i < files.size(); ) {
214 file = files.get(i);
215 if (file.getLastSyncDate() != mCurrentSyncTime) {
216 Log.v(TAG, "removing file: " + file);
217 getStorageManager().removeFile(file);
218 files.remove(i);
219 } else {
220 i++;
221 }
222 }
223
224 // synchronized folder -> notice to UI
225 sendStickyBroadcast(true, getStorageManager().getFileById(parentId).getRemotePath());
226
227 // recursive fetch
228 for (int i=0; i < files.size() && !mCancellation; i++) {
229 OCFile newFile = files.get(i);
230 if (newFile.getMimetype().equals("DIR")) {
231 fetchData(getUri().toString() + WebdavUtils.encodePath(newFile.getRemotePath()), syncResult, newFile.getFileId());
232 }
233 }
234 if (mCancellation) Log.d(TAG, "Leaving " + uri + " because cancelation request");
235
236 /* Commented code for ugly performance tests
237 mResponseDelays[mDelaysIndex] = responseDelay;
238 mSaveDelays[mDelaysIndex] = saveDelay;
239 mDelaysCount++;
240 mDelaysIndex++;
241 if (mDelaysIndex >= MAX_DELAYS)
242 mDelaysIndex = 0;
243 */
244
245 } catch (OperationCanceledException e) {
246 e.printStackTrace();
247 } catch (AuthenticatorException e) {
248 syncResult.stats.numAuthExceptions++;
249 e.printStackTrace();
250 } catch (IOException e) {
251 syncResult.stats.numIoExceptions++;
252 e.printStackTrace();
253 } catch (DavException e) {
254 syncResult.stats.numIoExceptions++;
255 e.printStackTrace();
256 } catch (Throwable t) {
257 // TODO update syncResult
258 Log.e(TAG, "problem while synchronizing owncloud account " + mAccount.name, t);
259 t.printStackTrace();
260
261 } finally {
262 if (query != null)
263 query.releaseConnection(); // let the connection available for other methods
264 }
265 }
266
267 private OCFile fillOCFile(WebdavEntry we) {
268 OCFile file = new OCFile(we.decodedPath());
269 file.setCreationTimestamp(we.createTimestamp());
270 file.setFileLength(we.contentLength());
271 file.setMimetype(we.contentType());
272 file.setModificationTimestamp(we.modifiedTimesamp());
273 file.setLastSyncDate(mCurrentSyncTime);
274 return file;
275 }
276
277
278 private void sendStickyBroadcast(boolean inProgress, String dirRemotePath) {
279 Intent i = new Intent(FileSyncService.SYNC_MESSAGE);
280 i.putExtra(FileSyncService.IN_PROGRESS, inProgress);
281 i.putExtra(FileSyncService.ACCOUNT_NAME, getAccount().name);
282 if (dirRemotePath != null) {
283 i.putExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH, dirRemotePath);
284 }
285 getContext().sendStickyBroadcast(i);
286 }
287
288 /**
289 * Called by system SyncManager when a synchronization is required to be cancelled.
290 *
291 * Sets the mCancellation flag to 'true'. THe synchronization will be stopped when before a new folder is fetched. Data of the last folder
292 * fetched will be still saved in the database. See onPerformSync implementation.
293 */
294 @Override
295 public void onSyncCanceled() {
296 Log.d(TAG, "Synchronization of " + mAccount.name + " has been requested to cancell");
297 mCancellation = true;
298 super.onSyncCanceled();
299 }
300
301 }