1 /* ownCloud Android client application
2 * Copyright (C) 2012 Bartek Przybylski
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 package com
.owncloud
.android
.files
.services
;
22 import java
.util
.ArrayList
;
23 import java
.util
.List
;
25 import com
.owncloud
.android
.AccountUtils
;
26 import com
.owncloud
.android
.datamodel
.FileDataStorageManager
;
27 import com
.owncloud
.android
.db
.ProviderMeta
.ProviderTableMeta
;
28 import com
.owncloud
.android
.files
.OwnCloudFileObserver
;
29 import com
.owncloud
.android
.files
.OwnCloudFileObserver
.FileObserverStatusListener
;
30 import com
.owncloud
.android
.ui
.activity
.ConflictsResolveActivity
;
32 import android
.accounts
.Account
;
33 import android
.accounts
.AccountManager
;
34 import android
.app
.Service
;
35 import android
.content
.BroadcastReceiver
;
36 import android
.content
.Context
;
37 import android
.content
.Intent
;
38 import android
.content
.IntentFilter
;
39 import android
.database
.Cursor
;
40 import android
.os
.Binder
;
41 import android
.os
.IBinder
;
42 import android
.util
.Log
;
44 public class FileObserverService
extends Service
implements FileObserverStatusListener
{
46 public final static String KEY_FILE_CMD
= "KEY_FILE_CMD";
47 public final static String KEY_CMD_ARG
= "KEY_CMD_ARG";
49 public final static int CMD_INIT_OBSERVED_LIST
= 1;
50 public final static int CMD_ADD_OBSERVED_FILE
= 2;
51 public final static int CMD_DEL_OBSERVED_FILE
= 3;
52 public final static int CMD_ADD_DOWNLOADING_FILE
= 4;
54 private static String TAG
= FileObserverService
.class.getSimpleName();
55 private static List
<OwnCloudFileObserver
> mObservers
;
56 private static List
<DownloadCompletedReceiver
> mDownloadReceivers
;
57 private static Object mReceiverListLock
= new Object();
58 private IBinder mBinder
= new LocalBinder();
60 public class LocalBinder
extends Binder
{
61 FileObserverService
getService() {
62 return FileObserverService
.this;
67 public IBinder
onBind(Intent intent
) {
72 public int onStartCommand(Intent intent
, int flags
, int startId
) {
73 // this occurs when system tries to restart
74 // service, so we need to reinitialize observers
76 initializeObservedList();
77 return Service
.START_STICKY
;
80 if (!intent
.hasExtra(KEY_FILE_CMD
)) {
81 Log
.e(TAG
, "No KEY_FILE_CMD argument given");
82 return Service
.START_STICKY
;
85 switch (intent
.getIntExtra(KEY_FILE_CMD
, -1)) {
86 case CMD_INIT_OBSERVED_LIST
:
87 initializeObservedList();
89 case CMD_ADD_OBSERVED_FILE
:
90 addObservedFile(intent
.getStringExtra(KEY_CMD_ARG
));
92 case CMD_DEL_OBSERVED_FILE
:
93 removeObservedFile(intent
.getStringExtra(KEY_CMD_ARG
));
95 case CMD_ADD_DOWNLOADING_FILE
:
96 addDownloadingFile(intent
.getStringExtra(KEY_CMD_ARG
));
99 Log
.wtf(TAG
, "Incorrect key given");
102 return Service
.START_STICKY
;
105 private void initializeObservedList() {
106 if (mObservers
!= null
) return; // nothing to do here
107 mObservers
= new ArrayList
<OwnCloudFileObserver
>();
108 mDownloadReceivers
= new ArrayList
<DownloadCompletedReceiver
>();
109 Cursor c
= getContentResolver().query(
110 ProviderTableMeta
.CONTENT_URI
,
112 ProviderTableMeta
.FILE_KEEP_IN_SYNC
+ " = ?",
113 new String
[] {String
.valueOf(1)},
115 if (!c
.moveToFirst()) return;
116 AccountManager acm
= AccountManager
.get(this);
117 Account
[] accounts
= acm
.getAccounts();
119 Account account
= null
;
120 for (Account a
: accounts
)
121 if (a
.name
.equals(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_ACCOUNT_OWNER
)))) {
126 if (account
== null
) continue;
127 FileDataStorageManager storage
=
128 new FileDataStorageManager(account
, getContentResolver());
129 if (!storage
.fileExists(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_PATH
))))
132 String path
= c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_STORAGE_PATH
));
133 OwnCloudFileObserver observer
=
134 new OwnCloudFileObserver(path
, OwnCloudFileObserver
.CHANGES_ONLY
);
135 observer
.setContext(getApplicationContext());
136 observer
.setAccount(account
);
137 observer
.setStorageManager(storage
);
138 observer
.setOCFile(storage
.getFileByPath(c
.getString(c
.getColumnIndex(ProviderTableMeta
.FILE_PATH
))));
139 observer
.addObserverStatusListener(this);
140 observer
.startWatching();
141 mObservers
.add(observer
);
142 Log
.d(TAG
, "Started watching file " + path
);
144 } while (c
.moveToNext());
148 private void addObservedFile(String path
) {
149 if (path
== null
) return;
150 if (mObservers
== null
) {
151 // this is very rare case when service was killed by system
152 // and observers list was deleted in that procedure
153 initializeObservedList();
155 boolean duplicate
= false
;
156 OwnCloudFileObserver observer
= null
;
157 for (int i
= 0; i
< mObservers
.size(); ++i
) {
158 observer
= mObservers
.get(i
);
159 if (observer
.getPath().equals(path
))
161 observer
.setContext(getApplicationContext());
163 if (duplicate
) return;
164 observer
= new OwnCloudFileObserver(path
, OwnCloudFileObserver
.CHANGES_ONLY
);
165 observer
.setContext(getApplicationContext());
166 Account account
= AccountUtils
.getCurrentOwnCloudAccount(getApplicationContext());
167 observer
.setAccount(account
);
168 FileDataStorageManager storage
=
169 new FileDataStorageManager(account
, getContentResolver());
170 observer
.setStorageManager(storage
);
171 observer
.setOCFile(storage
.getFileByLocalPath(path
));
172 observer
.addObserverStatusListener(this);
174 DownloadCompletedReceiver receiver
= new DownloadCompletedReceiver(path
, observer
);
175 registerReceiver(receiver
, new IntentFilter(FileDownloader
.DOWNLOAD_FINISH_MESSAGE
));
177 mObservers
.add(observer
);
178 Log
.d(TAG
, "Observer added for path " + path
);
181 private void removeObservedFile(String path
) {
182 if (path
== null
) return;
183 if (mObservers
== null
) {
184 initializeObservedList();
186 for (int i
= 0; i
< mObservers
.size(); ++i
) {
187 OwnCloudFileObserver observer
= mObservers
.get(i
);
188 if (observer
.getPath().equals(path
)) {
189 observer
.stopWatching();
190 mObservers
.remove(i
);
191 Log
.d(TAG
, "Stopped watching " + path
);
197 private void addDownloadingFile(String remotePath
) {
198 OwnCloudFileObserver observer
= null
;
199 for (OwnCloudFileObserver o
: mObservers
) {
200 if (o
.getRemotePath().equals(remotePath
)) {
205 if (observer
== null
) {
206 Log
.e(TAG
, "Couldn't find observer for remote file " + remotePath
);
209 observer
.stopWatching();
210 DownloadCompletedReceiver dcr
= new DownloadCompletedReceiver(observer
.getPath(), observer
);
211 registerReceiver(dcr
, new IntentFilter(FileDownloader
.DOWNLOAD_FINISH_MESSAGE
));
215 private static void addReceiverToList(DownloadCompletedReceiver r
) {
216 synchronized(mReceiverListLock
) {
217 mDownloadReceivers
.add(r
);
221 private static void removeReceiverFromList(DownloadCompletedReceiver r
) {
222 synchronized(mReceiverListLock
) {
223 mDownloadReceivers
.remove(r
);
228 public void OnObservedFileStatusUpdate(String localPath
, String remotePath
, Account account
, Status status
) {
232 // ISSUE 5: if the user is not running the app (this is a service!), this can be very intrusive; a notification should be preferred
233 Intent i
= new Intent(getApplicationContext(), ConflictsResolveActivity
.class);
234 i
.setFlags(i
.getFlags() | Intent
.FLAG_ACTIVITY_NEW_TASK
);
235 i
.putExtra("remotepath", remotePath
);
236 i
.putExtra("localpath", localPath
);
237 i
.putExtra("account", account
);
241 case SENDING_TO_UPLOADER
:
245 Log
.wtf(TAG
, "Unhandled status " + status
);
249 private class DownloadCompletedReceiver
extends BroadcastReceiver
{
251 OwnCloudFileObserver mObserver
;
253 public DownloadCompletedReceiver(String path
, OwnCloudFileObserver observer
) {
255 mObserver
= observer
;
256 addReceiverToList(this);
260 public void onReceive(Context context
, Intent intent
) {
261 if (mPath
.equals(intent
.getStringExtra(FileDownloader
.EXTRA_FILE_PATH
))) {
262 if ((new File(mPath
)).exists()) {
263 // the download could be successful, or not; in both cases, the file could be down, due to a former download or upload
264 context
.unregisterReceiver(this);
265 removeReceiverFromList(this);
266 mObserver
.startWatching();
267 Log
.d(TAG
, "Started watching " + mPath
);
269 } // else - keep waiting for a future retry of the download ;
270 // mObserver.startWatching() won't ever work if the file is not in the device when it's called
275 public boolean equals(Object o
) {
276 if (o
instanceof DownloadCompletedReceiver
)
277 return mPath
.equals(((DownloadCompletedReceiver
)o
).mPath
);
278 return super.equals(o
);