moving from eu.alefzero.eu to com.owncloud.android
[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;
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
128 /* Commented code for ugly performance tests
129 long sum = 0, mean = 0, max = 0, min = Long.MAX_VALUE;
130 for (int i=0; i<MAX_DELAYS && i<mDelaysCount; i++) {
131 sum += mResponseDelays[i];
132 max = Math.max(max, mResponseDelays[i]);
133 min = Math.min(min, mResponseDelays[i]);
134 }
135 mean = sum / mDelaysCount;
136 Log.e(TAG, "SYNC STATS - response: mean time = " + mean + " ; max time = " + max + " ; min time = " + min);
137
138 sum = 0; max = 0; min = Long.MAX_VALUE;
139 for (int i=0; i<MAX_DELAYS && i<mDelaysCount; i++) {
140 sum += mSaveDelays[i];
141 max = Math.max(max, mSaveDelays[i]);
142 min = Math.min(min, mSaveDelays[i]);
143 }
144 mean = sum / mDelaysCount;
145 Log.e(TAG, "SYNC STATS - save: mean time = " + mean + " ; max time = " + max + " ; min time = " + min);
146 Log.e(TAG, "SYNC STATS - folders measured: " + mDelaysCount);
147 */
148
149 sendStickyBroadcast(false, null);
150 }
151
152 private void fetchData(String uri, SyncResult syncResult, long parentId) {
153 try {
154 Log.d(TAG, "fetching " + uri);
155
156 // remote request
157 PropFindMethod query = new PropFindMethod(uri);
158 /* Commented code for ugly performance tests
159 long responseDelay = System.currentTimeMillis();
160 */
161 getClient().executeMethod(query);
162 /* Commented code for ugly performance tests
163 responseDelay = System.currentTimeMillis() - responseDelay;
164 Log.e(TAG, "syncing: RESPONSE TIME for " + uri + " contents, " + responseDelay + "ms");
165 */
166 MultiStatus resp = null;
167 resp = query.getResponseBodyAsMultiStatus();
168
169 // insertion or update of files
170 List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
171 for (int i = 1; i < resp.getResponses().length; ++i) {
172 WebdavEntry we = new WebdavEntry(resp.getResponses()[i], getUri().getPath());
173 OCFile file = fillOCFile(we);
174 file.setParentId(parentId);
175 if (getStorageManager().getFileByPath(file.getRemotePath()) != null &&
176 getStorageManager().getFileByPath(file.getRemotePath()).keepInSync() &&
177 file.getModificationTimestamp() > getStorageManager().getFileByPath(file.getRemotePath())
178 .getModificationTimestamp()) {
179 Intent intent = new Intent(this.getContext(), FileDownloader.class);
180 intent.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());
181 intent.putExtra(FileDownloader.EXTRA_FILE_PATH, file.getRemotePath());
182 intent.putExtra(FileDownloader.EXTRA_REMOTE_PATH, file.getRemotePath());
183 intent.putExtra(FileDownloader.EXTRA_FILE_SIZE, file.getFileLength());
184 file.setKeepInSync(true);
185 getContext().startService(intent);
186 }
187 if (getStorageManager().getFileByPath(file.getRemotePath()) != null)
188 file.setKeepInSync(getStorageManager().getFileByPath(file.getRemotePath()).keepInSync());
189
190 //Log.v(TAG, "adding file: " + file);
191 updatedFiles.add(file);
192 if (parentId == 0)
193 parentId = file.getFileId();
194 }
195 /* Commented code for ugly performance tests
196 long saveDelay = System.currentTimeMillis();
197 */
198 getStorageManager().saveFiles(updatedFiles); // all "at once" ; trying to get a best performance in database update
199 /* Commented code for ugly performance tests
200 saveDelay = System.currentTimeMillis() - saveDelay;
201 Log.e(TAG, "syncing: SAVE TIME for " + uri + " contents, " + mSaveDelays[mDelaysIndex] + "ms");
202 */
203
204 // removal of obsolete files
205 Vector<OCFile> files = getStorageManager().getDirectoryContent(
206 getStorageManager().getFileById(parentId));
207 OCFile file;
208 for (int i=0; i < files.size(); ) {
209 file = files.get(i);
210 if (file.getLastSyncDate() != mCurrentSyncTime) {
211 Log.v(TAG, "removing file: " + file);
212 getStorageManager().removeFile(file);
213 files.remove(i);
214 } else {
215 i++;
216 }
217 }
218
219 // synchronized folder -> notice to UI
220 sendStickyBroadcast(true, getStorageManager().getFileById(parentId).getRemotePath());
221
222 // recursive fetch
223 for (int i=0; i < files.size() && !mCancellation; i++) {
224 OCFile newFile = files.get(i);
225 if (newFile.getMimetype().equals("DIR")) {
226 fetchData(getUri().toString() + WebdavUtils.encodePath(newFile.getRemotePath()), syncResult, newFile.getFileId());
227 }
228 }
229 if (mCancellation) Log.d(TAG, "Leaving " + uri + " because cancelation request");
230
231 /* Commented code for ugly performance tests
232 mResponseDelays[mDelaysIndex] = responseDelay;
233 mSaveDelays[mDelaysIndex] = saveDelay;
234 mDelaysCount++;
235 mDelaysIndex++;
236 if (mDelaysIndex >= MAX_DELAYS)
237 mDelaysIndex = 0;
238 */
239
240
241
242 } catch (OperationCanceledException e) {
243 e.printStackTrace();
244 } catch (AuthenticatorException e) {
245 syncResult.stats.numAuthExceptions++;
246 e.printStackTrace();
247 } catch (IOException e) {
248 syncResult.stats.numIoExceptions++;
249 e.printStackTrace();
250 } catch (DavException e) {
251 syncResult.stats.numIoExceptions++;
252 e.printStackTrace();
253 } catch (Throwable t) {
254 // TODO update syncResult
255 Log.e(TAG, "problem while synchronizing owncloud account " + mAccount.name, t);
256 t.printStackTrace();
257 }
258 }
259
260 private OCFile fillOCFile(WebdavEntry we) {
261 OCFile file = new OCFile(we.decodedPath());
262 file.setCreationTimestamp(we.createTimestamp());
263 file.setFileLength(we.contentLength());
264 file.setMimetype(we.contentType());
265 file.setModificationTimestamp(we.modifiedTimesamp());
266 file.setLastSyncDate(mCurrentSyncTime);
267 return file;
268 }
269
270
271 private void sendStickyBroadcast(boolean inProgress, String dirRemotePath) {
272 Intent i = new Intent(FileSyncService.SYNC_MESSAGE);
273 i.putExtra(FileSyncService.IN_PROGRESS, inProgress);
274 i.putExtra(FileSyncService.ACCOUNT_NAME, getAccount().name);
275 if (dirRemotePath != null) {
276 i.putExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH, dirRemotePath);
277 }
278 getContext().sendStickyBroadcast(i);
279 }
280
281 /**
282 * Called by system SyncManager when a synchronization is required to be cancelled.
283 *
284 * Sets the mCancellation flag to 'true'. THe synchronization will be stopped when before a new folder is fetched. Data of the last folder
285 * fetched will be still saved in the database. See onPerformSync implementation.
286 */
287 @Override
288 public void onSyncCanceled() {
289 Log.d(TAG, "Synchronization of " + mAccount.name + " has been requested to cancell");
290 mCancellation = true;
291 super.onSyncCanceled();
292 }
293
294 }