Refactored SyncFolderHandler out of OperationsService
[pub/Android/ownCloud.git] / src / com / owncloud / android / services / SyncFolderHandler.java
1 /* ownCloud Android client application
2 * Copyright (C) 2015 ownCloud Inc.
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 version 2,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 package com.owncloud.android.services;
19
20 import android.accounts.Account;
21 import android.accounts.AccountsException;
22 import android.content.Intent;
23 import android.os.Handler;
24 import android.os.Looper;
25 import android.os.Message;
26 import android.util.Pair;
27
28 import com.owncloud.android.datamodel.FileDataStorageManager;
29 import com.owncloud.android.datamodel.OCFile;
30 import com.owncloud.android.files.services.FileDownloader;
31 import com.owncloud.android.lib.common.OwnCloudAccount;
32 import com.owncloud.android.lib.common.OwnCloudClient;
33 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
34 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
35 import com.owncloud.android.lib.common.utils.Log_OC;
36 import com.owncloud.android.operations.SynchronizeFolderOperation;
37 import com.owncloud.android.utils.FileStorageUtils;
38
39 import java.io.IOException;
40 import java.util.ArrayList;
41 import java.util.Iterator;
42 import java.util.concurrent.ConcurrentHashMap;
43 import java.util.concurrent.ConcurrentMap;
44
45 /**
46 * SyncFolder worker. Performs the pending operations in the order they were requested.
47 *
48 * Created with the Looper of a new thread, started in
49 * {@link com.owncloud.android.services.OperationsService#onCreate()}.
50 */
51 class SyncFolderHandler extends Handler {
52
53 private static final String TAG = SyncFolderHandler.class.getSimpleName();
54
55
56 OperationsService mService;
57
58 private ConcurrentMap<String,SynchronizeFolderOperation> mPendingOperations =
59 new ConcurrentHashMap<String,SynchronizeFolderOperation>();
60 private OwnCloudClient mOwnCloudClient = null;
61 private FileDataStorageManager mStorageManager;
62 private SynchronizeFolderOperation mCurrentSyncOperation;
63
64
65 public SyncFolderHandler(Looper looper, OperationsService service) {
66 super(looper);
67 if (service == null) {
68 throw new IllegalArgumentException("Received invalid NULL in parameter 'service'");
69 }
70 mService = service;
71 }
72
73
74 public boolean isSynchronizing(Account account, String remotePath) {
75 if (account == null || remotePath == null) return false;
76 String targetKey = buildRemoteName(account, remotePath);
77 synchronized (mPendingOperations) {
78 // TODO - this can be slow when synchronizing a big tree - need a better data structure
79 Iterator<String> it = mPendingOperations.keySet().iterator();
80 boolean found = false;
81 while (it.hasNext() && !found) {
82 found = it.next().startsWith(targetKey);
83 }
84 return found;
85 }
86 }
87
88
89 @Override
90 public void handleMessage(Message msg) {
91 Pair<Account, String> itemSyncKey = (Pair<Account, String>) msg.obj;
92 doOperation(itemSyncKey.first, itemSyncKey.second);
93 mService.stopSelf(msg.arg1);
94 }
95
96
97 /**
98 * Performs the next operation in the queue
99 */
100 private void doOperation(Account account, String remotePath) {
101
102 String syncKey = buildRemoteName(account,remotePath);
103
104 synchronized(mPendingOperations) {
105 mCurrentSyncOperation = mPendingOperations.get(syncKey);
106 }
107
108 if (mCurrentSyncOperation != null) {
109 RemoteOperationResult result = null;
110
111 try {
112
113 OwnCloudAccount ocAccount = new OwnCloudAccount(account, mService);
114 mOwnCloudClient = OwnCloudClientManagerFactory.getDefaultSingleton().
115 getClientFor(ocAccount, mService);
116 mStorageManager = new FileDataStorageManager(
117 account,
118 mService.getContentResolver()
119 );
120
121 result = mCurrentSyncOperation.execute(mOwnCloudClient, mStorageManager);
122
123 } catch (AccountsException e) {
124 Log_OC.e(TAG, "Error while trying to get autorization", e);
125 } catch (IOException e) {
126 Log_OC.e(TAG, "Error while trying to get autorization", e);
127 } finally {
128 synchronized (mPendingOperations) {
129 mPendingOperations.remove(syncKey);
130 /*
131 SynchronizeFolderOperation checkedOp = mCurrentSyncOperation;
132 String checkedKey = syncKey;
133 while (checkedOp.getPendingChildrenCount() <= 0) {
134 // while (!checkedOp.hasChildren()) {
135 mPendingOperations.remove(checkedKey);
136 String parentKey = buildRemoteName(account, (new File(checkedOp.getFolderPath())).getParent());
137 // String parentKey = buildRemoteName(account, checkedOp.getParentPath());
138 SynchronizeFolderOperation parentOp = mPendingOperations.get(parentKey);
139 if (parentOp != null) {
140 parentOp.decreasePendingChildrenCount();
141 }
142 }
143 */
144 }
145
146 mService.dispatchResultToOperationListeners(null, mCurrentSyncOperation, result);
147
148 sendBroadcastFinishedSyncFolder(account, remotePath, result.isSuccess());
149 }
150 }
151 }
152
153 public void add(Account account, String remotePath, SynchronizeFolderOperation syncFolderOperation){
154 String syncKey = buildRemoteName(account,remotePath);
155 mPendingOperations.putIfAbsent(syncKey,syncFolderOperation);
156 sendBroadcastNewSyncFolder(account, remotePath);
157 }
158
159 /**
160 * Cancels sync operations.
161 * @param account Owncloud account where the remote file is stored.
162 * @param file File OCFile
163 */
164 public void cancel(Account account, OCFile file){
165 SynchronizeFolderOperation syncOperation = null;
166 String targetKey = buildRemoteName(account, file.getRemotePath());
167 ArrayList<String> keyItems = new ArrayList<String>();
168 synchronized (mPendingOperations) {
169 if (file.isFolder()) {
170 Log_OC.d(TAG, "Canceling pending sync operations");
171 Iterator<String> it = mPendingOperations.keySet().iterator();
172 boolean found = false;
173 while (it.hasNext()) {
174 String keySyncOperation = it.next();
175 found = keySyncOperation.startsWith(targetKey);
176 if (found) {
177 keyItems.add(keySyncOperation);
178 }
179 }
180
181 } else {
182 // this is not really expected...
183 Log_OC.d(TAG, "Canceling sync operation");
184 keyItems.add(buildRemoteName(account, file.getRemotePath()));
185 }
186 for (String item: keyItems) {
187 syncOperation = mPendingOperations.remove(item);
188 if (syncOperation != null) {
189 syncOperation.cancel();
190 }
191 }
192 }
193
194 //sendBroadcastFinishedSyncFolder(account, file.getRemotePath());
195 }
196
197 /**
198 * Builds a key from the account and file to download
199 *
200 * @param account Account where the file to download is stored
201 * @param path File path
202 */
203 private String buildRemoteName(Account account, String path) {
204 return account.name + path;
205 }
206
207
208 /**
209 * TODO review this method when "folder synchronization" replaces "folder download"; this is a fast and ugly
210 * patch.
211 */
212 private void sendBroadcastNewSyncFolder(Account account, String remotePath) {
213 Intent added = new Intent(FileDownloader.getDownloadAddedMessage());
214 added.putExtra(FileDownloader.ACCOUNT_NAME, account.name);
215 added.putExtra(FileDownloader.EXTRA_REMOTE_PATH, remotePath);
216 added.putExtra(FileDownloader.EXTRA_FILE_PATH, FileStorageUtils.getSavePath(account.name) + remotePath);
217 mService.sendStickyBroadcast(added);
218 }
219
220 /**
221 * TODO review this method when "folder synchronization" replaces "folder download"; this is a fast and ugly
222 * patch.
223 */
224 private void sendBroadcastFinishedSyncFolder(Account account, String remotePath, boolean success) {
225 Intent finished = new Intent(FileDownloader.getDownloadFinishMessage());
226 finished.putExtra(FileDownloader.ACCOUNT_NAME, account.name);
227 finished.putExtra(FileDownloader.EXTRA_REMOTE_PATH, remotePath);
228 finished.putExtra(FileDownloader.EXTRA_FILE_PATH, FileStorageUtils.getSavePath(account.name) + remotePath);
229 finished.putExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, success);
230 mService.sendStickyBroadcast(finished);
231 }
232
233
234 }