Updating synchronization for providing SSL warning when necessary; step 1: refactorin...
[pub/Android/ownCloud.git] / src / com / owncloud / android / operations / SynchronizeFolderOperation.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012 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.operations;
20
21 import java.io.IOException;
22 import java.util.List;
23 import java.util.Vector;
24
25 import org.apache.http.HttpStatus;
26 import org.apache.jackrabbit.webdav.DavException;
27 import org.apache.jackrabbit.webdav.MultiStatus;
28 import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
29
30 import android.accounts.Account;
31 import android.content.Intent;
32 import android.util.Log;
33
34 import com.owncloud.android.datamodel.FileDataStorageManager;
35 import com.owncloud.android.datamodel.OCFile;
36 import com.owncloud.android.files.services.FileDownloader;
37
38 import eu.alefzero.webdav.WebdavClient;
39 import eu.alefzero.webdav.WebdavEntry;
40 import eu.alefzero.webdav.WebdavUtils;
41
42
43 /**
44 * Remote operation performing the synchronization a the contents of a remote folder with the local database
45 *
46 * @author David A. Velasco
47 */
48 public class SynchronizeFolderOperation extends RemoteOperation {
49
50 private static final String TAG = SynchronizeFolderOperation.class.getCanonicalName();
51
52 /** Remote folder to synchronize */
53 private String mRemotePath;
54
55 /** Timestamp for the synchronization in progress */
56 private long mCurrentSyncTime;
57
58 /** Id of the folder to synchronize in the local database */
59 private long mParentId;
60
61 /** Access to the local database */
62 private FileDataStorageManager mStorageManager;
63
64 /** Account where the file to synchronize belongs */
65 private Account mAccount;
66
67
68 SynchronizeFolderOperation(String remotePath, long currentSyncTime, long parentId, FileDataStorageManager storageManager, Account account) {
69 mRemotePath = remotePath;
70 mCurrentSyncTime = currentSyncTime;
71 mParentId = parentId;
72 mStorageManager = storageManager;
73 mAccount = account;
74 }
75
76
77 @Override
78 protected RemoteOperationResult run(WebdavClient client) {
79 RemoteOperationResult result = null;
80
81 // code before in FileSyncAdapter.fetchData
82 PropFindMethod query = null;
83 Vector<OCFile> children = null;
84 try {
85 Log.d(TAG, "Fetching files in " + mRemotePath);
86
87 // remote request
88 query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
89 int status = client.executeMethod(query);
90
91 // check and process response
92 if (isMultiStatus(status)) {
93 /// TODO take into account all the possible status per child-resource
94
95 MultiStatus resp = query.getResponseBodyAsMultiStatus();
96
97 // read contents in folder
98 List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
99 for (int i = 1; i < resp.getResponses().length; ++i) {
100 WebdavEntry we = new WebdavEntry(resp.getResponses()[i], client.getBaseUri().getPath());
101 OCFile file = fillOCFile(we);
102 file.setParentId(mParentId);
103 OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
104 if (oldFile != null) {
105 if (oldFile.keepInSync() && file.getModificationTimestamp() > oldFile.getModificationTimestamp()) {
106 requestContentDownload(file);
107 }
108 file.setKeepInSync(oldFile.keepInSync());
109 }
110
111 updatedFiles.add(file);
112 }
113
114
115 // save updated contents in local database; all at once, trying to get a best performance in database update (not a big deal, indeed)
116 mStorageManager.saveFiles(updatedFiles);
117
118
119 // removal of obsolete files
120 children = mStorageManager.getDirectoryContent(mStorageManager.getFileById(mParentId));
121 OCFile file;
122 String currentSavePath = FileDownloader.getSavePath(mAccount.name);
123 for (int i=0; i < children.size(); ) {
124 file = children.get(i);
125 if (file.getLastSyncDate() != mCurrentSyncTime) {
126 Log.d(TAG, "removing file: " + file);
127 mStorageManager.removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath)));
128 children.remove(i);
129 } else {
130 i++;
131 }
132 }
133
134 } else {
135 client.exhaustResponse(query.getResponseBodyAsStream());
136 }
137
138 // prepare result object
139 result = new RemoteOperationResult(isMultiStatus(status), status);
140 Log.i(TAG, "Synchronization of " + mRemotePath + ": " + result.getLogMessage());
141
142
143 } catch (Exception e) {
144 // TODO - distinguish IOException and DavException in RemoteOperationResult constructor
145 result = new RemoteOperationResult(e);
146 Log.e(TAG, "Synchronization of " + mRemotePath + ": " + result.getLogMessage(), result.getException());
147
148 } finally {
149 if (query != null)
150 query.releaseConnection(); // let the connection available for other methods
151 }
152
153 return result;
154 }
155
156
157 public boolean isMultiStatus(int status) {
158 return (status == HttpStatus.SC_MULTI_STATUS);
159 }
160
161
162 /**
163 * Creates and populates a new {@link OCFile} object with the data read from the server.
164 *
165 * @param we WebDAV entry read from the server for a WebDAV resource (remote file or folder).
166 * @return New OCFile instance representing the remote resource described by we.
167 */
168 private OCFile fillOCFile(WebdavEntry we) {
169 OCFile file = new OCFile(we.decodedPath());
170 file.setCreationTimestamp(we.createTimestamp());
171 file.setFileLength(we.contentLength());
172 file.setMimetype(we.contentType());
173 file.setModificationTimestamp(we.modifiedTimesamp());
174 file.setLastSyncDate(mCurrentSyncTime);
175 return file;
176 }
177
178
179 /**
180 * Probably this code would be better in other place
181 */
182 private void requestContentDownload(OCFile file) {
183 Intent intent = new Intent(this.getContext(), FileDownloader.class);
184 intent.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
185 intent.putExtra(FileDownloader.EXTRA_FILE, file);
186 getContext().startService(intent);
187 }
188
189
190 }