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