Merge branch 'develop' into imageGrid
[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.files.services.IndexedForest;
32 import com.owncloud.android.lib.common.OwnCloudAccount;
33 import com.owncloud.android.lib.common.OwnCloudClient;
34 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
35 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
36 import com.owncloud.android.lib.common.utils.Log_OC;
37 import com.owncloud.android.operations.SynchronizeFolderOperation;
38 import com.owncloud.android.utils.FileStorageUtils;
39
40 import java.io.IOException;
41
42 /**
43 * SyncFolder worker. Performs the pending operations in the order they were requested.
44 *
45 * Created with the Looper of a new thread, started in
46 * {@link com.owncloud.android.services.OperationsService#onCreate()}.
47 */
48 class SyncFolderHandler extends Handler {
49
50 private static final String TAG = SyncFolderHandler.class.getSimpleName();
51
52
53 OperationsService mService;
54
55 private IndexedForest<SynchronizeFolderOperation> mPendingOperations =
56 new IndexedForest<SynchronizeFolderOperation>();
57
58 private OwnCloudClient mOwnCloudClient = null;
59 private Account mCurrentAccount = null;
60 private FileDataStorageManager mStorageManager;
61 private SynchronizeFolderOperation mCurrentSyncOperation;
62
63
64 public SyncFolderHandler(Looper looper, OperationsService service) {
65 super(looper);
66 if (service == null) {
67 throw new IllegalArgumentException("Received invalid NULL in parameter 'service'");
68 }
69 mService = service;
70 }
71
72
73 /**
74 * Returns True when the folder located in 'remotePath' in the ownCloud account 'account', or any of its
75 * descendants, is being synchronized (or waiting for it).
76 *
77 * @param account ownCloud account where the remote folder is stored.
78 * @param remotePath The path to a folder that could be in the queue of synchronizations.
79 */
80 public boolean isSynchronizing(Account account, String remotePath) {
81 if (account == null || remotePath == null) return false;
82 return (mPendingOperations.contains(account, remotePath));
83 }
84
85
86 @Override
87 public void handleMessage(Message msg) {
88 Pair<Account, String> itemSyncKey = (Pair<Account, String>) msg.obj;
89 /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
90 "Handling sync folder " + itemSyncKey.second);*/
91 doOperation(itemSyncKey.first, itemSyncKey.second);
92 mService.stopSelf(msg.arg1);
93 }
94
95
96 /**
97 * Performs the next operation in the queue
98 */
99 private void doOperation(Account account, String remotePath) {
100
101 /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
102 "Getting sync folder " + remotePath);*/
103 mCurrentSyncOperation = mPendingOperations.get(account, remotePath);
104
105 if (mCurrentSyncOperation != null) {
106 RemoteOperationResult result = null;
107
108 try {
109
110 if (mCurrentAccount == null || !mCurrentAccount.equals(account)) {
111 mCurrentAccount = account;
112 mStorageManager = new FileDataStorageManager(
113 account,
114 mService.getContentResolver()
115 );
116 } // else, reuse storage manager from previous operation
117
118 // always get client from client manager, to get fresh credentials in case of update
119 OwnCloudAccount ocAccount = new OwnCloudAccount(account, mService);
120 mOwnCloudClient = OwnCloudClientManagerFactory.getDefaultSingleton().
121 getClientFor(ocAccount, mService);
122
123 /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
124 "Executing sync folder " + remotePath);*/
125 result = mCurrentSyncOperation.execute(mOwnCloudClient, mStorageManager);
126
127 } catch (AccountsException e) {
128 Log_OC.e(TAG, "Error while trying to get authorization", e);
129 } catch (IOException e) {
130 Log_OC.e(TAG, "Error while trying to get authorization", e);
131 } finally {
132 /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
133 "Removing payload " + remotePath);*/
134
135 mPendingOperations.removePayload(account, remotePath);
136
137 mService.dispatchResultToOperationListeners(null, mCurrentSyncOperation, result);
138
139 sendBroadcastFinishedSyncFolder(account, remotePath, result.isSuccess());
140 }
141 }
142 }
143
144 public void add(Account account, String remotePath, SynchronizeFolderOperation syncFolderOperation){
145 mPendingOperations.putIfAbsent(account, remotePath, syncFolderOperation);
146 sendBroadcastNewSyncFolder(account, remotePath); // TODO upgrade!
147 }
148
149
150 /**
151 * Cancels a pending or current sync' operation.
152 *
153 * @param account ownCloud account where the remote file is stored.
154 * @param file A file in the queue of pending synchronizations
155 */
156 public void cancel(Account account, OCFile file){
157 if (account == null || file == null) {
158 Log_OC.e(TAG, "Cannot cancel with NULL parameters");
159 return;
160 }
161 /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
162 "Removing sync folder " + file.getRemotePath());*/
163 Pair<SynchronizeFolderOperation, String> removeResult =
164 mPendingOperations.remove(account, file.getRemotePath());
165 SynchronizeFolderOperation synchronization = removeResult.first;
166 if (synchronization != null) {
167 /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
168 "Canceling returned sync of " + file.getRemotePath());*/
169 synchronization.cancel();
170 } else {
171 // TODO synchronize?
172 if (mCurrentSyncOperation != null && mCurrentAccount != null &&
173 mCurrentSyncOperation.getRemotePath().startsWith(file.getRemotePath()) &&
174 account.name.equals(mCurrentAccount.name)) {
175 /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
176 "Canceling current sync as descendant: " + mCurrentSyncOperation.getRemotePath());*/
177 mCurrentSyncOperation.cancel();
178 } else {
179 /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
180 "Nothing else in cancelation of " + file.getRemotePath());*/
181 }
182 }
183
184 //sendBroadcastFinishedSyncFolder(account, file.getRemotePath());
185 }
186
187 /**
188 * TODO review this method when "folder synchronization" replaces "folder download"; this is a fast and ugly
189 * patch.
190 */
191 private void sendBroadcastNewSyncFolder(Account account, String remotePath) {
192 Intent added = new Intent(FileDownloader.getDownloadAddedMessage());
193 added.putExtra(FileDownloader.ACCOUNT_NAME, account.name);
194 added.putExtra(FileDownloader.EXTRA_REMOTE_PATH, remotePath);
195 added.putExtra(FileDownloader.EXTRA_FILE_PATH, FileStorageUtils.getSavePath(account.name) + remotePath);
196 mService.sendStickyBroadcast(added);
197 }
198
199 /**
200 * TODO review this method when "folder synchronization" replaces "folder download"; this is a fast and ugly
201 * patch.
202 */
203 private void sendBroadcastFinishedSyncFolder(Account account, String remotePath, boolean success) {
204 Intent finished = new Intent(FileDownloader.getDownloadFinishMessage());
205 finished.putExtra(FileDownloader.ACCOUNT_NAME, account.name);
206 finished.putExtra(FileDownloader.EXTRA_REMOTE_PATH, remotePath);
207 finished.putExtra(FileDownloader.EXTRA_FILE_PATH, FileStorageUtils.getSavePath(account.name) + remotePath);
208 finished.putExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, success);
209 mService.sendStickyBroadcast(finished);
210 }
211
212
213 }