1 /* ownCloud Android client application
2 * Copyright (C) 2015 ownCloud Inc.
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.
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.
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/>.
18 package com
.owncloud
.android
.services
;
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
;
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
;
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
;
46 * SyncFolder worker. Performs the pending operations in the order they were requested.
48 * Created with the Looper of a new thread, started in
49 * {@link com.owncloud.android.services.OperationsService#onCreate()}.
51 class SyncFolderHandler
extends Handler
{
53 private static final String TAG
= SyncFolderHandler
.class.getSimpleName();
56 OperationsService mService
;
58 private ConcurrentMap
<String
,SynchronizeFolderOperation
> mPendingOperations
=
59 new ConcurrentHashMap
<String
,SynchronizeFolderOperation
>();
60 private OwnCloudClient mOwnCloudClient
= null
;
61 private FileDataStorageManager mStorageManager
;
62 private SynchronizeFolderOperation mCurrentSyncOperation
;
65 public SyncFolderHandler(Looper looper
, OperationsService service
) {
67 if (service
== null
) {
68 throw new IllegalArgumentException("Received invalid NULL in parameter 'service'");
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
);
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
);
98 * Performs the next operation in the queue
100 private void doOperation(Account account
, String remotePath
) {
102 String syncKey
= buildRemoteName(account
,remotePath
);
104 synchronized(mPendingOperations
) {
105 mCurrentSyncOperation
= mPendingOperations
.get(syncKey
);
108 if (mCurrentSyncOperation
!= null
) {
109 RemoteOperationResult result
= null
;
113 OwnCloudAccount ocAccount
= new OwnCloudAccount(account
, mService
);
114 mOwnCloudClient
= OwnCloudClientManagerFactory
.getDefaultSingleton().
115 getClientFor(ocAccount
, mService
);
116 mStorageManager
= new FileDataStorageManager(
118 mService
.getContentResolver()
121 result
= mCurrentSyncOperation
.execute(mOwnCloudClient
, mStorageManager
);
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
);
128 synchronized (mPendingOperations
) {
129 mPendingOperations
.remove(syncKey
);
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();
146 mService
.dispatchResultToOperationListeners(null
, mCurrentSyncOperation
, result
);
148 sendBroadcastFinishedSyncFolder(account
, remotePath
, result
.isSuccess());
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
);
160 * Cancels sync operations.
161 * @param account Owncloud account where the remote file is stored.
162 * @param file File OCFile
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
);
177 keyItems
.add(keySyncOperation
);
182 // this is not really expected...
183 Log_OC
.d(TAG
, "Canceling sync operation");
184 keyItems
.add(buildRemoteName(account
, file
.getRemotePath()));
186 for (String item
: keyItems
) {
187 syncOperation
= mPendingOperations
.remove(item
);
188 if (syncOperation
!= null
) {
189 syncOperation
.cancel();
194 //sendBroadcastFinishedSyncFolder(account, file.getRemotePath());
198 * Builds a key from the account and file to download
200 * @param account Account where the file to download is stored
201 * @param path File path
203 private String
buildRemoteName(Account account
, String path
) {
204 return account
.name
+ path
;
209 * TODO review this method when "folder synchronization" replaces "folder download"; this is a fast and ugly
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
);
221 * TODO review this method when "folder synchronization" replaces "folder download"; this is a fast and ugly
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
);