Merge branch 'develop' into cancel_transfer_for_deleted_users
[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 doOperation(itemSyncKey.first, itemSyncKey.second);
90 Log_OC.d(TAG, "Stopping after command with id " + msg.arg1);
91 mService.stopSelf(msg.arg1);
92 }
93
94
95 /**
96 * Performs the next operation in the queue
97 */
98 private void doOperation(Account account, String remotePath) {
99
100 mCurrentSyncOperation = mPendingOperations.get(account, remotePath);
101
102 if (mCurrentSyncOperation != null) {
103 RemoteOperationResult result = null;
104
105 try {
106
107 if (mCurrentAccount == null || !mCurrentAccount.equals(account)) {
108 mCurrentAccount = account;
109 mStorageManager = new FileDataStorageManager(
110 account,
111 mService.getContentResolver()
112 );
113 } // else, reuse storage manager from previous operation
114
115 // always get client from client manager, to get fresh credentials in case of update
116 OwnCloudAccount ocAccount = new OwnCloudAccount(account, mService);
117 mOwnCloudClient = OwnCloudClientManagerFactory.getDefaultSingleton().
118 getClientFor(ocAccount, mService);
119
120 result = mCurrentSyncOperation.execute(mOwnCloudClient, mStorageManager);
121
122 } catch (AccountsException e) {
123 Log_OC.e(TAG, "Error while trying to get authorization", e);
124 } catch (IOException e) {
125 Log_OC.e(TAG, "Error while trying to get authorization", e);
126 } finally {
127 mPendingOperations.removePayload(account, remotePath);
128
129 mService.dispatchResultToOperationListeners(mCurrentSyncOperation, result);
130
131 sendBroadcastFinishedSyncFolder(account, remotePath, result.isSuccess());
132 }
133 }
134 }
135
136 public void add(Account account, String remotePath, SynchronizeFolderOperation syncFolderOperation){
137 mPendingOperations.putIfAbsent(account, remotePath, syncFolderOperation);
138 sendBroadcastNewSyncFolder(account, remotePath); // TODO upgrade!
139 }
140
141
142 /**
143 * Cancels a pending or current sync' operation.
144 *
145 * @param account ownCloud account where the remote file is stored.
146 * @param file A file in the queue of pending synchronizations
147 */
148 public void cancel(Account account, OCFile file){
149 if (account == null || file == null) {
150 Log_OC.e(TAG, "Cannot cancel with NULL parameters");
151 return;
152 }
153 Pair<SynchronizeFolderOperation, String> removeResult =
154 mPendingOperations.remove(account, file.getRemotePath());
155 SynchronizeFolderOperation synchronization = removeResult.first;
156 if (synchronization != null) {
157 synchronization.cancel();
158 } else {
159 // TODO synchronize?
160 if (mCurrentSyncOperation != null && mCurrentAccount != null &&
161 mCurrentSyncOperation.getRemotePath().startsWith(file.getRemotePath()) &&
162 account.name.equals(mCurrentAccount.name)) {
163 mCurrentSyncOperation.cancel();
164 }
165 }
166
167 //sendBroadcastFinishedSyncFolder(account, file.getRemotePath());
168 }
169
170 /**
171 * TODO review this method when "folder synchronization" replaces "folder download"; this is a fast and ugly
172 * patch.
173 */
174 private void sendBroadcastNewSyncFolder(Account account, String remotePath) {
175 Intent added = new Intent(FileDownloader.getDownloadAddedMessage());
176 added.putExtra(FileDownloader.ACCOUNT_NAME, account.name);
177 added.putExtra(FileDownloader.EXTRA_REMOTE_PATH, remotePath);
178 added.putExtra(FileDownloader.EXTRA_FILE_PATH, FileStorageUtils.getSavePath(account.name) + remotePath);
179 mService.sendStickyBroadcast(added);
180 }
181
182 /**
183 * TODO review this method when "folder synchronization" replaces "folder download"; this is a fast and ugly
184 * patch.
185 */
186 private void sendBroadcastFinishedSyncFolder(Account account, String remotePath, boolean success) {
187 Intent finished = new Intent(FileDownloader.getDownloadFinishMessage());
188 finished.putExtra(FileDownloader.ACCOUNT_NAME, account.name);
189 finished.putExtra(FileDownloader.EXTRA_REMOTE_PATH, remotePath);
190 finished.putExtra(FileDownloader.EXTRA_FILE_PATH, FileStorageUtils.getSavePath(account.name) + remotePath);
191 finished.putExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, success);
192 mService.sendStickyBroadcast(finished);
193 }
194
195
196 }